1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.io;
18 
19 import com.android.layoutlib.bridge.impl.DelegateManager;
20 import com.android.layoutlib.bridge.libcore.io.BridgeBufferIterator;
21 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
22 
23 import android.system.ErrnoException;
24 
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.RandomAccessFile;
28 import java.nio.ByteOrder;
29 import java.nio.MappedByteBuffer;
30 import java.nio.channels.FileChannel.MapMode;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * Delegate used to provide alternate implementation of select methods of {@link MemoryMappedFile}.
36  */
37 public class MemoryMappedFile_Delegate {
38 
39     private static final DelegateManager<MemoryMappedFile_Delegate> sManager = new
40             DelegateManager<MemoryMappedFile_Delegate>(MemoryMappedFile_Delegate.class);
41 
42     private static final Map<MemoryMappedFile, Long> sMemoryMappedFileMap =
43             new HashMap<MemoryMappedFile, Long>();
44 
45     private final MappedByteBuffer mMappedByteBuffer;
46     private final long mSize;
47 
48     /** Path on the target device where the data file is available. */
49     private static final String TARGET_PATH = System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo";
50     /** Path on the host (inside the SDK) where the data files are available. */
51     private static File sRootPath;
52 
53     @LayoutlibDelegate
mmapRO(String path)54     static MemoryMappedFile mmapRO(String path) throws ErrnoException {
55         if (!path.startsWith(TARGET_PATH)) {
56             throw new ErrnoException("Custom timezone data files are not supported.", 1);
57         }
58         if (sRootPath == null) {
59             throw new ErrnoException("Bridge has not been initialized properly.", 1);
60         }
61         path = path.substring(TARGET_PATH.length());
62         try {
63             File f = new File(sRootPath, path);
64             if (!f.exists()) {
65                 throw new ErrnoException("File not found: " + f.getPath(), 1);
66             }
67             RandomAccessFile file = new RandomAccessFile(f, "r");
68             try {
69                 long size = file.length();
70                 MemoryMappedFile_Delegate newDelegate = new MemoryMappedFile_Delegate(file);
71                 long filePointer = file.getFilePointer();
72                 MemoryMappedFile mmFile = new MemoryMappedFile(filePointer, size);
73                 long delegateIndex = sManager.addNewDelegate(newDelegate);
74                 sMemoryMappedFileMap.put(mmFile, delegateIndex);
75                 return mmFile;
76             } finally {
77                 file.close();
78             }
79         } catch (IOException e) {
80             throw new ErrnoException("mmapRO", 1, e);
81         }
82     }
83 
84     @LayoutlibDelegate
close(MemoryMappedFile thisFile)85     static void close(MemoryMappedFile thisFile) throws ErrnoException {
86         Long index = sMemoryMappedFileMap.get(thisFile);
87         if (index != null) {
88             sMemoryMappedFileMap.remove(thisFile);
89             sManager.removeJavaReferenceFor(index);
90         }
91     }
92 
93     @LayoutlibDelegate
bigEndianIterator(MemoryMappedFile file)94     static BufferIterator bigEndianIterator(MemoryMappedFile file) {
95         MemoryMappedFile_Delegate delegate = getDelegate(file);
96         return new BridgeBufferIterator(delegate.mSize, delegate.mMappedByteBuffer.duplicate());
97     }
98 
99     // TODO: implement littleEndianIterator()
100 
MemoryMappedFile_Delegate(RandomAccessFile file)101     public MemoryMappedFile_Delegate(RandomAccessFile file) throws IOException {
102         mSize = file.length();
103         // It's weird that map() takes size as long, but returns MappedByteBuffer which uses an int
104         // to store the marker to the position.
105         mMappedByteBuffer = file.getChannel().map(MapMode.READ_ONLY, 0, mSize);
106         assert mMappedByteBuffer.order() == ByteOrder.BIG_ENDIAN;
107     }
108 
setDataDir(File path)109     public static void setDataDir(File path) {
110         sRootPath = path;
111     }
112 
getDelegate(MemoryMappedFile file)113     private static MemoryMappedFile_Delegate getDelegate(MemoryMappedFile file) {
114         Long index = sMemoryMappedFileMap.get(file);
115         return index == null ? null : sManager.getDelegate(index);
116     }
117 
118 }
119