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