Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
      4 import static android.os.Build.VERSION_CODES.LOLLIPOP;
      5 import static org.robolectric.RuntimeEnvironment.castNativePtr;
      6 
      7 import android.os.Parcel;
      8 import android.text.TextUtils;
      9 import android.util.Pair;
     10 import java.io.ByteArrayInputStream;
     11 import java.io.ByteArrayOutputStream;
     12 import java.io.IOException;
     13 import java.io.ObjectInputStream;
     14 import java.io.ObjectOutputStream;
     15 import java.util.ArrayList;
     16 import java.util.LinkedHashMap;
     17 import java.util.List;
     18 import java.util.Map;
     19 import java.util.Objects;
     20 import org.robolectric.annotation.HiddenApi;
     21 import org.robolectric.annotation.Implementation;
     22 import org.robolectric.annotation.Implements;
     23 import org.robolectric.annotation.RealObject;
     24 import org.robolectric.util.ReflectionHelpers;
     25 
     26 @Implements(Parcel.class)
     27 @SuppressWarnings("unchecked")
     28 public class ShadowParcel {
     29   @RealObject private Parcel realObject;
     30   private static final Map<Long, ByteBuffer> NATIVE_PTR_TO_PARCEL = new LinkedHashMap<>();
     31   private static long nextNativePtr = 1; // this needs to start above 0, which is a magic number to Parcel
     32 
     33   @Implementation
     34   public void writeByteArray(byte[] b, int offset, int len) {
     35     if (b == null) {
     36       realObject.writeInt(-1);
     37       return;
     38     }
     39     Number nativePtr = ReflectionHelpers.getField(realObject, "mNativePtr");
     40     nativeWriteByteArray(nativePtr.longValue(), b, offset, len);
     41   }
     42 
     43   @Implementation
     44   public void writeBlob(byte[] b) {
     45     writeByteArray(b, 0, b.length);
     46   }
     47 
     48   @HiddenApi
     49   @Implementation(maxSdk = KITKAT_WATCH)
     50   public static int nativeDataSize(int nativePtr) {
     51     return nativeDataSize((long) nativePtr);
     52   }
     53 
     54   @Implementation(minSdk = LOLLIPOP)
     55   public static int nativeDataSize(long nativePtr) {
     56     return NATIVE_PTR_TO_PARCEL.get(nativePtr).dataSize();
     57   }
     58 
     59   @HiddenApi
     60   @Implementation(maxSdk = KITKAT_WATCH)
     61   public static int nativeDataAvail(int nativePtr) {
     62     return nativeDataAvail((long) nativePtr);
     63   }
     64 
     65   @Implementation(minSdk = LOLLIPOP)
     66   public static int nativeDataAvail(long nativePtr) {
     67     return NATIVE_PTR_TO_PARCEL.get(nativePtr).dataAvailable();
     68   }
     69 
     70   @HiddenApi
     71   @Implementation(maxSdk = KITKAT_WATCH)
     72   public static int nativeDataPosition(int nativePtr) {
     73     return nativeDataPosition((long) nativePtr);
     74   }
     75 
     76   @Implementation(minSdk = LOLLIPOP)
     77   public static int nativeDataPosition(long nativePtr) {
     78     return NATIVE_PTR_TO_PARCEL.get(nativePtr).dataPosition();
     79   }
     80 
     81   @HiddenApi
     82   @Implementation(maxSdk = KITKAT_WATCH)
     83   public static int nativeDataCapacity(int nativePtr) {
     84     return nativeDataCapacity((long) nativePtr);
     85   }
     86 
     87   @Implementation(minSdk = LOLLIPOP)
     88   public static int nativeDataCapacity(long nativePtr) {
     89     return NATIVE_PTR_TO_PARCEL.get(nativePtr).dataCapacity();
     90   }
     91 
     92   @HiddenApi
     93   @Implementation(maxSdk = KITKAT_WATCH)
     94   public static void nativeSetDataSize(int nativePtr, int size) {
     95     nativeSetDataSize((long) nativePtr, size);
     96   }
     97 
     98   @Implementation(minSdk = LOLLIPOP)
     99   public static void nativeSetDataSize(long nativePtr, int size) {
    100     NATIVE_PTR_TO_PARCEL.get(nativePtr).setDataSize(size);
    101   }
    102 
    103   @HiddenApi
    104   @Implementation(maxSdk = KITKAT_WATCH)
    105   public static void nativeSetDataPosition(int nativePtr, int pos) {
    106     nativeSetDataPosition((long) nativePtr, pos);
    107   }
    108 
    109   @Implementation(minSdk = LOLLIPOP)
    110   public static void nativeSetDataPosition(long nativePtr, int pos) {
    111     NATIVE_PTR_TO_PARCEL.get(nativePtr).setDataPosition(pos);
    112   }
    113 
    114   @HiddenApi
    115   @Implementation(maxSdk = KITKAT_WATCH)
    116   public static void nativeSetDataCapacity(int nativePtr, int size) {
    117     nativeSetDataCapacity((long) nativePtr, size);
    118   }
    119 
    120   @Implementation(minSdk = LOLLIPOP)
    121   public static void nativeSetDataCapacity(long nativePtr, int size) {
    122     NATIVE_PTR_TO_PARCEL.get(nativePtr).setDataCapacity(size);
    123   }
    124 
    125   @HiddenApi
    126   @Implementation(maxSdk = KITKAT_WATCH)
    127   public static void nativeWriteByteArray(int nativePtr, byte[] b, int offset, int len) {
    128     nativeWriteByteArray((long) nativePtr, b, offset, len);
    129   }
    130 
    131   @Implementation(minSdk = LOLLIPOP)
    132   public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
    133     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeByteArray(b, offset, len);
    134   }
    135 
    136   @HiddenApi
    137   @Implementation(maxSdk = KITKAT_WATCH)
    138   public static void nativeWriteInt(int nativePtr, int val) {
    139     nativeWriteInt((long) nativePtr, val);
    140   }
    141 
    142   @Implementation(minSdk = LOLLIPOP)
    143   public static void nativeWriteInt(long nativePtr, int val) {
    144     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeInt(val);
    145   }
    146 
    147   @HiddenApi
    148   @Implementation(maxSdk = KITKAT_WATCH)
    149   public static void nativeWriteLong(int nativePtr, long val) {
    150     nativeWriteLong((long) nativePtr, val);
    151   }
    152 
    153   @Implementation(minSdk = LOLLIPOP)
    154   public static void nativeWriteLong(long nativePtr, long val) {
    155     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeLong(val);
    156   }
    157 
    158   @HiddenApi
    159   @Implementation(maxSdk = KITKAT_WATCH)
    160   public static void nativeWriteFloat(int nativePtr, float val) {
    161     nativeWriteFloat((long) nativePtr, val);
    162   }
    163 
    164   @Implementation(minSdk = LOLLIPOP)
    165   public static void nativeWriteFloat(long nativePtr, float val) {
    166     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeFloat(val);
    167   }
    168 
    169   @HiddenApi
    170   @Implementation(maxSdk = KITKAT_WATCH)
    171   public static void nativeWriteDouble(int nativePtr, double val) {
    172     nativeWriteDouble((long) nativePtr, val);
    173   }
    174 
    175   @Implementation(minSdk = LOLLIPOP)
    176   public static void nativeWriteDouble(long nativePtr, double val) {
    177     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeDouble(val);
    178   }
    179 
    180   @HiddenApi
    181   @Implementation(maxSdk = KITKAT_WATCH)
    182   public static void nativeWriteString(int nativePtr, String val) {
    183     nativeWriteString((long) nativePtr, val);
    184   }
    185 
    186   @Implementation(minSdk = LOLLIPOP)
    187   public static void nativeWriteString(long nativePtr, String val) {
    188     NATIVE_PTR_TO_PARCEL.get(nativePtr).writeString(val);
    189   }
    190 
    191   @HiddenApi
    192   @Implementation(maxSdk = KITKAT_WATCH)
    193   public static byte[] nativeCreateByteArray(int nativePtr) {
    194     return nativeCreateByteArray((long) nativePtr);
    195   }
    196 
    197   @Implementation(minSdk = LOLLIPOP)
    198   public static byte[] nativeCreateByteArray(long nativePtr) {
    199     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readByteArray();
    200   }
    201 
    202   @Implementation(minSdk = LOLLIPOP)
    203   public static byte[] nativeReadBlob(long nativePtr) {
    204     return nativeCreateByteArray(nativePtr);
    205   }
    206 
    207   @HiddenApi
    208   @Implementation(maxSdk = KITKAT_WATCH)
    209   public static int nativeReadInt(int nativePtr) {
    210     return nativeReadInt((long) nativePtr);
    211   }
    212 
    213   @Implementation(minSdk = LOLLIPOP)
    214   public static int nativeReadInt(long nativePtr) {
    215     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readInt();
    216   }
    217 
    218   @HiddenApi
    219   @Implementation(maxSdk = KITKAT_WATCH)
    220   public static long nativeReadLong(int nativePtr) {
    221     return nativeReadLong((long) nativePtr);
    222   }
    223 
    224   @Implementation(minSdk = LOLLIPOP)
    225   public static long nativeReadLong(long nativePtr) {
    226     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readLong();
    227   }
    228 
    229   @HiddenApi
    230   @Implementation(maxSdk = KITKAT_WATCH)
    231   public static float nativeReadFloat(int nativePtr) {
    232     return nativeReadFloat((long) nativePtr);
    233   }
    234 
    235   @Implementation(minSdk = LOLLIPOP)
    236   public static float nativeReadFloat(long nativePtr) {
    237     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readFloat();
    238   }
    239 
    240   @HiddenApi
    241   @Implementation(maxSdk = KITKAT_WATCH)
    242   public static double nativeReadDouble(int nativePtr) {
    243     return nativeReadDouble((long) nativePtr);
    244   }
    245 
    246   @Implementation(minSdk = LOLLIPOP)
    247   public static double nativeReadDouble(long nativePtr) {
    248     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readDouble();
    249   }
    250 
    251   @HiddenApi
    252   @Implementation(maxSdk = KITKAT_WATCH)
    253   public static String nativeReadString(int nativePtr) {
    254     return nativeReadString((long) nativePtr);
    255   }
    256 
    257   @Implementation(minSdk = LOLLIPOP)
    258   public static String nativeReadString(long nativePtr) {
    259     return NATIVE_PTR_TO_PARCEL.get(nativePtr).readString();
    260   }
    261 
    262   @Implementation @HiddenApi
    263   synchronized public static Number nativeCreate() {
    264     long nativePtr = nextNativePtr++;
    265     NATIVE_PTR_TO_PARCEL.put(nativePtr, new ByteBuffer());
    266     return castNativePtr(nativePtr);
    267   }
    268 
    269   @HiddenApi
    270   @Implementation(maxSdk = KITKAT_WATCH)
    271   public static void nativeFreeBuffer(int nativePtr) {
    272     nativeFreeBuffer((long) nativePtr);
    273   }
    274 
    275   @Implementation(minSdk = LOLLIPOP)
    276   public static void nativeFreeBuffer(long nativePtr) {
    277     NATIVE_PTR_TO_PARCEL.get(nativePtr).clear();
    278   }
    279 
    280   @HiddenApi
    281   @Implementation(maxSdk = KITKAT_WATCH)
    282   public static void nativeDestroy(int nativePtr) {
    283     nativeDestroy((long) nativePtr);
    284   }
    285 
    286   @Implementation(minSdk = LOLLIPOP)
    287   public static void nativeDestroy(long nativePtr) {
    288     NATIVE_PTR_TO_PARCEL.remove(nativePtr);
    289   }
    290 
    291   @HiddenApi
    292   @Implementation(maxSdk = KITKAT_WATCH)
    293   public static byte[] nativeMarshall(int nativePtr) {
    294     return nativeMarshall((long) nativePtr);
    295   }
    296 
    297   @Implementation(minSdk = LOLLIPOP)
    298   public static byte[] nativeMarshall(long nativePtr) {
    299     return NATIVE_PTR_TO_PARCEL.get(nativePtr).toByteArray();
    300   }
    301 
    302   @HiddenApi
    303   @Implementation(maxSdk = KITKAT_WATCH)
    304   public static void nativeUnmarshall(int nativePtr, byte[] data, int offset, int length) {
    305     nativeUnmarshall((long) nativePtr, data, offset, length);
    306   }
    307 
    308   @Implementation(minSdk = LOLLIPOP)
    309   public static void nativeUnmarshall(long nativePtr, byte[] data, int offset, int length) {
    310     NATIVE_PTR_TO_PARCEL.put(nativePtr, ByteBuffer.fromByteArray(data, offset, length));
    311   }
    312 
    313   @HiddenApi
    314   @Implementation(maxSdk = KITKAT_WATCH)
    315   public static void nativeAppendFrom(int thisNativePtr, int otherNativePtr, int offset, int length) {
    316     nativeAppendFrom((long) thisNativePtr, otherNativePtr, offset, length);
    317   }
    318 
    319   @Implementation(minSdk = LOLLIPOP)
    320   public static void nativeAppendFrom(long thisNativePtr, long otherNativePtr, int offset, int length) {
    321     ByteBuffer thisByteBuffer = NATIVE_PTR_TO_PARCEL.get(thisNativePtr);
    322     ByteBuffer otherByteBuffer = NATIVE_PTR_TO_PARCEL.get(otherNativePtr);
    323     thisByteBuffer.appendFrom(otherByteBuffer, offset, length);
    324   }
    325 
    326   @HiddenApi
    327   @Implementation(maxSdk = KITKAT_WATCH)
    328   public static void nativeWriteInterfaceToken(int nativePtr, String interfaceName) {
    329     nativeWriteInterfaceToken((long) nativePtr, interfaceName);
    330   }
    331 
    332   @Implementation(minSdk = LOLLIPOP)
    333   public static void nativeWriteInterfaceToken(long nativePtr, String interfaceName) {
    334     // Write StrictMode.ThreadPolicy bits (assume 0 for test).
    335     nativeWriteInt(nativePtr, 0);
    336     nativeWriteString(nativePtr, interfaceName);
    337   }
    338 
    339   @HiddenApi
    340   @Implementation(maxSdk = KITKAT_WATCH)
    341   public static void nativeEnforceInterface(int nativePtr, String interfaceName) {
    342     nativeEnforceInterface((long) nativePtr, interfaceName);
    343   }
    344 
    345   @Implementation(minSdk = LOLLIPOP)
    346   public static void nativeEnforceInterface(long nativePtr, String interfaceName) {
    347     // Consume StrictMode.ThreadPolicy bits (don't bother setting in test).
    348     nativeReadInt(nativePtr);
    349     String actualInterfaceName = nativeReadString(nativePtr);
    350     if (!Objects.equals(interfaceName, actualInterfaceName)) {
    351       throw new SecurityException("Binder invocation to an incorrect interface");
    352     }
    353   }
    354 
    355   private static class ByteBuffer {
    356 
    357     // List of elements where a pair is a piece of data and the sizeof that data
    358     private List<Pair<Integer, ?>> buffer = new ArrayList<>();
    359     private int index;
    360 
    361     /**
    362      * Removes all elements from the byte buffer
    363      */
    364     public void clear() {
    365       index = 0;
    366       buffer.clear();
    367     }
    368 
    369     /**
    370      * Reads a byte array from the byte buffer based on the current data position
    371      */
    372     public byte[] readByteArray() {
    373       int length = readInt();
    374       if (length == -1) {
    375         return null;
    376       }
    377       byte[] array = new byte[length];
    378       for (int i = 0; i < length; i++) {
    379         array[i] = readByte();
    380       }
    381       return array;
    382     }
    383 
    384     /**
    385      * Writes a byte to the byte buffer at the current data position
    386      */
    387     public void writeByte(byte b) {
    388       writeValue(Byte.SIZE / 8, b);
    389     }
    390 
    391     /**
    392      * Writes a byte array starting at offset for length bytes to the byte buffer at the current
    393      * data position
    394      */
    395     public void writeByteArray(byte[] b, int offset, int length) {
    396       writeInt(b.length);
    397       for (int i = offset; i < offset + length && i < b.length; i++) {
    398         writeByte(b[i]);
    399       }
    400     }
    401 
    402     /**
    403      * Reads a byte from the byte buffer based on the current data position
    404      */
    405     public byte readByte() {
    406       return readValue((byte) 0);
    407     }
    408 
    409     /**
    410      * Writes an int to the byte buffer at the current data position
    411      */
    412     public void writeInt(int i) {
    413       writeValue(Integer.SIZE / 8, i);
    414     }
    415 
    416     /**
    417      * Reads a int from the byte buffer based on the current data position
    418      */
    419     public int readInt() {
    420       return readValue(0);
    421     }
    422 
    423     /**
    424      * Writes a long to the byte buffer at the current data position
    425      */
    426     public void writeLong(long l) {
    427       writeValue(Long.SIZE / 8, l);
    428     }
    429 
    430     /**
    431      * Reads a long from the byte buffer based on the current data position
    432      */
    433     public long readLong() {
    434       return readValue(0L);
    435     }
    436 
    437     /**
    438      * Writes a float to the byte buffer at the current data position
    439      */
    440     public void writeFloat(float f) {
    441       writeValue(Float.SIZE / 8, f);
    442     }
    443 
    444     /**
    445      * Reads a float from the byte buffer based on the current data position
    446      */
    447     public float readFloat() {
    448       return readValue(0f);
    449     }
    450 
    451     /**
    452      * Writes a double to the byte buffer at the current data position
    453      */
    454     public void writeDouble(double d) {
    455       writeValue(Double.SIZE / 8, d);
    456     }
    457 
    458     /**
    459      * Reads a double from the byte buffer based on the current data position
    460      */
    461     public double readDouble() {
    462       return readValue(0d);
    463     }
    464 
    465     /**
    466      * Writes a String to the byte buffer at the current data position
    467      */
    468     public void writeString(String s) {
    469       int length = TextUtils.isEmpty(s) ? Integer.SIZE / 8 : s.length();
    470       writeValue(length, s);
    471     }
    472 
    473     /**
    474      * Reads a String from the byte buffer based on the current data position
    475      */
    476     public String readString() {
    477       return readValue(null);
    478     }
    479 
    480     /**
    481      * Appends the contents of the other byte buffer to this byte buffer
    482      * starting at offset and ending at length.
    483      *
    484      * @param other ByteBuffer to append to this one
    485      * @param offset number of bytes from beginning of byte buffer to start copy from
    486      * @param length number of bytes to copy
    487      */
    488     public void appendFrom(ByteBuffer other, int offset, int length) {
    489       int otherIndex = other.toIndex(offset);
    490       int otherEndIndex = other.toIndex(offset + length);
    491       for (int i = otherIndex; i < otherEndIndex && i < other.buffer.size(); i++) {
    492         int elementSize = other.buffer.get(i).first;
    493         Object elementValue = other.buffer.get(i).second;
    494         writeValue(elementSize, elementValue);
    495       }
    496     }
    497 
    498     /**
    499      * Creates a Byte buffer from a raw byte array.
    500      *
    501      * @param array byte array to read from
    502      * @param offset starting position in bytes to start reading array at
    503      * @param length number of bytes to read from array
    504      */
    505     public static ByteBuffer fromByteArray(byte[] array, int offset, int length) {
    506       ByteBuffer byteBuffer = new ByteBuffer();
    507 
    508       try {
    509         ByteArrayInputStream bis = new ByteArrayInputStream(array, offset,
    510             length);
    511         ObjectInputStream ois = new ObjectInputStream(bis);
    512         int numElements = ois.readInt();
    513         for (int i = 0; i < numElements; i++) {
    514           int sizeOf = ois.readInt();
    515           Object value = ois.readObject();
    516           byteBuffer.buffer.add(Pair.create(sizeOf, value));
    517         }
    518         return byteBuffer;
    519       } catch (Exception e) {
    520         throw new RuntimeException(e);
    521       }
    522     }
    523 
    524     /**
    525      * Converts a ByteBuffer to a raw byte array. This method should be
    526      * symmetrical with fromByteArray.
    527      */
    528     public byte[] toByteArray() {
    529       try {
    530         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    531         ObjectOutputStream oos = new ObjectOutputStream(bos);
    532         int length = buffer.size();
    533         oos.writeInt(length);
    534         for (Pair<Integer, ?> element : buffer) {
    535           oos.writeInt(element.first);
    536           oos.writeObject(element.second);
    537         }
    538         return bos.toByteArray();
    539       } catch (IOException e) {
    540         throw new RuntimeException(e);
    541       }
    542     }
    543 
    544     /**
    545      * Number of unused bytes in this byte buffer.
    546      */
    547     public int dataAvailable() {
    548       return dataSize() - dataPosition();
    549     }
    550 
    551     /**
    552      * Total buffer size in bytes of byte buffer included unused space.
    553      */
    554     public int dataCapacity() {
    555       return dataSize();
    556     }
    557 
    558     /**
    559      * Current data position of byte buffer in bytes. Reads / writes are from this position.
    560      */
    561     public int dataPosition() {
    562       return toDataPosition(index);
    563     }
    564 
    565     /**
    566      * Current amount of bytes currently written for ByteBuffer.
    567      */
    568     public int dataSize() {
    569       int totalSize = totalSize();
    570       int dataPosition = dataPosition();
    571       return totalSize > dataPosition ? totalSize : dataPosition;
    572     }
    573 
    574     /**
    575      * Sets the current data position.
    576      *
    577      * @param pos
    578      *          Desired position in bytes
    579      */
    580     public void setDataPosition(int pos) {
    581       index = toIndex(pos);
    582     }
    583 
    584     public void setDataSize(int size) {
    585       // TODO
    586     }
    587 
    588     public void setDataCapacity(int size) {
    589       // TODO
    590     }
    591 
    592     private int totalSize() {
    593       int size = 0;
    594       for (Pair<Integer, ?> element : buffer) {
    595         size += element.first;
    596       }
    597       return size;
    598     }
    599 
    600     private <T> T readValue(T defaultValue) {
    601       return (index < buffer.size()) ? (T) buffer.get(index++).second : defaultValue;
    602     }
    603 
    604     private void writeValue(int i, Object o) {
    605       Pair<Integer, ?> value = Pair.create(i, o);
    606       if (index < buffer.size()) {
    607         buffer.set(index, value);
    608       } else {
    609         buffer.add(value);
    610       }
    611       index++;
    612     }
    613 
    614     private int toDataPosition(int index) {
    615       int pos = 0;
    616       for (int i = 0; i < index; i++) {
    617         pos += buffer.get(i).first;
    618       }
    619       return pos;
    620     }
    621 
    622     private int toIndex(int dataPosition) {
    623       int calculatedPos = 0;
    624       int i = 0;
    625       for (; i < buffer.size() && calculatedPos < dataPosition; i++) {
    626         calculatedPos += buffer.get(i).first;
    627       }
    628       return i;
    629     }
    630   }
    631 }
    632