1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.appsearch.safeparcel; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.PendingIntent; 22 import android.os.Bundle; 23 import android.os.IBinder; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.util.SparseArray; 27 import android.util.SparseBooleanArray; 28 import android.util.SparseIntArray; 29 import android.util.SparseLongArray; 30 31 import java.math.BigDecimal; 32 import java.math.BigInteger; 33 import java.util.List; 34 35 /** 36 * Functions to write a safe parcel. A safe parcel consists of a sequence of header/payload bytes. 37 * 38 * <p>The header is 16 bits of size and 16 bits of field id. If the size in the header is 0xffff, 39 * the next 4 bytes are the size field instead. 40 * 41 * @hide 42 */ 43 // Include the SafeParcel source code directly in AppSearch until it gets officially open-sourced. 44 public class SafeParcelWriter { 45 46 static final int OBJECT_HEADER = 0x00004f45; 47 SafeParcelWriter()48 private SafeParcelWriter() {} 49 writeHeader(Parcel p, int id, int size)50 private static void writeHeader(Parcel p, int id, int size) { 51 if (size >= 0x0000ffff) { 52 p.writeInt(0xffff0000 | id); 53 p.writeInt(size); 54 } else { 55 p.writeInt((size << 16) | id); 56 } 57 } 58 59 /** Returns the cookie that should be passed to endVariableData. */ beginVariableData(Parcel p, int id)60 private static int beginVariableData(Parcel p, int id) { 61 // Since we don't know the size yet, assume it might be big and always use the 62 // size overflow. 63 p.writeInt(0xffff0000 | id); 64 p.writeInt(0); 65 return p.dataPosition(); 66 } 67 68 /** 69 * @param start The result of the paired beginVariableData. 70 */ finishVariableData(Parcel p, int start)71 private static void finishVariableData(Parcel p, int start) { 72 int end = p.dataPosition(); 73 int size = end - start; 74 // The size is one int before start. 75 p.setDataPosition(start - 4); 76 p.writeInt(size); 77 p.setDataPosition(end); 78 } 79 80 /** Begins the objects header. */ beginObjectHeader(@onNull Parcel p)81 public static int beginObjectHeader(@NonNull Parcel p) { 82 return beginVariableData(p, OBJECT_HEADER); 83 } 84 85 /** Finishes the objects header. */ finishObjectHeader(@onNull Parcel p, int start)86 public static void finishObjectHeader(@NonNull Parcel p, int start) { 87 finishVariableData(p, start); 88 } 89 90 /** Writes a boolean. */ writeBoolean(@onNull Parcel p, int id, boolean val)91 public static void writeBoolean(@NonNull Parcel p, int id, boolean val) { 92 writeHeader(p, id, 4); 93 p.writeInt(val ? 1 : 0); 94 } 95 96 /** Writes a {@link Boolean} object. */ writeBooleanObject( @onNull Parcel p, int id, @Nullable Boolean val, boolean writeNull)97 public static void writeBooleanObject( 98 @NonNull Parcel p, int id, @Nullable Boolean val, boolean writeNull) { 99 if (val == null) { 100 if (writeNull) { 101 writeHeader(p, id, 0); 102 } 103 return; 104 } 105 106 writeHeader(p, id, 4); 107 p.writeInt(val ? 1 : 0); 108 } 109 110 /** Writes a byte. */ writeByte(@onNull Parcel p, int id, byte val)111 public static void writeByte(@NonNull Parcel p, int id, byte val) { 112 writeHeader(p, id, 4); 113 p.writeInt(val); 114 } 115 116 /** Writes a char. */ writeChar(@onNull Parcel p, int id, char val)117 public static void writeChar(@NonNull Parcel p, int id, char val) { 118 writeHeader(p, id, 4); 119 p.writeInt(val); 120 } 121 122 /** Writes a short. */ writeShort(@onNull Parcel p, int id, short val)123 public static void writeShort(@NonNull Parcel p, int id, short val) { 124 writeHeader(p, id, 4); 125 p.writeInt(val); 126 } 127 128 /** Writes an int. */ writeInt(@onNull Parcel p, int id, int val)129 public static void writeInt(@NonNull Parcel p, int id, int val) { 130 writeHeader(p, id, 4); 131 p.writeInt(val); 132 } 133 134 /** Writes an {@link Integer}. */ writeIntegerObject( @onNull Parcel p, int id, @Nullable Integer val, boolean writeNull)135 public static void writeIntegerObject( 136 @NonNull Parcel p, int id, @Nullable Integer val, boolean writeNull) { 137 if (val == null) { 138 if (writeNull) { 139 writeHeader(p, id, 0); 140 } 141 return; 142 } 143 writeHeader(p, id, 4); 144 p.writeInt(val); 145 } 146 147 /** Writes a long. */ writeLong(@onNull Parcel p, int id, long val)148 public static void writeLong(@NonNull Parcel p, int id, long val) { 149 writeHeader(p, id, 8); 150 p.writeLong(val); 151 } 152 153 /** Writes a {@link Long}. */ writeLongObject( @onNull Parcel p, int id, @Nullable Long val, boolean writeNull)154 public static void writeLongObject( 155 @NonNull Parcel p, int id, @Nullable Long val, boolean writeNull) { 156 if (val == null) { 157 if (writeNull) { 158 writeHeader(p, id, 0); 159 } 160 return; 161 } 162 writeHeader(p, id, 8); 163 p.writeLong(val); 164 } 165 166 /** Writes a {@link BigInteger}. */ writeBigInteger( @onNull Parcel p, int id, @Nullable BigInteger val, boolean writeNull)167 public static void writeBigInteger( 168 @NonNull Parcel p, int id, @Nullable BigInteger val, boolean writeNull) { 169 if (val == null) { 170 if (writeNull) { 171 writeHeader(p, id, 0); 172 } 173 return; 174 } 175 int start = beginVariableData(p, id); 176 p.writeByteArray(val.toByteArray()); 177 finishVariableData(p, start); 178 } 179 180 /** Writes a float. */ writeFloat(@onNull Parcel p, int id, float val)181 public static void writeFloat(@NonNull Parcel p, int id, float val) { 182 writeHeader(p, id, 4); 183 p.writeFloat(val); 184 } 185 186 /** Writes a {@link Float}. */ writeFloatObject( @onNull Parcel p, int id, @Nullable Float val, boolean writeNull)187 public static void writeFloatObject( 188 @NonNull Parcel p, int id, @Nullable Float val, boolean writeNull) { 189 if (val == null) { 190 if (writeNull) { 191 writeHeader(p, id, 0); 192 } 193 return; 194 } 195 writeHeader(p, id, 4); 196 p.writeFloat(val); 197 } 198 199 /** Writes a double. */ writeDouble(@onNull Parcel p, int id, double val)200 public static void writeDouble(@NonNull Parcel p, int id, double val) { 201 writeHeader(p, id, 8); 202 p.writeDouble(val); 203 } 204 205 /** Writes a {@link Double} object. */ writeDoubleObject( @onNull Parcel p, int id, @Nullable Double val, boolean writeNull)206 public static void writeDoubleObject( 207 @NonNull Parcel p, int id, @Nullable Double val, boolean writeNull) { 208 if (val == null) { 209 if (writeNull) { 210 writeHeader(p, id, 0); 211 } 212 return; 213 } 214 writeHeader(p, id, 8); 215 p.writeDouble(val); 216 } 217 218 /** Writes a {@link BigDecimal}. */ writeBigDecimal( @onNull Parcel p, int id, @Nullable BigDecimal val, boolean writeNull)219 public static void writeBigDecimal( 220 @NonNull Parcel p, int id, @Nullable BigDecimal val, boolean writeNull) { 221 if (val == null) { 222 if (writeNull) { 223 writeHeader(p, id, 0); 224 } 225 return; 226 } 227 int start = beginVariableData(p, id); 228 p.writeByteArray(val.unscaledValue().toByteArray()); 229 p.writeInt(val.scale()); 230 finishVariableData(p, start); 231 } 232 233 /** Writes a {@link String}. */ writeString( @onNull Parcel p, int id, @Nullable String val, boolean writeNull)234 public static void writeString( 235 @NonNull Parcel p, int id, @Nullable String val, boolean writeNull) { 236 if (val == null) { 237 if (writeNull) { 238 writeHeader(p, id, 0); 239 } 240 return; 241 } 242 int start = beginVariableData(p, id); 243 p.writeString(val); 244 finishVariableData(p, start); 245 } 246 247 /** Writes a {@link IBinder}. */ writeIBinder( @onNull Parcel p, int id, @Nullable IBinder val, boolean writeNull)248 public static void writeIBinder( 249 @NonNull Parcel p, int id, @Nullable IBinder val, boolean writeNull) { 250 if (val == null) { 251 if (writeNull) { 252 writeHeader(p, id, 0); 253 } 254 return; 255 } 256 // The size of the flat_binder_object in Parcel.cpp is not actually variable 257 // but is not part of the CDD, so treat it as variable. It almost certainly 258 // won't change between processes on a given device. 259 int start = beginVariableData(p, id); 260 p.writeStrongBinder(val); 261 finishVariableData(p, start); 262 } 263 264 /** Writes a {@link Parcelable}. */ writeParcelable( @onNull Parcel p, int id, @Nullable Parcelable val, int parcelableFlags, boolean writeNull)265 public static void writeParcelable( 266 @NonNull Parcel p, 267 int id, 268 @Nullable Parcelable val, 269 int parcelableFlags, 270 boolean writeNull) { 271 if (val == null) { 272 if (writeNull) { 273 writeHeader(p, id, 0); 274 } 275 return; 276 } 277 int start = beginVariableData(p, id); 278 val.writeToParcel(p, parcelableFlags); 279 finishVariableData(p, start); 280 } 281 282 /** Writes a {@link Bundle}. */ writeBundle( @onNull Parcel p, int id, @Nullable Bundle val, boolean writeNull)283 public static void writeBundle( 284 @NonNull Parcel p, int id, @Nullable Bundle val, boolean writeNull) { 285 if (val == null) { 286 if (writeNull) { 287 writeHeader(p, id, 0); 288 } 289 return; 290 } 291 int start = beginVariableData(p, id); 292 p.writeBundle(val); 293 finishVariableData(p, start); 294 } 295 296 /** Writes a byte array. */ writeByteArray( @onNull Parcel p, int id, @Nullable byte[] buf, boolean writeNull)297 public static void writeByteArray( 298 @NonNull Parcel p, int id, @Nullable byte[] buf, boolean writeNull) { 299 if (buf == null) { 300 if (writeNull) { 301 writeHeader(p, id, 0); 302 } 303 return; 304 } 305 int start = beginVariableData(p, id); 306 p.writeByteArray(buf); 307 finishVariableData(p, start); 308 } 309 310 /** Writes a byte array array. */ writeByteArrayArray( @onNull Parcel p, int id, @Nullable byte[][] buf, boolean writeNull)311 public static void writeByteArrayArray( 312 @NonNull Parcel p, int id, @Nullable byte[][] buf, boolean writeNull) { 313 if (buf == null) { 314 if (writeNull) { 315 writeHeader(p, id, 0); 316 } 317 return; 318 } 319 int start = beginVariableData(p, id); 320 final int length = buf.length; 321 p.writeInt(length); 322 for (int i = 0; i < length; i++) { 323 p.writeByteArray(buf[i]); 324 } 325 finishVariableData(p, start); 326 } 327 328 /** Writes a boolean array. */ writeBooleanArray( @onNull Parcel p, int id, @Nullable boolean[] val, boolean writeNull)329 public static void writeBooleanArray( 330 @NonNull Parcel p, int id, @Nullable boolean[] val, boolean writeNull) { 331 if (val == null) { 332 if (writeNull) { 333 writeHeader(p, id, 0); 334 } 335 return; 336 } 337 int start = beginVariableData(p, id); 338 p.writeBooleanArray(val); 339 finishVariableData(p, start); 340 } 341 342 /** Writes a char array. */ writeCharArray( @onNull Parcel p, int id, @Nullable char[] val, boolean writeNull)343 public static void writeCharArray( 344 @NonNull Parcel p, int id, @Nullable char[] val, boolean writeNull) { 345 if (val == null) { 346 if (writeNull) { 347 writeHeader(p, id, 0); 348 } 349 return; 350 } 351 int start = beginVariableData(p, id); 352 p.writeCharArray(val); 353 finishVariableData(p, start); 354 } 355 356 /** Writes an int array. */ writeIntArray( @onNull Parcel p, int id, @Nullable int[] val, boolean writeNull)357 public static void writeIntArray( 358 @NonNull Parcel p, int id, @Nullable int[] val, boolean writeNull) { 359 if (val == null) { 360 if (writeNull) { 361 writeHeader(p, id, 0); 362 } 363 return; 364 } 365 int start = beginVariableData(p, id); 366 p.writeIntArray(val); 367 finishVariableData(p, start); 368 } 369 370 /** Writes a long array. */ writeLongArray( @onNull Parcel p, int id, @Nullable long[] val, boolean writeNull)371 public static void writeLongArray( 372 @NonNull Parcel p, int id, @Nullable long[] val, boolean writeNull) { 373 if (val == null) { 374 if (writeNull) { 375 writeHeader(p, id, 0); 376 } 377 return; 378 } 379 int start = beginVariableData(p, id); 380 p.writeLongArray(val); 381 finishVariableData(p, start); 382 } 383 384 /** Writes a {@link BigInteger} array. */ writeBigIntegerArray( @onNull Parcel p, int id, @Nullable BigInteger[] val, boolean writeNull)385 public static void writeBigIntegerArray( 386 @NonNull Parcel p, int id, @Nullable BigInteger[] val, boolean writeNull) { 387 if (val == null) { 388 if (writeNull) { 389 writeHeader(p, id, 0); 390 } 391 return; 392 } 393 int start = beginVariableData(p, id); 394 final int length = val.length; 395 p.writeInt(length); 396 for (int i = 0; i < length; i++) { 397 p.writeByteArray(val[i].toByteArray()); 398 } 399 finishVariableData(p, start); 400 } 401 402 /** Writes a float array. */ writeFloatArray( @onNull Parcel p, int id, @Nullable float[] val, boolean writeNull)403 public static void writeFloatArray( 404 @NonNull Parcel p, int id, @Nullable float[] val, boolean writeNull) { 405 if (val == null) { 406 if (writeNull) { 407 writeHeader(p, id, 0); 408 } 409 return; 410 } 411 int start = beginVariableData(p, id); 412 p.writeFloatArray(val); 413 finishVariableData(p, start); 414 } 415 416 /** Writes a double array. */ writeDoubleArray( @onNull Parcel p, int id, @Nullable double[] val, boolean writeNull)417 public static void writeDoubleArray( 418 @NonNull Parcel p, int id, @Nullable double[] val, boolean writeNull) { 419 if (val == null) { 420 if (writeNull) { 421 writeHeader(p, id, 0); 422 } 423 return; 424 } 425 int start = beginVariableData(p, id); 426 p.writeDoubleArray(val); 427 finishVariableData(p, start); 428 } 429 430 /** Writes a {@link BigDecimal} array. */ writeBigDecimalArray( @onNull Parcel p, int id, @Nullable BigDecimal[] val, boolean writeNull)431 public static void writeBigDecimalArray( 432 @NonNull Parcel p, int id, @Nullable BigDecimal[] val, boolean writeNull) { 433 if (val == null) { 434 if (writeNull) { 435 writeHeader(p, id, 0); 436 } 437 return; 438 } 439 int start = beginVariableData(p, id); 440 final int length = val.length; 441 p.writeInt(length); 442 for (int i = 0; i < length; i++) { 443 p.writeByteArray(val[i].unscaledValue().toByteArray()); 444 p.writeInt(val[i].scale()); 445 } 446 finishVariableData(p, start); 447 } 448 449 /** Writes a {@link String} array. */ writeStringArray( @onNull Parcel p, int id, @Nullable String[] val, boolean writeNull)450 public static void writeStringArray( 451 @NonNull Parcel p, int id, @Nullable String[] val, boolean writeNull) { 452 if (val == null) { 453 if (writeNull) { 454 writeHeader(p, id, 0); 455 } 456 return; 457 } 458 int start = beginVariableData(p, id); 459 p.writeStringArray(val); 460 finishVariableData(p, start); 461 } 462 463 /** Writes a {@link IBinder} array. */ writeIBinderArray( @onNull Parcel p, int id, @Nullable IBinder[] val, boolean writeNull)464 public static void writeIBinderArray( 465 @NonNull Parcel p, int id, @Nullable IBinder[] val, boolean writeNull) { 466 if (val == null) { 467 if (writeNull) { 468 writeHeader(p, id, 0); 469 } 470 return; 471 } 472 int start = beginVariableData(p, id); 473 p.writeBinderArray(val); 474 finishVariableData(p, start); 475 } 476 477 /** Writes a boolean list. */ writeBooleanList( @onNull Parcel p, int id, @Nullable List<Boolean> val, boolean writeNull)478 public static void writeBooleanList( 479 @NonNull Parcel p, int id, @Nullable List<Boolean> val, boolean writeNull) { 480 if (val == null) { 481 if (writeNull) { 482 writeHeader(p, id, 0); 483 } 484 return; 485 } 486 int start = beginVariableData(p, id); 487 final int size = val.size(); 488 p.writeInt(size); 489 for (int i = 0; i < size; i++) { 490 p.writeInt(val.get(i) ? 1 : 0); 491 } 492 finishVariableData(p, start); 493 } 494 495 /** Writes an {@link Integer} list. */ writeIntegerList( @onNull Parcel p, int id, @Nullable List<Integer> val, boolean writeNull)496 public static void writeIntegerList( 497 @NonNull Parcel p, int id, @Nullable List<Integer> val, boolean writeNull) { 498 if (val == null) { 499 if (writeNull) { 500 writeHeader(p, id, 0); 501 } 502 return; 503 } 504 int start = beginVariableData(p, id); 505 final int size = val.size(); 506 p.writeInt(size); 507 for (int i = 0; i < size; i++) { 508 p.writeInt(val.get(i)); 509 } 510 finishVariableData(p, start); 511 } 512 513 /** Writes a {@link Long} list. */ writeLongList( @onNull Parcel p, int id, @Nullable List<Long> val, boolean writeNull)514 public static void writeLongList( 515 @NonNull Parcel p, int id, @Nullable List<Long> val, boolean writeNull) { 516 if (val == null) { 517 if (writeNull) { 518 writeHeader(p, id, 0); 519 } 520 return; 521 } 522 int start = beginVariableData(p, id); 523 final int size = val.size(); 524 p.writeInt(size); 525 for (int i = 0; i < size; i++) { 526 p.writeLong(val.get(i)); 527 } 528 finishVariableData(p, start); 529 } 530 531 /** Writes a {@link Float} list. */ writeFloatList( @onNull Parcel p, int id, @Nullable List<Float> val, boolean writeNull)532 public static void writeFloatList( 533 @NonNull Parcel p, int id, @Nullable List<Float> val, boolean writeNull) { 534 if (val == null) { 535 if (writeNull) { 536 writeHeader(p, id, 0); 537 } 538 return; 539 } 540 int start = beginVariableData(p, id); 541 final int size = val.size(); 542 p.writeInt(size); 543 for (int i = 0; i < size; i++) { 544 p.writeFloat(val.get(i)); 545 } 546 finishVariableData(p, start); 547 } 548 549 /** Writes a {@link Double} list. */ writeDoubleList( @onNull Parcel p, int id, @Nullable List<Double> val, boolean writeNull)550 public static void writeDoubleList( 551 @NonNull Parcel p, int id, @Nullable List<Double> val, boolean writeNull) { 552 if (val == null) { 553 if (writeNull) { 554 writeHeader(p, id, 0); 555 } 556 return; 557 } 558 int start = beginVariableData(p, id); 559 final int size = val.size(); 560 p.writeInt(size); 561 for (int i = 0; i < size; i++) { 562 p.writeDouble(val.get(i)); 563 } 564 finishVariableData(p, start); 565 } 566 567 /** Writes a {@link String} list. */ writeStringList( @onNull Parcel p, int id, @Nullable List<String> val, boolean writeNull)568 public static void writeStringList( 569 @NonNull Parcel p, int id, @Nullable List<String> val, boolean writeNull) { 570 if (val == null) { 571 if (writeNull) { 572 writeHeader(p, id, 0); 573 } 574 return; 575 } 576 int start = beginVariableData(p, id); 577 p.writeStringList(val); 578 finishVariableData(p, start); 579 } 580 581 /** Writes a {@link IBinder} list. */ writeIBinderList( @onNull Parcel p, int id, @Nullable List<IBinder> val, boolean writeNull)582 public static void writeIBinderList( 583 @NonNull Parcel p, int id, @Nullable List<IBinder> val, boolean writeNull) { 584 if (val == null) { 585 if (writeNull) { 586 writeHeader(p, id, 0); 587 } 588 return; 589 } 590 int start = beginVariableData(p, id); 591 p.writeBinderList(val); 592 finishVariableData(p, start); 593 } 594 595 /** Writes a typed array. */ writeTypedArray( @onNull Parcel p, int id, @Nullable T[] val, int parcelableFlags, boolean writeNull)596 public static <T extends Parcelable> void writeTypedArray( 597 @NonNull Parcel p, int id, @Nullable T[] val, int parcelableFlags, boolean writeNull) { 598 if (val == null) { 599 if (writeNull) { 600 writeHeader(p, id, 0); 601 } 602 return; 603 } 604 int start = beginVariableData(p, id); 605 // We need to customize the built-in Parcel.writeTypedArray() because we need to write 606 // the sizes for each individual SafeParcelable objects since they can vary in size due 607 // to supporting missing fields. 608 final int length = val.length; 609 p.writeInt(length); 610 for (int i = 0; i < length; i++) { 611 T item = val[i]; 612 if (item == null) { 613 p.writeInt(0); 614 } else { 615 writeTypedItemWithSize(p, item, parcelableFlags); 616 } 617 } 618 finishVariableData(p, start); 619 } 620 621 /** Writes a typed list. */ writeTypedList( @onNull Parcel p, int id, @Nullable List<T> val, boolean writeNull)622 public static <T extends Parcelable> void writeTypedList( 623 @NonNull Parcel p, int id, @Nullable List<T> val, boolean writeNull) { 624 if (val == null) { 625 if (writeNull) { 626 writeHeader(p, id, 0); 627 } 628 return; 629 } 630 int start = beginVariableData(p, id); 631 // We need to customize the built-in Parcel.writeTypedList() because we need to write 632 // the sizes for each individual SafeParcelable objects since they can vary in size due 633 // supporting missing fields. 634 final int length = val.size(); 635 p.writeInt(length); 636 for (int i = 0; i < length; i++) { 637 T item = val.get(i); 638 if (item == null) { 639 p.writeInt(0); 640 } else { 641 writeTypedItemWithSize(p, item, 0); 642 } 643 } 644 finishVariableData(p, start); 645 } 646 647 /** Writes a typed item with size. */ writeTypedItemWithSize( Parcel p, T item, int parcelableFlags)648 private static <T extends Parcelable> void writeTypedItemWithSize( 649 Parcel p, T item, int parcelableFlags) { 650 // Just write a 1 as a placeholder since we don't know the exact size of item 651 // yet, and save the data position in Parcel p. 652 final int itemSizeDataPosition = p.dataPosition(); 653 p.writeInt(1); 654 final int itemStartPosition = p.dataPosition(); 655 item.writeToParcel(p, parcelableFlags); 656 final int currentDataPosition = p.dataPosition(); 657 658 // go back and write the length in bytes 659 p.setDataPosition(itemSizeDataPosition); 660 p.writeInt(currentDataPosition - itemStartPosition); 661 662 // set the parcel data position to where it was before 663 p.setDataPosition(currentDataPosition); 664 } 665 666 /** Writes a parcel. */ writeParcel( @onNull Parcel p, int id, @Nullable Parcel val, boolean writeNull)667 public static void writeParcel( 668 @NonNull Parcel p, int id, @Nullable Parcel val, boolean writeNull) { 669 if (val == null) { 670 if (writeNull) { 671 writeHeader(p, id, 0); 672 } 673 return; 674 } 675 int start = beginVariableData(p, id); 676 p.appendFrom(val, 0, val.dataSize()); 677 finishVariableData(p, start); 678 } 679 680 /** 681 * This is made to be compatible with writeTypedArray. See implementation of 682 * Parcel.writeTypedArray(T[] val, parcelableFlags); 683 */ writeParcelArray( @onNull Parcel p, int id, @Nullable Parcel[] val, boolean writeNull)684 public static void writeParcelArray( 685 @NonNull Parcel p, int id, @Nullable Parcel[] val, boolean writeNull) { 686 if (val == null) { 687 if (writeNull) { 688 writeHeader(p, id, 0); 689 } 690 return; 691 } 692 int start = beginVariableData(p, id); 693 final int length = val.length; 694 p.writeInt(length); 695 for (int i = 0; i < length; i++) { 696 Parcel item = val[i]; 697 if (item != null) { 698 p.writeInt(item.dataSize()); 699 // custom part 700 p.appendFrom(item, 0, item.dataSize()); 701 } else { 702 p.writeInt(0); 703 } 704 } 705 finishVariableData(p, start); 706 } 707 708 /** 709 * This is made to be compatible with writeTypedList. See implementation of 710 * Parce.writeTypedList(List<T> val). 711 */ writeParcelList( @onNull Parcel p, int id, @Nullable List<Parcel> val, boolean writeNull)712 public static void writeParcelList( 713 @NonNull Parcel p, int id, @Nullable List<Parcel> val, boolean writeNull) { 714 if (val == null) { 715 if (writeNull) { 716 writeHeader(p, id, 0); 717 } 718 return; 719 } 720 int start = beginVariableData(p, id); 721 final int size = val.size(); 722 p.writeInt(size); 723 for (int i = 0; i < size; i++) { 724 Parcel item = val.get(i); 725 if (item != null) { 726 p.writeInt(item.dataSize()); 727 // custom part 728 p.appendFrom(item, 0, item.dataSize()); 729 } else { 730 p.writeInt(0); 731 } 732 } 733 finishVariableData(p, start); 734 } 735 736 /** Writes a {@link PendingIntent}. */ writePendingIntent( @onNull Parcel p, int id, @Nullable PendingIntent val, boolean writeNull)737 public static void writePendingIntent( 738 @NonNull Parcel p, int id, @Nullable PendingIntent val, boolean writeNull) { 739 if (val == null) { 740 if (writeNull) { 741 writeHeader(p, id, 0); 742 } 743 return; 744 } 745 int start = beginVariableData(p, id); 746 PendingIntent.writePendingIntentOrNullToParcel(val, p); 747 finishVariableData(p, start); 748 } 749 750 /** Writes a list. */ writeList( @onNull Parcel p, int id, @SuppressWarnings("rawtypes") @Nullable List list, boolean writeNull)751 public static void writeList( 752 @NonNull Parcel p, 753 int id, 754 @SuppressWarnings("rawtypes") @Nullable List list, 755 boolean writeNull) { 756 if (list == null) { 757 if (writeNull) { 758 writeHeader(p, id, 0); 759 } 760 return; 761 } 762 int start = beginVariableData(p, id); 763 p.writeList(list); 764 finishVariableData(p, start); 765 } 766 767 /** Writes a {@link SparseBooleanArray}. */ writeSparseBooleanArray( @onNull Parcel p, int id, @Nullable SparseBooleanArray val, boolean writeNull)768 public static void writeSparseBooleanArray( 769 @NonNull Parcel p, int id, @Nullable SparseBooleanArray val, boolean writeNull) { 770 if (val == null) { 771 if (writeNull) { 772 writeHeader(p, id, 0); 773 } 774 return; 775 } 776 int start = beginVariableData(p, id); 777 p.writeSparseBooleanArray(val); 778 finishVariableData(p, start); 779 } 780 781 /** Writes a {@link Double} {@link SparseArray}. */ writeDoubleSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<Double> val, boolean writeNull)782 public static void writeDoubleSparseArray( 783 @NonNull Parcel p, int id, @Nullable SparseArray<Double> val, boolean writeNull) { 784 if (val == null) { 785 if (writeNull) { 786 writeHeader(p, id, 0); 787 } 788 return; 789 } 790 int start = beginVariableData(p, id); 791 final int size = val.size(); 792 p.writeInt(size); 793 for (int i = 0; i < size; i++) { 794 p.writeInt(val.keyAt(i)); 795 p.writeDouble(val.valueAt(i)); 796 } 797 finishVariableData(p, start); 798 } 799 800 /** Writes a {@link Float} {@link SparseArray}. */ writeFloatSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<Float> val, boolean writeNull)801 public static void writeFloatSparseArray( 802 @NonNull Parcel p, int id, @Nullable SparseArray<Float> val, boolean writeNull) { 803 if (val == null) { 804 if (writeNull) { 805 writeHeader(p, id, 0); 806 } 807 return; 808 } 809 int start = beginVariableData(p, id); 810 final int size = val.size(); 811 p.writeInt(size); 812 for (int i = 0; i < size; i++) { 813 p.writeInt(val.keyAt(i)); 814 p.writeFloat(val.valueAt(i)); 815 } 816 finishVariableData(p, start); 817 } 818 819 /** Writes a {@link SparseIntArray}. */ writeSparseIntArray( @onNull Parcel p, int id, @Nullable SparseIntArray val, boolean writeNull)820 public static void writeSparseIntArray( 821 @NonNull Parcel p, int id, @Nullable SparseIntArray val, boolean writeNull) { 822 if (val == null) { 823 if (writeNull) { 824 writeHeader(p, id, 0); 825 } 826 return; 827 } 828 int start = beginVariableData(p, id); 829 final int size = val.size(); 830 p.writeInt(size); 831 for (int i = 0; i < size; i++) { 832 p.writeInt(val.keyAt(i)); 833 p.writeInt(val.valueAt(i)); 834 } 835 finishVariableData(p, start); 836 } 837 838 /** Writes a {@link SparseLongArray}. */ writeSparseLongArray( @onNull Parcel p, int id, @Nullable SparseLongArray val, boolean writeNull)839 public static void writeSparseLongArray( 840 @NonNull Parcel p, int id, @Nullable SparseLongArray val, boolean writeNull) { 841 if (val == null) { 842 if (writeNull) { 843 writeHeader(p, id, 0); 844 } 845 return; 846 } 847 int start = beginVariableData(p, id); 848 final int size = val.size(); 849 p.writeInt(size); 850 for (int i = 0; i < size; i++) { 851 p.writeInt(val.keyAt(i)); 852 p.writeLong(val.valueAt(i)); 853 } 854 finishVariableData(p, start); 855 } 856 857 /** Writes a {@link String} {@link SparseArray}. */ writeStringSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<String> val, boolean writeNull)858 public static void writeStringSparseArray( 859 @NonNull Parcel p, int id, @Nullable SparseArray<String> val, boolean writeNull) { 860 if (val == null) { 861 if (writeNull) { 862 writeHeader(p, id, 0); 863 } 864 return; 865 } 866 int start = beginVariableData(p, id); 867 final int size = val.size(); 868 p.writeInt(size); 869 for (int i = 0; i < size; i++) { 870 p.writeInt(val.keyAt(i)); 871 p.writeString(val.valueAt(i)); 872 } 873 finishVariableData(p, start); 874 } 875 876 /** Writes a {@link Parcel} {@link SparseArray}. */ writeParcelSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<Parcel> val, boolean writeNull)877 public static void writeParcelSparseArray( 878 @NonNull Parcel p, int id, @Nullable SparseArray<Parcel> val, boolean writeNull) { 879 if (val == null) { 880 if (writeNull) { 881 writeHeader(p, id, 0); 882 } 883 return; 884 } 885 int start = beginVariableData(p, id); 886 final int size = val.size(); 887 p.writeInt(size); 888 for (int i = 0; i < size; i++) { 889 p.writeInt(val.keyAt(i)); 890 Parcel item = val.valueAt(i); 891 if (item != null) { 892 p.writeInt(item.dataSize()); 893 // custom part 894 p.appendFrom(item, 0, item.dataSize()); 895 } else { 896 p.writeInt(0); 897 } 898 } 899 finishVariableData(p, start); 900 } 901 902 /** Writes typed {@link SparseArray}. */ writeTypedSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<T> val, boolean writeNull)903 public static <T extends Parcelable> void writeTypedSparseArray( 904 @NonNull Parcel p, int id, @Nullable SparseArray<T> val, boolean writeNull) { 905 if (val == null) { 906 if (writeNull) { 907 writeHeader(p, id, 0); 908 } 909 return; 910 } 911 int start = beginVariableData(p, id); 912 // We follow the same approach as writeTypedList(). 913 final int size = val.size(); 914 p.writeInt(size); 915 for (int i = 0; i < size; i++) { 916 p.writeInt(val.keyAt(i)); 917 T item = val.valueAt(i); 918 if (item == null) { 919 p.writeInt(0); 920 } else { 921 writeTypedItemWithSize(p, item, 0); 922 } 923 } 924 finishVariableData(p, start); 925 } 926 927 /** Writes {@link IBinder} {@link SparseArray}. */ writeIBinderSparseArray( @onNull Parcel p, int id, @Nullable SparseArray<IBinder> val, boolean writeNull)928 public static void writeIBinderSparseArray( 929 @NonNull Parcel p, int id, @Nullable SparseArray<IBinder> val, boolean writeNull) { 930 if (val == null) { 931 if (writeNull) { 932 writeHeader(p, id, 0); 933 } 934 return; 935 } 936 int start = beginVariableData(p, id); 937 final int size = val.size(); 938 p.writeInt(size); 939 for (int i = 0; i < size; i++) { 940 p.writeInt(val.keyAt(i)); 941 p.writeStrongBinder(val.valueAt(i)); 942 } 943 finishVariableData(p, start); 944 } 945 946 /** Writes byte array {@link SparseArray}. */ writeByteArraySparseArray( @onNull Parcel p, int id, @Nullable SparseArray<byte[]> val, boolean writeNull)947 public static void writeByteArraySparseArray( 948 @NonNull Parcel p, int id, @Nullable SparseArray<byte[]> val, boolean writeNull) { 949 if (val == null) { 950 if (writeNull) { 951 writeHeader(p, id, 0); 952 } 953 return; 954 } 955 int start = beginVariableData(p, id); 956 final int size = val.size(); 957 p.writeInt(size); 958 for (int i = 0; i < size; i++) { 959 p.writeInt(val.keyAt(i)); 960 p.writeByteArray(val.valueAt(i)); 961 } 962 finishVariableData(p, start); 963 } 964 } 965