• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 
4 
5 import static android.os.Build.VERSION_CODES.M;
6 import static android.os.Build.VERSION_CODES.O_MR1;
7 import static android.os.Build.VERSION_CODES.P;
8 import static android.os.Build.VERSION_CODES.Q;
9 import static android.os.Build.VERSION_CODES.R;
10 import static android.os.Build.VERSION_CODES.S;
11 import static android.os.Build.VERSION_CODES.TIRAMISU;
12 
13 import android.os.BadParcelableException;
14 import android.os.IBinder;
15 import android.os.Parcel;
16 import android.os.ParcelFileDescriptor;
17 import android.os.Parcelable;
18 import android.os.Parcelable.Creator;
19 import android.util.Log;
20 import android.util.Pair;
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.FileDescriptor;
24 import java.io.IOException;
25 import java.io.ObjectInputStream;
26 import java.io.ObjectOutputStream;
27 import java.io.RandomAccessFile;
28 import java.io.Serializable;
29 import java.lang.reflect.Field;
30 import java.lang.reflect.Modifier;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Objects;
37 import org.robolectric.annotation.HiddenApi;
38 import org.robolectric.annotation.Implementation;
39 import org.robolectric.annotation.Implements;
40 import org.robolectric.annotation.RealObject;
41 import org.robolectric.annotation.Resetter;
42 import org.robolectric.res.android.NativeObjRegistry;
43 import org.robolectric.util.ReflectionHelpers;
44 import org.robolectric.util.ReflectionHelpers.ClassParameter;
45 
46 /**
47  * Robolectric's {@link Parcel} pretends to be backed by a byte buffer, closely matching {@link
48  * Parcel}'s position, size, and capacity behavior. However, its internal pure-Java representation
49  * is strongly typed, to detect non-portable code and common testing mistakes. It may throw {@link
50  * IllegalArgumentException} or {@link IllegalStateException} for error-prone behavior normal {@link
51  * Parcel} tolerates.
52  */
53 @Implements(value = Parcel.class, looseSignatures = true)
54 public class ShadowParcel {
55   protected static final String TAG = "Parcel";
56 
57   @RealObject private Parcel realObject;
58 
59   private static final NativeObjRegistry<ByteBuffer> NATIVE_BYTE_BUFFER_REGISTRY =
60       new NativeObjRegistry<>(ByteBuffer.class);
61 
62   private static final HashMap<ClassLoader, HashMap<String, Pair<Creator<?>, Class<?>>>>
63       pairedCreators = new HashMap<>();
64 
65 
66 
67   @HiddenApi
68   @Implementation
readParcelableCreator(ClassLoader loader)69   public Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
70     // note: calling `readString` will also consume the string, and increment the data-pointer.
71     // which is exactly what we need, since we do not call the real `readParcelableCreator`.
72     final String name = realObject.readString();
73     if (name == null) {
74       return null;
75     }
76 
77     Parcelable.Creator<?> creator;
78     try {
79       // If loader == null, explicitly emulate Class.forName(String) "caller
80       // classloader" behavior.
81       ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader);
82       // Avoid initializing the Parcelable class until we know it implements
83       // Parcelable and has the necessary CREATOR field.
84       Class<?> parcelableClass = Class.forName(name, false /* initialize */, parcelableClassLoader);
85       if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
86         throw new BadParcelableException(
87             "Parcelable protocol requires that the " + "class implements Parcelable");
88       }
89       Field f = parcelableClass.getField("CREATOR");
90 
91       // this is a fix for JDK8<->Android VM incompatibility:
92       // Apparently, JDK will not allow access to a public field if its
93       // class is not visible (private or package-private) from the call-site.
94       f.setAccessible(true);
95 
96       if ((f.getModifiers() & Modifier.STATIC) == 0) {
97         throw new BadParcelableException(
98             "Parcelable protocol requires " + "the CREATOR object to be static on class " + name);
99       }
100       Class<?> creatorType = f.getType();
101       if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
102         // Fail before calling Field.get(), not after, to avoid initializing
103         // parcelableClass unnecessarily.
104         throw new BadParcelableException(
105             "Parcelable protocol requires a "
106                 + "Parcelable.Creator object called "
107                 + "CREATOR on class "
108                 + name);
109       }
110       creator = (Parcelable.Creator<?>) f.get(null);
111     } catch (IllegalAccessException e) {
112       Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
113       throw new BadParcelableException("IllegalAccessException when unmarshalling: " + name);
114     } catch (ClassNotFoundException e) {
115       Log.e(TAG, "Class not found when unmarshalling: " + name, e);
116       throw new BadParcelableException("ClassNotFoundException when unmarshalling: " + name);
117     } catch (NoSuchFieldException e) {
118       throw new BadParcelableException(
119           "Parcelable protocol requires a "
120               + "Parcelable.Creator object called "
121               + "CREATOR on class "
122               + name);
123     }
124     if (creator == null) {
125       throw new BadParcelableException(
126           "Parcelable protocol requires a "
127               + "non-null Parcelable.Creator object called "
128               + "CREATOR on class "
129               + name);
130     }
131     return creator;
132   }
133 
134   /**
135    * The goal of this shadow method is to workaround a JVM/ART incompatibility.
136    *
137    * <p>In ART, a public field is visible regardless whether or not the enclosing class is public.
138    * On the JVM, this is not the case. For compatibility, we need to use {@link
139    * Field#setAccessible(boolean)} to simulate the same behavior.
140    */
141   @SuppressWarnings("unchecked")
142   @Implementation(minSdk = TIRAMISU)
readParcelableCreatorInternal( ClassLoader loader, Class<T> clazz)143   protected <T> Parcelable.Creator<T> readParcelableCreatorInternal(
144       ClassLoader loader, Class<T> clazz) {
145     String name = realObject.readString();
146     if (name == null) {
147       return null;
148     }
149 
150     Pair<Creator<?>, Class<?>> creatorAndParcelableClass;
151     synchronized (pairedCreators) {
152       HashMap<String, Pair<Creator<?>, Class<?>>> map = pairedCreators.get(loader);
153       if (map == null) {
154         pairedCreators.put(loader, new HashMap<>());
155         creatorAndParcelableClass = null;
156       } else {
157         creatorAndParcelableClass = map.get(name);
158       }
159     }
160 
161     if (creatorAndParcelableClass != null) {
162       Parcelable.Creator<?> creator = creatorAndParcelableClass.first;
163       Class<?> parcelableClass = creatorAndParcelableClass.second;
164       if (clazz != null) {
165         if (!clazz.isAssignableFrom(parcelableClass)) {
166           throw newBadTypeParcelableException(
167               "Parcelable creator "
168                   + name
169                   + " is not "
170                   + "a subclass of required class "
171                   + clazz.getName()
172                   + " provided in the parameter");
173         }
174       }
175 
176       return (Parcelable.Creator<T>) creator;
177     }
178 
179     Parcelable.Creator<?> creator;
180     Class<?> parcelableClass;
181     try {
182       // If loader == null, explicitly emulate Class.forName(String) "caller
183       // classloader" behavior.
184       ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader);
185       // Avoid initializing the Parcelable class until we know it implements
186       // Parcelable and has the necessary CREATOR field.
187       parcelableClass = Class.forName(name, /* initialize= */ false, parcelableClassLoader);
188       if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
189         throw new BadParcelableException(
190             "Parcelable protocol requires subclassing " + "from Parcelable on class " + name);
191       }
192       if (clazz != null) {
193         if (!clazz.isAssignableFrom(parcelableClass)) {
194           throw newBadTypeParcelableException(
195               "Parcelable creator "
196                   + name
197                   + " is not "
198                   + "a subclass of required class "
199                   + clazz.getName()
200                   + " provided in the parameter");
201         }
202       }
203 
204       Field f = parcelableClass.getField("CREATOR");
205 
206       // this is a fix for JDK8<->Android VM incompatibility:
207       // Apparently, JDK will not allow access to a public field if its
208       // class is not visible (private or package-private) from the call-site.
209       f.setAccessible(true);
210 
211       if ((f.getModifiers() & Modifier.STATIC) == 0) {
212         throw new BadParcelableException(
213             "Parcelable protocol requires " + "the CREATOR object to be static on class " + name);
214       }
215       Class<?> creatorType = f.getType();
216       if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
217         // Fail before calling Field.get(), not after, to avoid initializing
218         // parcelableClass unnecessarily.
219         throw new BadParcelableException(
220             "Parcelable protocol requires a "
221                 + "Parcelable.Creator object called "
222                 + "CREATOR on class "
223                 + name);
224       }
225       creator = (Parcelable.Creator<?>) f.get(null);
226     } catch (IllegalAccessException e) {
227       Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
228       throw new BadParcelableException("IllegalAccessException when unmarshalling: " + name, e);
229     } catch (ClassNotFoundException e) {
230       Log.e(TAG, "Class not found when unmarshalling: " + name, e);
231       throw new BadParcelableException("ClassNotFoundException when unmarshalling: " + name, e);
232     } catch (NoSuchFieldException e) {
233       throw new BadParcelableException(
234           "Parcelable protocol requires a "
235               + "Parcelable.Creator object called "
236               + "CREATOR on class "
237               + name,
238           e);
239     }
240     if (creator == null) {
241       throw new BadParcelableException(
242           "Parcelable protocol requires a "
243               + "non-null Parcelable.Creator object called "
244               + "CREATOR on class "
245               + name);
246     }
247 
248     synchronized (pairedCreators) {
249       pairedCreators.get(loader).put(name, Pair.create(creator, parcelableClass));
250     }
251 
252     return (Parcelable.Creator<T>) creator;
253   }
254 
newBadTypeParcelableException(String message)255   private BadParcelableException newBadTypeParcelableException(String message) {
256     try {
257       return (BadParcelableException)
258           ReflectionHelpers.callConstructor(
259               Class.forName("android.os.BadTypeParcelableException"),
260               ClassParameter.from(String.class, message));
261     } catch (ClassNotFoundException e) {
262       throw new LinkageError(e.getMessage(), e);
263     }
264   }
265 
266   @Implementation
writeByteArray(byte[] b, int offset, int len)267   protected void writeByteArray(byte[] b, int offset, int len) {
268     if (b == null) {
269       realObject.writeInt(-1);
270       return;
271     }
272     long nativePtr = ReflectionHelpers.getField(realObject, "mNativePtr");
273     nativeWriteByteArray(nativePtr, b, offset, len);
274   }
275 
276   @Implementation
nativeDataSize(long nativePtr)277   protected static int nativeDataSize(long nativePtr) {
278     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).dataSize();
279   }
280 
281   @Implementation
nativeDataAvail(long nativePtr)282   protected static int nativeDataAvail(long nativePtr) {
283     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).dataAvailable();
284   }
285 
286   @Implementation
nativeDataPosition(long nativePtr)287   protected static int nativeDataPosition(long nativePtr) {
288     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).dataPosition();
289   }
290 
291   @Implementation
nativeDataCapacity(long nativePtr)292   protected static int nativeDataCapacity(long nativePtr) {
293     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).dataCapacity();
294   }
295 
296   @Implementation
297   @SuppressWarnings("robolectric.ShadowReturnTypeMismatch")
nativeSetDataSize(long nativePtr, int size)298   protected static void nativeSetDataSize(long nativePtr, int size) {
299     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).setDataSize(size);
300   }
301 
302   @Implementation
nativeSetDataPosition(long nativePtr, int pos)303   protected static void nativeSetDataPosition(long nativePtr, int pos) {
304     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).setDataPosition(pos);
305   }
306 
307   @Implementation
nativeSetDataCapacity(long nativePtr, int size)308   protected static void nativeSetDataCapacity(long nativePtr, int size) {
309     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).setDataCapacityAtLeast(size);
310   }
311 
312   @Implementation
nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len)313   protected static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
314     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeByteArray(b, offset, len);
315   }
316 
317   // duplicate the writeBlob implementation from latest android, to avoid referencing the
318   // non-existent-in-JDK java.util.Arrays.checkOffsetAndCount method.
319   @Implementation(minSdk = M)
writeBlob(byte[] b, int offset, int len)320   protected void writeBlob(byte[] b, int offset, int len) {
321     if (b == null) {
322       realObject.writeInt(-1);
323       return;
324     }
325     throwsIfOutOfBounds(b.length, offset, len);
326     long nativePtr = ReflectionHelpers.getField(realObject, "mNativePtr");
327     nativeWriteBlob(nativePtr, b, offset, len);
328   }
329 
throwsIfOutOfBounds(int len, int offset, int count)330   private static void throwsIfOutOfBounds(int len, int offset, int count) {
331     if (len < 0) {
332       throw new ArrayIndexOutOfBoundsException("Negative length: " + len);
333     }
334 
335     if ((offset | count) < 0 || offset > len - count) {
336       throw new ArrayIndexOutOfBoundsException();
337     }
338   }
339 
340   // nativeWriteBlob was introduced in lollipop, thus no need for a int nativePtr variant
341   @Implementation
nativeWriteBlob(long nativePtr, byte[] b, int offset, int len)342   protected static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) {
343     nativeWriteByteArray(nativePtr, b, offset, len);
344   }
345 
346   @Implementation(maxSdk = R)
nativeWriteInt(long nativePtr, int val)347   protected static void nativeWriteInt(long nativePtr, int val) {
348     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeInt(val);
349   }
350 
351   @Implementation(maxSdk = R)
nativeWriteLong(long nativePtr, long val)352   protected static void nativeWriteLong(long nativePtr, long val) {
353     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeLong(val);
354   }
355 
356   @Implementation(maxSdk = R)
nativeWriteFloat(long nativePtr, float val)357   protected static void nativeWriteFloat(long nativePtr, float val) {
358     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeFloat(val);
359   }
360 
361   @Implementation(maxSdk = R)
nativeWriteDouble(long nativePtr, double val)362   protected static void nativeWriteDouble(long nativePtr, double val) {
363     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeDouble(val);
364   }
365 
366   @Implementation(maxSdk = Q)
nativeWriteString(long nativePtr, String val)367   protected static void nativeWriteString(long nativePtr, String val) {
368     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeString(val);
369   }
370 
371   @Implementation
nativeWriteStrongBinder(long nativePtr, IBinder val)372   protected static void nativeWriteStrongBinder(long nativePtr, IBinder val) {
373     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeStrongBinder(val);
374   }
375 
376   @Implementation
nativeCreateByteArray(long nativePtr)377   protected static byte[] nativeCreateByteArray(long nativePtr) {
378     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).createByteArray();
379   }
380 
381   // nativeReadBlob was introduced in lollipop, thus no need for a int nativePtr variant
382   @Implementation
nativeReadBlob(long nativePtr)383   protected static byte[] nativeReadBlob(long nativePtr) {
384     return nativeCreateByteArray(nativePtr);
385   }
386 
387   @Implementation(minSdk = O_MR1)
nativeReadByteArray(long nativePtr, byte[] dest, int destLen)388   protected static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) {
389     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readByteArray(dest, destLen);
390   }
391 
392   @Implementation
nativeReadInt(long nativePtr)393   protected static int nativeReadInt(long nativePtr) {
394     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readInt();
395   }
396 
397   @Implementation
nativeReadLong(long nativePtr)398   protected static long nativeReadLong(long nativePtr) {
399     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readLong();
400   }
401 
402   @Implementation
nativeReadFloat(long nativePtr)403   protected static float nativeReadFloat(long nativePtr) {
404     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readFloat();
405   }
406 
407   @Implementation
nativeReadDouble(long nativePtr)408   protected static double nativeReadDouble(long nativePtr) {
409     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readDouble();
410   }
411 
412   @Implementation(maxSdk = Q)
nativeReadString(long nativePtr)413   protected static String nativeReadString(long nativePtr) {
414     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readString();
415   }
416 
417   @Implementation
nativeReadStrongBinder(long nativePtr)418   protected static IBinder nativeReadStrongBinder(long nativePtr) {
419     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readStrongBinder();
420   }
421 
422   @Implementation
423   @HiddenApi
nativeCreate()424   public static long nativeCreate() {
425     return NATIVE_BYTE_BUFFER_REGISTRY.register(new ByteBuffer());
426   }
427 
428   @Implementation
429   @SuppressWarnings("robolectric.ShadowReturnTypeMismatch")
nativeFreeBuffer(long nativePtr)430   protected static void nativeFreeBuffer(long nativePtr) {
431     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).clear();
432   }
433 
434   @Implementation
nativeDestroy(long nativePtr)435   protected static void nativeDestroy(long nativePtr) {
436     NATIVE_BYTE_BUFFER_REGISTRY.unregister(nativePtr);
437   }
438 
439   @Implementation
nativeMarshall(long nativePtr)440   protected static byte[] nativeMarshall(long nativePtr) {
441     return NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).toByteArray();
442   }
443 
444   @Implementation
445   @SuppressWarnings("robolectric.ShadowReturnTypeMismatch")
nativeUnmarshall(long nativePtr, byte[] data, int offset, int length)446   protected static void nativeUnmarshall(long nativePtr, byte[] data, int offset, int length) {
447     NATIVE_BYTE_BUFFER_REGISTRY.update(nativePtr, ByteBuffer.fromByteArray(data, offset, length));
448   }
449 
450   @Implementation
451   @SuppressWarnings("robolectric.ShadowReturnTypeMismatch")
nativeAppendFrom( long thisNativePtr, long otherNativePtr, int offset, int length)452   protected static void nativeAppendFrom(
453       long thisNativePtr, long otherNativePtr, int offset, int length) {
454     ByteBuffer thisByteBuffer = NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(thisNativePtr);
455     ByteBuffer otherByteBuffer = NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(otherNativePtr);
456     thisByteBuffer.appendFrom(otherByteBuffer, offset, length);
457   }
458 
459   @Implementation
nativeWriteInterfaceToken(long nativePtr, String interfaceName)460   protected static void nativeWriteInterfaceToken(long nativePtr, String interfaceName) {
461     // Write StrictMode.ThreadPolicy bits (assume 0 for test).
462     nativeWriteInt(nativePtr, 0);
463     nativeWriteString(nativePtr, interfaceName);
464   }
465 
466   @Implementation
nativeEnforceInterface(long nativePtr, String interfaceName)467   protected static void nativeEnforceInterface(long nativePtr, String interfaceName) {
468     // Consume StrictMode.ThreadPolicy bits (don't bother setting in test).
469     nativeReadInt(nativePtr);
470     String actualInterfaceName = nativeReadString(nativePtr);
471     if (!Objects.equals(interfaceName, actualInterfaceName)) {
472       throw new SecurityException("Binder invocation to an incorrect interface");
473     }
474   }
475 
476   /**
477    * Robolectric-specific error thrown when tests exercise error-prone behavior in Parcel.
478    *
479    * <p>Standard Android parcels rarely throw exceptions, but will happily behave in unintended
480    * ways. Parcels are not strongly typed, so will happily re-interpret corrupt contents in ways
481    * that cause hard-to-diagnose failures, or will cause tests to pass when they should not.
482    * ShadowParcel attempts to detect these conditions.
483    *
484    * <p>This exception is package-private because production or test code should never catch or rely
485    * on this, and may be changed to be an Error (rather than Exception) in the future.
486    */
487   static class UnreliableBehaviorError extends AssertionError {
UnreliableBehaviorError(String message)488     UnreliableBehaviorError(String message) {
489       super(message);
490     }
491 
UnreliableBehaviorError(String message, Throwable cause)492     UnreliableBehaviorError(String message, Throwable cause) {
493       super(message, cause);
494     }
495 
UnreliableBehaviorError( Class<?> clazz, int position, ByteBuffer.FakeEncodedItem item, String extraMessage)496     UnreliableBehaviorError(
497         Class<?> clazz, int position, ByteBuffer.FakeEncodedItem item, String extraMessage) {
498       super(
499           String.format(
500               Locale.US,
501               "Looking for %s at position %d, found %s [%s] taking %d bytes, %s",
502               clazz.getSimpleName(),
503               position,
504               item.value == null ? "null" : item.value.getClass().getSimpleName(),
505               item.value,
506               item.sizeBytes,
507               extraMessage));
508     }
509   }
510 
511   /**
512    * ByteBuffer pretends to be the underlying Parcel implementation.
513    *
514    * <p>It faithfully simulates Parcel's handling of position, size, and capacity, but is strongly
515    * typed internally. It was debated whether this should instead faithfully represent Android's
516    * Parcel bug-for-bug as a true byte array, along with all of its error-tolerant behavior and
517    * ability essentially to {@code reinterpret_cast} data. However, the fail-fast behavior here has
518    * found several test bugs and avoids reliance on architecture-specific details like Endian-ness.
519    *
520    * <p>Quirky behavior this explicitly emulates:
521    *
522    * <ul>
523    *   <li>Continuing to read past the end returns zeros/nulls.
524    *   <li>{@link setDataCapacity} never decreases buffer size.
525    *   <li>It is possible to partially or completely overwrite byte ranges in the buffer.
526    *   <li>Zero bytes can be exchanged between primitive data types and empty array/string.
527    * </ul>
528    *
529    * <p>Quirky behavior this forbids:
530    *
531    * <ul>
532    *   <li>Reading past the end after writing without calling setDataPosition(0), since there's no
533    *       legitimate reason to do this, and is a very common test bug.
534    *   <li>Writing one type and reading another; for example, writing a Long and reading two
535    *       Integers, or writing a byte array and reading a String. This, effectively like {@code
536    *       reinterpret_cast}, may not be portable across architectures.
537    *   <li>Similarly, reading from objects that have been truncated or partially overwritten, or
538    *       reading from the middle of them.
539    *   <li>Using appendFrom to overwrite data, which in Parcel will overwrite the data <i>and</i>
540    *       expand data size by the same amount, introducing empty gaps.
541    *   <li>Reading from or marshalling buffers with uninitialized gaps (e.g. where data position was
542    *       expanded but nothing was written)
543    * </ul>
544    *
545    * <p>Possibly-unwanted divergent behavior:
546    *
547    * <ul>
548    *   <li>Reading an object will often return the same instance that was written.
549    *   <li>The marshalled form does not at all resemble Parcel's. This is to maintain compatibility
550    *       with existing clients that rely on the Java-serialization-based format.
551    *   <li>Uses substantially more memory, since each "byte" takes at minimum 4 bytes for a pointer,
552    *       and even more for the overhead of allocating a record for each write. But note there is
553    *       only at most one allocation for every 4 byte positions.
554    * </ul>
555    */
556   private static class ByteBuffer {
557     /** Number of bytes in Parcel used by an int, length, or anything smaller. */
558     private static final int INT_SIZE_BYTES = 4;
559     /** Number of bytes in Parcel used by a long or double. */
560     private static final int LONG_OR_DOUBLE_SIZE_BYTES = 8;
561     /** Immutable empty byte array. */
562     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
563 
564     /** Representation for an item that has been serialized in a parcel. */
565     private static class FakeEncodedItem implements Serializable {
566       /** Number of consecutive bytes consumed by this object. */
567       final int sizeBytes;
568       /** The original typed value stored. */
569       final Object value;
570       /**
571        * Whether this item's byte-encoding is all zero.
572        *
573        * <p>This is the one exception to strong typing in ShadowParcel. Since zero can be portably
574        * handled by many primitive types as zeros, and strings and arrays as empty. Note that when
575        * zeroes are successfully read, the size of this entry may be ignored and the position may
576        * progress to the middle of this, which remains safe as long as types that handle zeros are
577        * used.
578        */
579       final boolean isEncodedAsAllZeroBytes;
580 
FakeEncodedItem(int sizeBytes, Object value)581       FakeEncodedItem(int sizeBytes, Object value) {
582         this.sizeBytes = sizeBytes;
583         this.value = value;
584         this.isEncodedAsAllZeroBytes = isEncodedAsAllZeroBytes(value);
585       }
586     }
587 
588     /**
589      * A type-safe simulation of the Parcel's data buffer.
590      *
591      * <p>Each index represents a byte of the parcel. Instead of storing raw bytes, this contains
592      * records containing both the original data (in its original Java type) as well as the length.
593      * Consecutive indices will point to the same FakeEncodedItem instance; for example, an item
594      * with sizeBytes of 24 will, in normal cases, have references from 24 consecutive indices.
595      *
596      * <p>There are two main fail-fast features in this type-safe buffer. First, objects may only be
597      * read from the parcel as the same type they were stored with, enforced by casting. Second,
598      * this fails fast when reading incomplete or partially overwritten items.
599      *
600      * <p>Even though writing a custom resizable array is a code smell vs ArrayList, arrays' fixed
601      * capacity closely models Parcel's dataCapacity (which we emulate anyway), and bulk array
602      * utilities are robust compared to ArrayList's bulk operations.
603      */
604     private FakeEncodedItem[] data;
605     /** The read/write pointer. */
606     private int dataPosition;
607     /** The length of the buffer; the capacity is data.length. */
608     private int dataSize;
609     /**
610      * Whether the next read should fail if it's past the end of the array.
611      *
612      * <p>This is set true when modifying the end of the buffer, and cleared if a data position was
613      * explicitly set.
614      */
615     private boolean failNextReadIfPastEnd;
616 
ByteBuffer()617     ByteBuffer() {
618       clear();
619     }
620 
621     /** Removes all elements from the byte buffer */
clear()622     public void clear() {
623       data = new FakeEncodedItem[0];
624       dataPosition = 0;
625       dataSize = 0;
626       failNextReadIfPastEnd = false;
627     }
628 
629     /** Reads a byte array from the byte buffer based on the current data position */
createByteArray()630     public byte[] createByteArray() {
631       // It would be simpler just to store the byte array without a separate length.  However, the
632       // "non-native" code in Parcel short-circuits null to -1, so this must consistently write a
633       // separate length field in all cases.
634       int length = readInt();
635       if (length == -1) {
636         return null;
637       }
638       if (length == 0) {
639         return EMPTY_BYTE_ARRAY;
640       }
641       Object current = peek();
642       if (current instanceof Byte) {
643         // Legacy-encoded byte arrays (created by some tests) encode individual bytes, and do not
644         // align to the integer.
645         return readLegacyByteArray(length);
646       } else if (readZeroes(alignToInt(length))) {
647         return new byte[length];
648       }
649       byte[] result = readValue(EMPTY_BYTE_ARRAY, byte[].class, /* allowNull= */ false);
650       if (result.length != length) {
651         // Looks like the length doesn't correspond to the array.
652         throw new UnreliableBehaviorError(
653             String.format(
654                 Locale.US,
655                 "Byte array's length prefix is %d but real length is %d",
656                 length,
657                 result.length));
658       }
659       return result;
660     }
661 
662     /** Reads a byte array encoded the way ShadowParcel previously encoded byte arrays. */
readLegacyByteArray(int length)663     private byte[] readLegacyByteArray(int length) {
664       // Some tests rely on ShadowParcel's previous byte-by-byte encoding.
665       byte[] result = new byte[length];
666       for (int i = 0; i < length; i++) {
667         result[i] = readPrimitive(1, (byte) 0, Byte.class);
668       }
669       return result;
670     }
671 
672     /** Reads a byte array from the byte buffer based on the current data position */
readByteArray(byte[] dest, int destLen)673     public boolean readByteArray(byte[] dest, int destLen) {
674       byte[] result = createByteArray();
675       if (result == null || destLen != result.length) {
676         // Since older versions of Android (pre O MR1) don't call this method at all, let's be more
677         // consistent with them and let android.os.Parcel throw RuntimeException, instead of
678         // throwing a more helpful exception.
679         return false;
680       }
681       System.arraycopy(result, 0, dest, 0, destLen);
682       return true;
683     }
684 
685     /**
686      * Writes a byte array starting at offset for length bytes to the byte buffer at the current
687      * data position
688      */
writeByteArray(byte[] b, int offset, int length)689     public void writeByteArray(byte[] b, int offset, int length) {
690       writeInt(length);
691       // Native parcel writes a byte array as length plus the individual bytes.  But we can't write
692       // bytes individually because each byte would take up 4 bytes due to Parcel's alignment
693       // behavior.  Instead we write the length, and if non-empty, we write the array.
694       if (length != 0) {
695         writeValue(length, Arrays.copyOfRange(b, offset, offset + length));
696       }
697     }
698 
699     /** Writes an int to the byte buffer at the current data position */
writeInt(int i)700     public void writeInt(int i) {
701       writeValue(INT_SIZE_BYTES, i);
702     }
703 
704     /** Reads a int from the byte buffer based on the current data position */
readInt()705     public int readInt() {
706       return readPrimitive(INT_SIZE_BYTES, 0, Integer.class);
707     }
708 
709     /** Writes a long to the byte buffer at the current data position */
writeLong(long l)710     public void writeLong(long l) {
711       writeValue(LONG_OR_DOUBLE_SIZE_BYTES, l);
712     }
713 
714     /** Reads a long from the byte buffer based on the current data position */
readLong()715     public long readLong() {
716       return readPrimitive(LONG_OR_DOUBLE_SIZE_BYTES, 0L, Long.class);
717     }
718 
719     /** Writes a float to the byte buffer at the current data position */
writeFloat(float f)720     public void writeFloat(float f) {
721       writeValue(INT_SIZE_BYTES, f);
722     }
723 
724     /** Reads a float from the byte buffer based on the current data position */
readFloat()725     public float readFloat() {
726       return readPrimitive(INT_SIZE_BYTES, 0f, Float.class);
727     }
728 
729     /** Writes a double to the byte buffer at the current data position */
writeDouble(double d)730     public void writeDouble(double d) {
731       writeValue(LONG_OR_DOUBLE_SIZE_BYTES, d);
732     }
733 
734     /** Reads a double from the byte buffer based on the current data position */
readDouble()735     public double readDouble() {
736       return readPrimitive(LONG_OR_DOUBLE_SIZE_BYTES, 0d, Double.class);
737     }
738 
739     /** Writes a String to the byte buffer at the current data position */
writeString(String s)740     public void writeString(String s) {
741       int nullTerminatedChars = (s != null) ? (s.length() + 1) : 0;
742       // Android encodes strings as length plus a null-terminated array of 2-byte characters.
743       // writeValue will pad to nearest 4 bytes.  Null is encoded as just -1.
744       int sizeBytes = INT_SIZE_BYTES + (nullTerminatedChars * 2);
745       writeValue(sizeBytes, s);
746     }
747 
748     /** Reads a String from the byte buffer based on the current data position */
readString()749     public String readString() {
750       if (readZeroes(INT_SIZE_BYTES * 2)) {
751         // Empty string is 4 bytes for length of 0, and 4 bytes for null terminator and padding.
752         return "";
753       }
754       return readValue(null, String.class, /* allowNull= */ true);
755     }
756 
757     /** Writes an IBinder to the byte buffer at the current data position */
writeStrongBinder(IBinder b)758     public void writeStrongBinder(IBinder b) {
759       // Size of struct flat_binder_object in android/binder.h used to encode binders in the real
760       // parceling code.
761       int length = 5 * INT_SIZE_BYTES;
762       writeValue(length, b);
763     }
764 
765     /** Reads an IBinder from the byte buffer based on the current data position */
readStrongBinder()766     public IBinder readStrongBinder() {
767       return readValue(null, IBinder.class, /* allowNull= */ true);
768     }
769 
770     /**
771      * Appends the contents of the other byte buffer to this byte buffer starting at offset and
772      * ending at length.
773      *
774      * @param other ByteBuffer to append to this one
775      * @param offset number of bytes from beginning of byte buffer to start copy from
776      * @param length number of bytes to copy
777      */
appendFrom(ByteBuffer other, int offset, int length)778     public void appendFrom(ByteBuffer other, int offset, int length) {
779       int oldSize = dataSize;
780       if (dataPosition != dataSize) {
781         // Parcel.cpp will always expand the buffer by length even if it is overwriting existing
782         // data, yielding extra uninitialized data at the end, in contrast to write methods that
783         // won't increase the data length if they are overwriting in place.  This is surprising
784         // behavior that production code should avoid.
785         throw new UnreliableBehaviorError(
786             "Real Android parcels behave unreliably if appendFrom is "
787                 + "called from any position other than the end");
788       }
789       setDataSize(oldSize + length);
790       // Just blindly copy whatever happens to be in the buffer.  Reads will validate whether any
791       // of the objects were only incompletely copied.
792       System.arraycopy(other.data, offset, data, dataPosition, length);
793       dataPosition += length;
794       failNextReadIfPastEnd = true;
795     }
796 
797     /** Returns whether a data type is encoded as all zeroes. */
isEncodedAsAllZeroBytes(Object value)798     private static boolean isEncodedAsAllZeroBytes(Object value) {
799       if (value == null) {
800         return false; // Nulls are usually encoded as -1.
801       }
802       if (value instanceof Number) {
803         Number number = (Number) value;
804         return number.longValue() == 0 && number.doubleValue() == 0;
805       }
806       if (value instanceof byte[]) {
807         byte[] array = (byte[]) value;
808         return isAllZeroes(array, 0, array.length);
809       }
810       // NOTE: While empty string is all zeros, trying to read an empty string as zeroes is
811       // probably unintended; the reverse is supported just so all-zero buffers don't fail.
812       return false;
813     }
814 
815     /** Identifies all zeroes, which can be safely reinterpreted to other types. */
isAllZeroes(byte[] array, int offset, int length)816     private static boolean isAllZeroes(byte[] array, int offset, int length) {
817       for (int i = offset; i < length; i++) {
818         if (array[i] != 0) {
819           return false;
820         }
821       }
822       return true;
823     }
824 
825     /**
826      * Creates a Byte buffer from a raw byte array.
827      *
828      * @param array byte array to read from
829      * @param offset starting position in bytes to start reading array at
830      * @param length number of bytes to read from array
831      */
832     @SuppressWarnings("BanSerializableRead")
fromByteArray(byte[] array, int offset, int length)833     public static ByteBuffer fromByteArray(byte[] array, int offset, int length) {
834       ByteBuffer byteBuffer = new ByteBuffer();
835 
836       if (isAllZeroes(array, offset, length)) {
837         // Special case: for all zeroes, it's definitely not an ObjectInputStream, because it has a
838         // non-zero mandatory magic.  Zeroes have a portable, unambiguous interpretation.
839         byteBuffer.setDataSize(length);
840         byteBuffer.writeItem(new FakeEncodedItem(length, new byte[length]));
841         return byteBuffer;
842       }
843 
844       try {
845         ByteArrayInputStream bis = new ByteArrayInputStream(array, offset, length);
846         ObjectInputStream ois = new ObjectInputStream(bis);
847         int numElements = ois.readInt();
848         for (int i = 0; i < numElements; i++) {
849           int sizeOf = ois.readInt();
850           Object value = ois.readObject();
851           // NOTE: Bypassing writeValue so that this will support ShadowParcels that were
852           // marshalled before ShadowParcel simulated alignment.
853           byteBuffer.writeItem(new FakeEncodedItem(sizeOf, value));
854         }
855         // Android leaves the data position at the end in this case.
856         return byteBuffer;
857       } catch (Exception e) {
858         throw new UnreliableBehaviorError("ShadowParcel unable to unmarshall its custom format", e);
859       }
860     }
861 
862     /**
863      * Converts a ByteBuffer to a raw byte array. This method should be symmetrical with
864      * fromByteArray.
865      */
toByteArray()866     public byte[] toByteArray() {
867       int oldDataPosition = dataPosition;
868       try {
869         ByteArrayOutputStream bos = new ByteArrayOutputStream();
870         ObjectOutputStream oos = new ObjectOutputStream(bos);
871         // NOTE: Serializing the data array would be simpler, and serialization would actually
872         // preserve reference equality between entries.  However, the length-encoded format here
873         // preserves the previous format, which some tests appear to rely on.
874         List<FakeEncodedItem> entries = new ArrayList<>();
875         // NOTE: Use readNextItem to scan so the contents can be proactively validated.
876         dataPosition = 0;
877         while (dataPosition < dataSize) {
878           entries.add(readNextItem(Object.class));
879         }
880         oos.writeInt(entries.size());
881         for (FakeEncodedItem item : entries) {
882           oos.writeInt(item.sizeBytes);
883           oos.writeObject(item.value);
884         }
885         oos.flush();
886         return bos.toByteArray();
887       } catch (IOException e) {
888         throw new UnreliableBehaviorError("ErrorProne unable to serialize its custom format", e);
889       } finally {
890         dataPosition = oldDataPosition;
891       }
892     }
893 
894     /** Number of unused bytes in this byte buffer. */
dataAvailable()895     public int dataAvailable() {
896       return dataSize() - dataPosition();
897     }
898 
899     /** Total buffer size in bytes of byte buffer included unused space. */
dataCapacity()900     public int dataCapacity() {
901       return data.length;
902     }
903 
904     /** Current data position of byte buffer in bytes. Reads / writes are from this position. */
dataPosition()905     public int dataPosition() {
906       return dataPosition;
907     }
908 
909     /** Current amount of bytes currently written for ByteBuffer. */
dataSize()910     public int dataSize() {
911       return dataSize;
912     }
913 
914     /**
915      * Sets the current data position.
916      *
917      * @param pos Desired position in bytes
918      */
setDataPosition(int pos)919     public void setDataPosition(int pos) {
920       if (pos > dataSize) {
921         // NOTE: Real parcel ignores this until a write occurs.
922         throw new UnreliableBehaviorError(pos + " greater than dataSize " + dataSize);
923       }
924       dataPosition = pos;
925       failNextReadIfPastEnd = false;
926     }
927 
setDataSize(int size)928     public void setDataSize(int size) {
929       if (size < dataSize) {
930         // Clear all the inaccessible bytes when shrinking, to allow garbage collection, and so
931         // they remain cleared if expanded again.  Note this might truncate something mid-object,
932         // which would be handled at read time.
933         Arrays.fill(data, size, dataSize, null);
934       }
935       setDataCapacityAtLeast(size);
936       dataSize = size;
937       if (dataPosition >= dataSize) {
938         dataPosition = dataSize;
939       }
940     }
941 
setDataCapacityAtLeast(int newCapacity)942     public void setDataCapacityAtLeast(int newCapacity) {
943       // NOTE: Oddly, Parcel only every increases data capacity, and never decreases it, so this
944       // really should have never been named setDataCapacity.
945       if (newCapacity > data.length) {
946         FakeEncodedItem[] newData = new FakeEncodedItem[newCapacity];
947         dataSize = Math.min(dataSize, newCapacity);
948         dataPosition = Math.min(dataPosition, dataSize);
949         System.arraycopy(data, 0, newData, 0, dataSize);
950         data = newData;
951       }
952     }
953 
954     /** Rounds to next 4-byte bounder similar to native Parcel. */
alignToInt(int unpaddedSizeBytes)955     private int alignToInt(int unpaddedSizeBytes) {
956       return ((unpaddedSizeBytes + 3) / 4) * 4;
957     }
958 
959     /**
960      * Ensures that the next sizeBytes are all the initial value we read.
961      *
962      * <p>This detects:
963      *
964      * <ul>
965      *   <li>Reading an item, but not starting at its start position
966      *   <li>Reading items that were truncated by setSize
967      *   <li>Reading items that were partially overwritten by another
968      * </ul>
969      */
checkConsistentReadAndIncrementPosition(Class<?> clazz, FakeEncodedItem item)970     private void checkConsistentReadAndIncrementPosition(Class<?> clazz, FakeEncodedItem item) {
971       int endPosition = dataPosition + item.sizeBytes;
972       for (int i = dataPosition; i < endPosition; i++) {
973         FakeEncodedItem foundItemItem = i < dataSize ? data[i] : null;
974         if (foundItemItem != item) {
975           throw new UnreliableBehaviorError(
976               clazz,
977               dataPosition,
978               item,
979               String.format(
980                   Locale.US,
981                   "but [%s] interrupts it at position %d",
982                   foundItemItem == null
983                       ? "uninitialized data or the end of the buffer"
984                       : foundItemItem.value,
985                   i));
986         }
987       }
988       dataPosition = Math.min(dataSize, dataPosition + item.sizeBytes);
989     }
990 
991     /** Returns the item at the current position, or null if uninitialized or null. */
992     private Object peek() {
993       return dataPosition < dataSize && data[dataPosition] != null
994           ? data[dataPosition].value
995           : null;
996     }
997 
998     /**
999      * Reads a complete item in the byte buffer.
1000      *
1001      * @param clazz this is the type that is being read, but not checked in this method
1002      * @return null if the default value should be returned, otherwise the item holding the data
1003      */
1004     private <T> FakeEncodedItem readNextItem(Class<T> clazz) {
1005       FakeEncodedItem item = data[dataPosition];
1006       if (item == null) {
1007         // While Parcel will treat these as zeros, in tests, this is almost always an error.
1008         throw new UnreliableBehaviorError("Reading uninitialized data at position " + dataPosition);
1009       }
1010       checkConsistentReadAndIncrementPosition(clazz, item);
1011       return item;
1012     }
1013 
1014     /**
1015      * Reads the next value in the byte buffer of a specified type.
1016      *
1017      * @param pastEndValue value to return when reading past the end of the buffer
1018      * @param clazz this is the type that is being read, but not checked in this method
1019      * @param allowNull whether null values are permitted
1020      */
1021     private <T> T readValue(T pastEndValue, Class<T> clazz, boolean allowNull) {
1022       if (dataPosition >= dataSize) {
1023         // Normally, reading past the end is permitted, and returns the default values.  However,
1024         // writing to a parcel then reading without setting the position back to 0 is an incredibly
1025         // common error to make in tests, and should never really happen in production code, so
1026         // this shadow will fail in this condition.
1027         if (failNextReadIfPastEnd) {
1028           throw new UnreliableBehaviorError(
1029               "Did you forget to setDataPosition(0) before reading the parcel?");
1030         }
1031         return pastEndValue;
1032       }
1033       int startPosition = dataPosition;
1034       FakeEncodedItem item = readNextItem(clazz);
1035       if (item == null) {
1036         return pastEndValue;
1037       } else if (item.value == null && allowNull) {
1038         return null;
1039       } else if (clazz.isInstance(item.value)) {
1040         return clazz.cast(item.value);
1041       } else {
1042         // Numerous existing tests rely on ShadowParcel throwing RuntimeException and catching
1043         // them.  Many of these tests are trying to test what happens when an invalid Parcel is
1044         // provided.  However, Android has no concept of an "invalid parcel" because Parcel will
1045         // happily return garbage if you ask for it.  The only runtime exceptions are thrown on
1046         // array length mismatches, or higher-level APIs like Parcelable (which has its own safety
1047         // checks).  Tests trying to test error-handling behavior should instead craft a Parcel
1048         // that specifically triggers a BadParcelableException.
1049         throw new RuntimeException(
1050             new UnreliableBehaviorError(
1051                 clazz, startPosition, item, "and it is non-portable to reinterpret it"));
1052       }
1053     }
1054 
1055     /**
1056      * Determines if there is a sequence of castable zeroes, and consumes them.
1057      *
1058      * <p>This is the only exception for strong typing, because zero bytes are portable and
1059      * unambiguous. There are a few situations where well-written code can rely on this, so it is
1060      * worthwhile making a special exception for. This tolerates partially-overwritten and truncated
1061      * values if all bytes are zero.
1062      */
1063     private boolean readZeroes(int bytes) {
1064       int endPosition = dataPosition + bytes;
1065       if (endPosition > dataSize) {
1066         return false;
1067       }
1068       for (int i = dataPosition; i < endPosition; i++) {
1069         if (data[i] == null || !data[i].isEncodedAsAllZeroBytes) {
1070           return false;
1071         }
1072       }
1073       // Note in this case we short-circuit other verification -- even if we are reading weirdly
1074       // clobbered zeroes, they're still zeroes.  Future reads might fail, though.
1075       dataPosition = endPosition;
1076       return true;
1077     }
1078 
1079     /**
1080      * Reads a primitive, which may reinterpret zeros of other types.
1081      *
1082      * @param defaultSizeBytes if reinterpreting zeros, the number of bytes to consume
1083      * @param defaultValue the default value for zeros or reading past the end
1084      * @param clazz this is the type that is being read, but not checked in this method
1085      */
readPrimitive(int defaultSizeBytes, T defaultValue, Class<T> clazz)1086     private <T> T readPrimitive(int defaultSizeBytes, T defaultValue, Class<T> clazz) {
1087       // Check for zeroes first, since partially-overwritten values are not an error for zeroes.
1088       if (readZeroes(defaultSizeBytes)) {
1089         return defaultValue;
1090       }
1091       return readValue(defaultValue, clazz, /* allowNull= */ false);
1092     }
1093 
1094     /** Writes an encoded item directly, bypassing alignment, and possibly repeating an item. */
writeItem(FakeEncodedItem item)1095     private void writeItem(FakeEncodedItem item) {
1096       int endPosition = dataPosition + item.sizeBytes;
1097       if (endPosition > data.length) {
1098         // Parcel grows by 3/2 of the new size.
1099         setDataCapacityAtLeast(endPosition * 3 / 2);
1100       }
1101       if (endPosition > dataSize) {
1102         failNextReadIfPastEnd = true;
1103         dataSize = endPosition;
1104       }
1105       Arrays.fill(data, dataPosition, endPosition, item);
1106       dataPosition = endPosition;
1107     }
1108 
1109     /**
1110      * Writes a value to the next range of bytes.
1111      *
1112      * <p>Writes are aligned to 4-byte regions.
1113      */
writeValue(int unpaddedSizeBytes, Object o)1114     private void writeValue(int unpaddedSizeBytes, Object o) {
1115       // Create the item with its final, aligned byte size.
1116       writeItem(new FakeEncodedItem(alignToInt(unpaddedSizeBytes), o));
1117     }
1118   }
1119 
1120   @Implementation(maxSdk = P)
openFileDescriptor(String file, int mode)1121   protected static FileDescriptor openFileDescriptor(String file, int mode) throws IOException {
1122     RandomAccessFile randomAccessFile =
1123         new RandomAccessFile(file, mode == ParcelFileDescriptor.MODE_READ_ONLY ? "r" : "rw");
1124     return randomAccessFile.getFD();
1125   }
1126 
1127   @Implementation(minSdk = M, maxSdk = R)
nativeWriteFileDescriptor(long nativePtr, FileDescriptor val)1128   protected static long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val) {
1129     // The Java version of FileDescriptor stored the fd in a field called "fd", and the Android
1130     // version changed the field name to "descriptor". But it looks like Robolectric uses the
1131     // Java version of FileDescriptor instead of the Android version.
1132     int fd = ReflectionHelpers.getField(val, "fd");
1133     NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).writeInt(fd);
1134     return (long) nativeDataPosition(nativePtr);
1135   }
1136 
1137   @Implementation(minSdk = M)
nativeReadFileDescriptor(long nativePtr)1138   protected static FileDescriptor nativeReadFileDescriptor(long nativePtr) {
1139     int fd = NATIVE_BYTE_BUFFER_REGISTRY.getNativeObject(nativePtr).readInt();
1140     return ReflectionHelpers.callConstructor(
1141         FileDescriptor.class, ClassParameter.from(int.class, fd));
1142   }
1143 
1144   @Implementation(minSdk = R)
nativeWriteString8(long nativePtr, String val)1145   protected static void nativeWriteString8(long nativePtr, String val) {
1146     nativeWriteString(nativePtr, val);
1147   }
1148 
1149   @Implementation(minSdk = R)
nativeWriteString16(long nativePtr, String val)1150   protected static void nativeWriteString16(long nativePtr, String val) {
1151     nativeWriteString(nativePtr, val);
1152   }
1153 
1154   @Implementation(minSdk = R)
nativeReadString8(long nativePtr)1155   protected static String nativeReadString8(long nativePtr) {
1156     return nativeReadString(nativePtr);
1157   }
1158 
1159   @Implementation(minSdk = R)
nativeReadString16(long nativePtr)1160   protected static String nativeReadString16(long nativePtr) {
1161     return nativeReadString(nativePtr);
1162   }
1163 
1164   // need to use looseSignatures for the S methods because method signatures differ only by return
1165   // type
1166   @Implementation(minSdk = S)
nativeWriteInt(Object nativePtr, Object val)1167   protected static int nativeWriteInt(Object nativePtr, Object val) {
1168     nativeWriteInt((long) nativePtr, (int) val);
1169     return 0; /* OK */
1170   }
1171 
1172   @Implementation(minSdk = S)
nativeWriteLong(Object nativePtr, Object val)1173   protected static int nativeWriteLong(Object nativePtr, Object val) {
1174     nativeWriteLong((long) nativePtr, (long) val);
1175     return 0; /* OK */
1176   }
1177 
1178   @Implementation(minSdk = S)
nativeWriteFloat(Object nativePtr, Object val)1179   protected static int nativeWriteFloat(Object nativePtr, Object val) {
1180     nativeWriteFloat((long) nativePtr, (float) val);
1181     return 0; /* OK */
1182   }
1183 
1184   @Implementation(minSdk = S)
nativeWriteDouble(Object nativePtr, Object val)1185   protected static int nativeWriteDouble(Object nativePtr, Object val) {
1186     nativeWriteDouble((long) nativePtr, (double) val);
1187     return 0; /* OK */
1188   }
1189 
1190   @Implementation(minSdk = S)
nativeWriteFileDescriptor(Object nativePtr, Object val)1191   protected static void nativeWriteFileDescriptor(Object nativePtr, Object val) {
1192     nativeWriteFileDescriptor((long) nativePtr, (FileDescriptor) val);
1193   }
1194 
1195   @Resetter
reset()1196   public static void reset() {
1197     pairedCreators.clear();
1198   }
1199 }
1200