Home | History | Annotate | Download | only in io
      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
     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