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