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
17package libcore.io;
18
19import com.android.layoutlib.bridge.impl.DelegateManager;
20import com.android.layoutlib.bridge.libcore.io.BridgeBufferIterator;
21import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
22
23import android.system.ErrnoException;
24
25import java.io.File;
26import java.io.IOException;
27import java.io.RandomAccessFile;
28import java.nio.ByteOrder;
29import java.nio.MappedByteBuffer;
30import java.nio.channels.FileChannel.MapMode;
31import java.util.HashMap;
32import java.util.Map;
33
34/**
35 * Delegate used to provide alternate implementation of select methods of {@link MemoryMappedFile}.
36 */
37public 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
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
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
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
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
109    public static void setDataDir(File path) {
110        sRootPath = path;
111    }
112
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