Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.LOLLIPOP;
      4 import static org.robolectric.RuntimeEnvironment.getApiLevel;
      5 
      6 import android.system.ErrnoException;
      7 import java.io.IOException;
      8 import java.io.InputStream;
      9 import java.nio.ByteBuffer;
     10 import java.nio.ByteOrder;
     11 import libcore.io.BufferIterator;
     12 import libcore.io.MemoryMappedFile;
     13 import libcore.io.Streams;
     14 import org.robolectric.annotation.Implementation;
     15 import org.robolectric.annotation.Implements;
     16 import org.robolectric.shadow.api.Shadow;
     17 
     18 /**
     19  * This is used by Android to load and inferFromValue time zone information. Robolectric emulates
     20  * this functionality by proxying to a time zone database file packaged into the android-all
     21  * jar.
     22  */
     23 @Implements(value = MemoryMappedFile.class, isInAndroidSdk = false)
     24 public class ShadowMemoryMappedFile {
     25     private byte[] bytes;
     26     private static final String TZ_DATA_1 = "/misc/zoneinfo/tzdata";
     27     private static final String TZ_DATA_2 = "/usr/share/zoneinfo/tzdata";
     28     private static final String TZ_DATA_3 = "/misc/zoneinfo/current/tzdata";
     29 
     30     @Implementation
     31     public static MemoryMappedFile mmapRO(String path) throws Throwable {
     32         if (path.endsWith(TZ_DATA_1) || path.endsWith(TZ_DATA_2) || path.endsWith(TZ_DATA_3)) {
     33             InputStream is = MemoryMappedFile.class.getResourceAsStream(TZ_DATA_2);
     34             if (is == null) {
     35                 throw (Throwable) exceptionClass().getConstructor(String.class, int.class)
     36                     .newInstance("open", -1);
     37             }
     38             try {
     39                 MemoryMappedFile memoryMappedFile = new MemoryMappedFile(0L, 0L);
     40                 ShadowMemoryMappedFile shadowMemoryMappedFile = Shadow.extract(memoryMappedFile);
     41                 shadowMemoryMappedFile.bytes = Streams.readFully(is);
     42                 return memoryMappedFile;
     43             } catch (IOException e) {
     44                 throw (Throwable) exceptionClass().getConstructor(String.class, int.class, Throwable.class)
     45                     .newInstance("mmap", -1, e);
     46             }
     47         } else {
     48             throw new IllegalArgumentException("Unknown file for mmap: '" + path);
     49         }
     50     }
     51 
     52     private static Class exceptionClass() {
     53         if (getApiLevel() >= LOLLIPOP) {
     54             return ErrnoException.class;
     55         } else {
     56             try {
     57                 return MemoryMappedFile.class.getClassLoader().loadClass("libcore.io.ErrnoException");
     58             } catch (ClassNotFoundException e) {
     59                 throw new RuntimeException(e);
     60             }
     61         }
     62     }
     63 
     64     @Implementation
     65     public synchronized void close() throws Exception {
     66         bytes = null;
     67     }
     68 
     69     @Implementation
     70     public BufferIterator bigEndianIterator() {
     71         return getHeapBufferIterator(ByteOrder.BIG_ENDIAN);
     72     }
     73 
     74     @Implementation
     75     public BufferIterator littleEndianIterator() {
     76         return getHeapBufferIterator(ByteOrder.LITTLE_ENDIAN);
     77     }
     78 
     79     private BufferIterator getHeapBufferIterator(ByteOrder endianness) {
     80         return new RoboBufferIterator(bytes, endianness);
     81     }
     82 
     83     @Implementation
     84     public int size() {
     85         return bytes.length;
     86     }
     87 
     88     private static class RoboBufferIterator extends BufferIterator {
     89         private final ByteBuffer buffer;
     90 
     91         public RoboBufferIterator(byte[] buffer, ByteOrder order) {
     92             this.buffer = ByteBuffer.wrap(buffer);
     93         }
     94 
     95         @Override public void seek(int offset) {
     96             buffer.position(offset);
     97         }
     98 
     99         @Override public void skip(int byteCount) {
    100             buffer.position(buffer.position() + byteCount);
    101         }
    102 
    103         @Override
    104         public int pos() {
    105             return 0;
    106         }
    107 
    108         @Override public void readByteArray(byte[] dst, int dstOffset, int byteCount) {
    109             System.arraycopy(buffer.array(), buffer.position(), dst, dstOffset, byteCount);
    110             skip(byteCount);
    111         }
    112 
    113         @Override public byte readByte() {
    114             return buffer.get();
    115         }
    116 
    117         @Override public int readInt() {
    118             return buffer.getInt();
    119         }
    120 
    121         @Override public void readIntArray(int[] dst, int dstOffset, int intCount) {
    122             for (int i = 0; i < intCount; i++) {
    123                 dst[dstOffset + i] = buffer.getInt();
    124             }
    125         }
    126 
    127         @Override public short readShort() {
    128             return buffer.getShort();
    129         }
    130     }
    131 }
    132