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