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