Repo cloned

This commit is contained in:
Fr4nz D13trich 2025-12-29 13:18:34 +01:00
commit 496ae75f58
7988 changed files with 1451097 additions and 0 deletions

View file

@ -0,0 +1,7 @@
plugins {
id("signal-library")
}
android {
namespace = "com.google.android.gms.common.internal.safeparcel"
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal.safeparcel;
/**
* Implements {@link SafeParcelable} and implements some default methods defined by {@link
* android.os.Parcelable}.
*
* @hide
*/
public abstract class AbstractSafeParcelable implements SafeParcelable {
/** @hide */
@Override
public final int describeContents() {
return 0;
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal.safeparcel;
import android.os.Parcelable;
/**
* Interface for Parcelables that have the class name reflectively read as part of serialization.
* This happens when when put into an Intent or Bundle, or in some Parcel write methods.
*
* <p>This interface is needed because the errorprone checker has some limitations on detecting
* annotations (like {@code @KeepName}), where detecting inheritance is easier.
*
* @hide
*/
public interface ReflectedParcelable extends Parcelable {}

View file

@ -0,0 +1,930 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal.safeparcel;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* Functions to read in a safe parcel.
*
* @hide
*/
public class SafeParcelReader {
/** class to parse the exception. */
public static class ParseException extends RuntimeException {
public ParseException(@NonNull String message, @NonNull Parcel p) {
super(message + " Parcel: pos=" + p.dataPosition() + " size=" + p.dataSize());
}
}
private SafeParcelReader() {}
/** Reads the header. */
public static int readHeader(@NonNull Parcel p) {
return p.readInt();
}
/** Gets the id for the field. */
public static int getFieldId(int header) {
return header & 0x0000ffff;
}
/** Reads the size. */
public static int readSize(@NonNull Parcel p, int header) {
if ((header & 0xffff0000) != 0xffff0000) {
return (header >> 16) & 0x0000ffff;
} else {
return p.readInt();
}
}
/** Skips the unknown field. */
public static void skipUnknownField(@NonNull Parcel p, int header) {
int size = readSize(p, header);
p.setDataPosition(p.dataPosition() + size);
}
private static void readAndEnforceSize(@NonNull Parcel p, int header, int required) {
final int size = readSize(p, header);
if (size != required) {
throw new ParseException(
"Expected size "
+ required
+ " got "
+ size
+ " (0x"
+ Integer.toHexString(size)
+ ")",
p);
}
}
private static void enforceSize(@NonNull Parcel p, int header, int size, int required) {
if (size != required) {
throw new ParseException(
"Expected size "
+ required
+ " got "
+ size
+ " (0x"
+ Integer.toHexString(size)
+ ")",
p);
}
}
/** Returns the end position of the object in the parcel. */
public static int validateObjectHeader(@NonNull Parcel p) {
final int header = readHeader(p);
final int size = readSize(p, header);
final int start = p.dataPosition();
if (getFieldId(header) != SafeParcelWriter.OBJECT_HEADER) {
throw new ParseException(
"Expected object header. Got 0x" + Integer.toHexString(header), p);
}
final int end = start + size;
if (end < start || end > p.dataSize()) {
throw new ParseException("Size read is invalid start=" + start + " end=" + end, p);
}
return end;
}
/** Reads a boolean. */
public static boolean readBoolean(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return p.readInt() != 0;
}
/** Reads a {@link Boolean} object. */
@Nullable
public static Boolean readBooleanObject(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
if (size == 0) {
return null;
} else {
enforceSize(p, header, size, 4);
return p.readInt() != 0;
}
}
/** Reads a byte. */
public static byte readByte(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return (byte) p.readInt();
}
/** Reads a char. */
public static char readChar(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return (char) p.readInt();
}
/** Reads a short. */
public static short readShort(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return (short) p.readInt();
}
/** Reads an int. */
public static int readInt(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return p.readInt();
}
/** Reads an {@link Integer} object. */
@Nullable
public static Integer readIntegerObject(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
if (size == 0) {
return null;
} else {
enforceSize(p, header, size, 4);
return p.readInt();
}
}
/** Reads a long. */
public static long readLong(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 8);
return p.readLong();
}
/** Reads a {@link Long} object. */
@Nullable
public static Long readLongObject(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
if (size == 0) {
return null;
} else {
enforceSize(p, header, size, 8);
return p.readLong();
}
}
/** Creates a {@link BigInteger}. */
@Nullable
public static BigInteger createBigInteger(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final byte[] val = p.createByteArray();
p.setDataPosition(pos + size);
return new BigInteger(val);
}
/** Reads a float. */
public static float readFloat(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 4);
return p.readFloat();
}
/** Reads a {@link Float}. */
@Nullable
public static Float readFloatObject(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
if (size == 0) {
return null;
} else {
enforceSize(p, header, size, 4);
return p.readFloat();
}
}
/** Reads a double. */
public static double readDouble(@NonNull Parcel p, int header) {
readAndEnforceSize(p, header, 8);
return p.readDouble();
}
/** Reads a {@link Double}. */
@Nullable
public static Double readDoubleObject(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
if (size == 0) {
return null;
} else {
enforceSize(p, header, size, 8);
return p.readDouble();
}
}
/** Creates a {@link BigDecimal}. */
@Nullable
public static BigDecimal createBigDecimal(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final byte[] unscaledValue = p.createByteArray();
final int scale = p.readInt();
p.setDataPosition(pos + size);
return new BigDecimal(new BigInteger(unscaledValue), scale);
}
/** Creates a {@link String}. */
@Nullable
public static String createString(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final String result = p.readString();
p.setDataPosition(pos + size);
return result;
}
/** Reads an {@link IBinder}. */
@Nullable
public static IBinder readIBinder(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final IBinder result = p.readStrongBinder();
p.setDataPosition(pos + size);
return result;
}
/** Reads a {@link PendingIntent}. */
@Nullable
public static PendingIntent readPendingIntent(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final PendingIntent result = PendingIntent.readPendingIntentOrNullFromParcel(p);
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Parcelable}. */
@Nullable
public static <T extends Parcelable> T createParcelable(
@NonNull Parcel p, int header, @NonNull Parcelable.Creator<T> creator) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final T result = creator.createFromParcel(p);
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Bundle}. */
@Nullable
public static Bundle createBundle(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final Bundle result = p.readBundle();
p.setDataPosition(pos + size);
return result;
}
/** Creates a byte array. */
@Nullable
public static byte[] createByteArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final byte[] result = p.createByteArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a byte array array. */
@Nullable
public static byte[][] createByteArrayArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int length = p.readInt();
final byte[][] result = new byte[length][];
for (int i = 0; i < length; i++) {
result[i] = p.createByteArray();
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a boolean array array. */
@Nullable
public static boolean[] createBooleanArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final boolean[] result = p.createBooleanArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a char array. */
@Nullable
public static char[] createCharArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final char[] result = p.createCharArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates an int array. */
@Nullable
public static int[] createIntArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int[] result = p.createIntArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a long array. */
@Nullable
public static long[] createLongArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final long[] result = p.createLongArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link BigInteger} array. */
@Nullable
public static BigInteger[] createBigIntegerArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int length = p.readInt();
final BigInteger[] result = new BigInteger[length];
for (int i = 0; i < length; i++) {
result[i] = new BigInteger(p.createByteArray());
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a float array. */
@Nullable
public static float[] createFloatArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final float[] result = p.createFloatArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a double array. */
@Nullable
public static double[] createDoubleArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final double[] result = p.createDoubleArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link BigDecimal} array. */
@Nullable
public static BigDecimal[] createBigDecimalArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int length = p.readInt();
final BigDecimal[] result = new BigDecimal[length];
for (int i = 0; i < length; i++) {
byte[] unscaledValue = p.createByteArray();
int scale = p.readInt();
result[i] = new BigDecimal(new BigInteger(unscaledValue), scale);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link String} array. */
@Nullable
public static String[] createStringArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final String[] result = p.createStringArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link IBinder} array. */
@Nullable
public static IBinder[] createIBinderArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final IBinder[] result = p.createBinderArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Boolean} list. */
@Nullable
public static ArrayList<Boolean> createBooleanList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<Boolean> result = new ArrayList<Boolean>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
result.add(p.readInt() != 0 ? true : false);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Integer} list. */
@Nullable
public static ArrayList<Integer> createIntegerList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<Integer> result = new ArrayList<Integer>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
result.add(p.readInt());
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link SparseBooleanArray}. */
@Nullable
public static SparseBooleanArray createSparseBooleanArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
SparseBooleanArray result = p.readSparseBooleanArray();
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link SparseIntArray}. */
@Nullable
public static SparseIntArray createSparseIntArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final SparseIntArray result = new SparseIntArray();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
int key = p.readInt();
int value = p.readInt();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Float} {@link SparseArray}. */
@Nullable
public static SparseArray<Float> createFloatSparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final SparseArray<Float> result = new SparseArray<Float>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
int key = p.readInt();
float value = p.readFloat();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Double} {@link SparseArray}. */
@Nullable
public static SparseArray<Double> createDoubleSparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final SparseArray<Double> result = new SparseArray<Double>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
int key = p.readInt();
double value = p.readDouble();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link SparseLongArray}. */
@Nullable
public static SparseLongArray createSparseLongArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final SparseLongArray result = new SparseLongArray();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
int key = p.readInt();
long value = p.readLong();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link String} {@link SparseArray}. */
@Nullable
public static SparseArray<String> createStringSparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final SparseArray<String> result = new SparseArray<String>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
int key = p.readInt();
String value = p.readString();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates a {@link Parcel} {@link SparseArray}. */
@Nullable
public static SparseArray<Parcel> createParcelSparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int count = p.readInt();
final SparseArray<Parcel> result = new SparseArray<Parcel>();
for (int i = 0; i < count; i++) {
int key = p.readInt();
// read in the flag of whether this element is null
int parcelSize = p.readInt();
if (parcelSize != 0) {
// non-null
int currentDataPosition = p.dataPosition();
Parcel item = Parcel.obtain();
item.appendFrom(p, currentDataPosition, parcelSize);
result.append(key, item);
// move p's data position
p.setDataPosition(currentDataPosition + parcelSize);
} else {
// is null
result.append(key, null);
}
}
p.setDataPosition(pos + size);
return result;
}
/** Creates typed {@link SparseArray}. */
@Nullable
public static <T> SparseArray<T> createTypedSparseArray(
@NonNull Parcel p, int header, @NonNull Parcelable.Creator<T> c) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int count = p.readInt();
final SparseArray<T> result = new SparseArray<>();
for (int i = 0; i < count; i++) {
int key = p.readInt();
T value;
if (p.readInt() != 0) {
value = c.createFromParcel(p);
} else {
value = null;
}
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link IBinder} {@link SparseArray}. */
@Nullable
public static SparseArray<IBinder> createIBinderSparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int count = p.readInt();
final SparseArray<IBinder> result = new SparseArray<>(count);
for (int i = 0; i < count; i++) {
int key = p.readInt();
IBinder value = p.readStrongBinder();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates byte array {@link SparseArray}. */
@Nullable
public static SparseArray<byte[]> createByteArraySparseArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int count = p.readInt();
final SparseArray<byte[]> result = new SparseArray<byte[]>(count);
for (int i = 0; i < count; i++) {
int key = p.readInt();
byte[] value = p.createByteArray();
result.append(key, value);
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Long} {@link ArrayList}. */
@Nullable
public static ArrayList<Long> createLongList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<Long> result = new ArrayList<Long>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
result.add(p.readLong());
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Float} {@link ArrayList}. */
@Nullable
public static ArrayList<Float> createFloatList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<Float> result = new ArrayList<Float>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
result.add(p.readFloat());
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Double} {@link ArrayList}. */
@Nullable
public static ArrayList<Double> createDoubleList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<Double> result = new ArrayList<Double>();
final int count = p.readInt();
for (int i = 0; i < count; i++) {
result.add(p.readDouble());
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link String} {@link ArrayList}. */
@Nullable
public static ArrayList<String> createStringList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<String> result = p.createStringArrayList();
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link IBinder} {@link ArrayList}. */
@Nullable
public static ArrayList<IBinder> createIBinderList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<IBinder> result = p.createBinderArrayList();
p.setDataPosition(pos + size);
return result;
}
/** Creates typed array. */
@Nullable
public static <T> T[] createTypedArray(
@NonNull Parcel p, int header, @NonNull Parcelable.Creator<T> c) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final T[] result = p.createTypedArray(c);
p.setDataPosition(pos + size);
return result;
}
/** Creates typed {@link ArrayList}. */
@Nullable
public static <T> ArrayList<T> createTypedList(
@NonNull Parcel p, int header, @NonNull Parcelable.Creator<T> c) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final ArrayList<T> result = p.createTypedArrayList(c);
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Parcel}. */
@Nullable
public static Parcel createParcel(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final Parcel result = Parcel.obtain();
result.appendFrom(p, pos, size);
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Parcel} array. */
@Nullable
public static Parcel[] createParcelArray(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int length = p.readInt();
final Parcel[] result = new Parcel[length];
for (int i = 0; i < length; i++) {
int parcelSize = p.readInt();
if (parcelSize != 0) {
int currentDataPosition = p.dataPosition();
Parcel item = Parcel.obtain();
item.appendFrom(p, currentDataPosition, parcelSize);
result[i] = item;
// move p's data position
p.setDataPosition(currentDataPosition + parcelSize);
} else {
result[i] = null;
}
}
p.setDataPosition(pos + size);
return result;
}
/** Creates {@link Parcel} {@link ArrayList}. */
@Nullable
public static ArrayList<Parcel> createParcelList(@NonNull Parcel p, int header) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return null;
}
final int length = p.readInt();
final ArrayList<Parcel> result = new ArrayList<Parcel>();
for (int i = 0; i < length; i++) {
// read in the flag of whether this element is null
int parcelSize = p.readInt();
if (parcelSize != 0) {
// non-null
int currentDataPosition = p.dataPosition();
Parcel item = Parcel.obtain();
item.appendFrom(p, currentDataPosition, parcelSize);
result.add(item);
// move p's data position
p.setDataPosition(currentDataPosition + parcelSize);
} else {
// is null
result.add(null);
}
}
p.setDataPosition(pos + size);
return result;
}
/** Reads the list. */
public static void readList(
@NonNull Parcel p,
int header,
@SuppressWarnings("rawtypes") @NonNull List list,
@Nullable ClassLoader loader) {
final int size = readSize(p, header);
final int pos = p.dataPosition();
if (size == 0) {
return;
}
p.readList(list, loader);
p.setDataPosition(pos + size);
}
/** Ensures at end. */
public static void ensureAtEnd(@NonNull Parcel parcel, int end) {
if (parcel.dataPosition() != end) {
throw new ParseException("Overread allowed size end=" + end, parcel);
}
}
}

View file

@ -0,0 +1,964 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal.safeparcel;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
/**
* Functions to write a safe parcel. A safe parcel consists of a sequence of header/payload bytes.
*
* <p>The header is 16 bits of size and 16 bits of field id. If the size in the header is 0xffff,
* the next 4 bytes are the size field instead.
*
* @hide
*/
public class SafeParcelWriter {
static final int OBJECT_HEADER = 0x00004f45;
private SafeParcelWriter() {}
private static void writeHeader(Parcel p, int id, int size) {
if (size >= 0x0000ffff) {
p.writeInt(0xffff0000 | id);
p.writeInt(size);
} else {
p.writeInt((size << 16) | id);
}
}
/** Returns the cookie that should be passed to endVariableData. */
private static int beginVariableData(Parcel p, int id) {
// Since we don't know the size yet, assume it might be big and always use the
// size overflow.
p.writeInt(0xffff0000 | id);
p.writeInt(0);
return p.dataPosition();
}
/**
* @param start The result of the paired beginVariableData.
*/
private static void finishVariableData(Parcel p, int start) {
int end = p.dataPosition();
int size = end - start;
// The size is one int before start.
p.setDataPosition(start - 4);
p.writeInt(size);
p.setDataPosition(end);
}
/** Begins the objects header. */
public static int beginObjectHeader(@NonNull Parcel p) {
return beginVariableData(p, OBJECT_HEADER);
}
/** Finishes the objects header. */
public static void finishObjectHeader(@NonNull Parcel p, int start) {
finishVariableData(p, start);
}
/** Writes a boolean. */
public static void writeBoolean(@NonNull Parcel p, int id, boolean val) {
writeHeader(p, id, 4);
p.writeInt(val ? 1 : 0);
}
/** Writes a {@link Boolean} object. */
public static void writeBooleanObject(
@NonNull Parcel p, int id, @Nullable Boolean val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
writeHeader(p, id, 4);
p.writeInt(val ? 1 : 0);
}
/** Writes a byte. */
public static void writeByte(@NonNull Parcel p, int id, byte val) {
writeHeader(p, id, 4);
p.writeInt(val);
}
/** Writes a char. */
public static void writeChar(@NonNull Parcel p, int id, char val) {
writeHeader(p, id, 4);
p.writeInt(val);
}
/** Writes a short. */
public static void writeShort(@NonNull Parcel p, int id, short val) {
writeHeader(p, id, 4);
p.writeInt(val);
}
/** Writes an int. */
public static void writeInt(@NonNull Parcel p, int id, int val) {
writeHeader(p, id, 4);
p.writeInt(val);
}
/** Writes an {@link Integer}. */
public static void writeIntegerObject(
@NonNull Parcel p, int id, @Nullable Integer val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
writeHeader(p, id, 4);
p.writeInt(val);
}
/** Writes a long. */
public static void writeLong(@NonNull Parcel p, int id, long val) {
writeHeader(p, id, 8);
p.writeLong(val);
}
/** Writes a {@link Long}. */
public static void writeLongObject(
@NonNull Parcel p, int id, @Nullable Long val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
writeHeader(p, id, 8);
p.writeLong(val);
}
/** Writes a {@link BigInteger}. */
public static void writeBigInteger(
@NonNull Parcel p, int id, @Nullable BigInteger val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeByteArray(val.toByteArray());
finishVariableData(p, start);
}
/** Writes a float. */
public static void writeFloat(@NonNull Parcel p, int id, float val) {
writeHeader(p, id, 4);
p.writeFloat(val);
}
/** Writes a {@link Float}. */
public static void writeFloatObject(
@NonNull Parcel p, int id, @Nullable Float val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
writeHeader(p, id, 4);
p.writeFloat(val);
}
/** Writes a double. */
public static void writeDouble(@NonNull Parcel p, int id, double val) {
writeHeader(p, id, 8);
p.writeDouble(val);
}
/** Writes a {@link Double} object. */
public static void writeDoubleObject(
@NonNull Parcel p, int id, @Nullable Double val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
writeHeader(p, id, 8);
p.writeDouble(val);
}
/** Writes a {@link BigDecimal}. */
public static void writeBigDecimal(
@NonNull Parcel p, int id, @Nullable BigDecimal val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeByteArray(val.unscaledValue().toByteArray());
p.writeInt(val.scale());
finishVariableData(p, start);
}
/** Writes a {@link String}. */
public static void writeString(
@NonNull Parcel p, int id, @Nullable String val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeString(val);
finishVariableData(p, start);
}
/** Writes a {@link IBinder}. */
public static void writeIBinder(
@NonNull Parcel p, int id, @Nullable IBinder val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
// The size of the flat_binder_object in Parcel.cpp is not actually variable
// but is not part of the CDD, so treat it as variable. It almost certainly
// won't change between processes on a given device.
int start = beginVariableData(p, id);
p.writeStrongBinder(val);
finishVariableData(p, start);
}
/** Writes a {@link Parcelable}. */
public static void writeParcelable(
@NonNull Parcel p,
int id,
@Nullable Parcelable val,
int parcelableFlags,
boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
val.writeToParcel(p, parcelableFlags);
finishVariableData(p, start);
}
/** Writes a {@link Bundle}. */
public static void writeBundle(
@NonNull Parcel p, int id, @Nullable Bundle val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeBundle(val);
finishVariableData(p, start);
}
/** Writes a byte array. */
public static void writeByteArray(
@NonNull Parcel p, int id, @Nullable byte[] buf, boolean writeNull) {
if (buf == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeByteArray(buf);
finishVariableData(p, start);
}
/** Writes a byte array array. */
public static void writeByteArrayArray(
@NonNull Parcel p, int id, @Nullable byte[][] buf, boolean writeNull) {
if (buf == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int length = buf.length;
p.writeInt(length);
for (int i = 0; i < length; i++) {
p.writeByteArray(buf[i]);
}
finishVariableData(p, start);
}
/** Writes a boolean array. */
public static void writeBooleanArray(
@NonNull Parcel p, int id, @Nullable boolean[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeBooleanArray(val);
finishVariableData(p, start);
}
/** Writes a char array. */
public static void writeCharArray(
@NonNull Parcel p, int id, @Nullable char[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeCharArray(val);
finishVariableData(p, start);
}
/** Writes an int array. */
public static void writeIntArray(
@NonNull Parcel p, int id, @Nullable int[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeIntArray(val);
finishVariableData(p, start);
}
/** Writes a long array. */
public static void writeLongArray(
@NonNull Parcel p, int id, @Nullable long[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeLongArray(val);
finishVariableData(p, start);
}
/** Writes a {@link BigInteger} array. */
public static void writeBigIntegerArray(
@NonNull Parcel p, int id, @Nullable BigInteger[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int length = val.length;
p.writeInt(length);
for (int i = 0; i < length; i++) {
p.writeByteArray(val[i].toByteArray());
}
finishVariableData(p, start);
}
/** Writes a float array. */
public static void writeFloatArray(
@NonNull Parcel p, int id, @Nullable float[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeFloatArray(val);
finishVariableData(p, start);
}
/** Writes a double array. */
public static void writeDoubleArray(
@NonNull Parcel p, int id, @Nullable double[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeDoubleArray(val);
finishVariableData(p, start);
}
/** Writes a {@link BigDecimal} array. */
public static void writeBigDecimalArray(
@NonNull Parcel p, int id, @Nullable BigDecimal[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int length = val.length;
p.writeInt(length);
for (int i = 0; i < length; i++) {
p.writeByteArray(val[i].unscaledValue().toByteArray());
p.writeInt(val[i].scale());
}
finishVariableData(p, start);
}
/** Writes a {@link String} array. */
public static void writeStringArray(
@NonNull Parcel p, int id, @Nullable String[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeStringArray(val);
finishVariableData(p, start);
}
/** Writes a {@link IBinder} array. */
public static void writeIBinderArray(
@NonNull Parcel p, int id, @Nullable IBinder[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeBinderArray(val);
finishVariableData(p, start);
}
/** Writes a boolean list. */
public static void writeBooleanList(
@NonNull Parcel p, int id, @Nullable List<Boolean> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.get(i) ? 1 : 0);
}
finishVariableData(p, start);
}
/** Writes an {@link Integer} list. */
public static void writeIntegerList(
@NonNull Parcel p, int id, @Nullable List<Integer> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.get(i));
}
finishVariableData(p, start);
}
/** Writes a {@link Long} list. */
public static void writeLongList(
@NonNull Parcel p, int id, @Nullable List<Long> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeLong(val.get(i));
}
finishVariableData(p, start);
}
/** Writes a {@link Float} list. */
public static void writeFloatList(
@NonNull Parcel p, int id, @Nullable List<Float> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeFloat(val.get(i));
}
finishVariableData(p, start);
}
/** Writes a {@link Double} list. */
public static void writeDoubleList(
@NonNull Parcel p, int id, @Nullable List<Double> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeDouble(val.get(i));
}
finishVariableData(p, start);
}
/** Writes a {@link String} list. */
public static void writeStringList(
@NonNull Parcel p, int id, @Nullable List<String> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeStringList(val);
finishVariableData(p, start);
}
/** Writes a {@link IBinder} list. */
public static void writeIBinderList(
@NonNull Parcel p, int id, @Nullable List<IBinder> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeBinderList(val);
finishVariableData(p, start);
}
/** Writes a typed array. */
public static <T extends Parcelable> void writeTypedArray(
@NonNull Parcel p, int id, @Nullable T[] val, int parcelableFlags, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
// We need to customize the built-in Parcel.writeTypedArray() because we need to write
// the sizes for each individual SafeParcelable objects since they can vary in size due
// to supporting missing fields.
final int length = val.length;
p.writeInt(length);
for (int i = 0; i < length; i++) {
T item = val[i];
if (item == null) {
p.writeInt(0);
} else {
writeTypedItemWithSize(p, item, parcelableFlags);
}
}
finishVariableData(p, start);
}
/** Writes a typed list. */
public static <T extends Parcelable> void writeTypedList(
@NonNull Parcel p, int id, @Nullable List<T> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
// We need to customize the built-in Parcel.writeTypedList() because we need to write
// the sizes for each individual SafeParcelable objects since they can vary in size due
// supporting missing fields.
final int length = val.size();
p.writeInt(length);
for (int i = 0; i < length; i++) {
T item = val.get(i);
if (item == null) {
p.writeInt(0);
} else {
writeTypedItemWithSize(p, item, 0);
}
}
finishVariableData(p, start);
}
/** Writes a typed item with size. */
private static <T extends Parcelable> void writeTypedItemWithSize(
Parcel p, T item, int parcelableFlags) {
// Just write a 1 as a placeholder since we don't know the exact size of item
// yet, and save the data position in Parcel p.
final int itemSizeDataPosition = p.dataPosition();
p.writeInt(1);
final int itemStartPosition = p.dataPosition();
item.writeToParcel(p, parcelableFlags);
final int currentDataPosition = p.dataPosition();
// go back and write the length in bytes
p.setDataPosition(itemSizeDataPosition);
p.writeInt(currentDataPosition - itemStartPosition);
// set the parcel data position to where it was before
p.setDataPosition(currentDataPosition);
}
/** Writes a parcel. */
public static void writeParcel(
@NonNull Parcel p, int id, @Nullable Parcel val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.appendFrom(val, 0, val.dataSize());
finishVariableData(p, start);
}
/**
* This is made to be compatible with writeTypedArray. See implementation of
* Parcel.writeTypedArray(T[] val, parcelableFlags);
*/
public static void writeParcelArray(
@NonNull Parcel p, int id, @Nullable Parcel[] val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int length = val.length;
p.writeInt(length);
for (int i = 0; i < length; i++) {
Parcel item = val[i];
if (item != null) {
p.writeInt(item.dataSize());
// custom part
p.appendFrom(item, 0, item.dataSize());
} else {
p.writeInt(0);
}
}
finishVariableData(p, start);
}
/**
* This is made to be compatible with writeTypedList. See implementation of
* Parce.writeTypedList(List<T> val).
*/
public static void writeParcelList(
@NonNull Parcel p, int id, @Nullable List<Parcel> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
Parcel item = val.get(i);
if (item != null) {
p.writeInt(item.dataSize());
// custom part
p.appendFrom(item, 0, item.dataSize());
} else {
p.writeInt(0);
}
}
finishVariableData(p, start);
}
/** Writes a {@link PendingIntent}. */
public static void writePendingIntent(
@NonNull Parcel p, int id, @Nullable PendingIntent val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
PendingIntent.writePendingIntentOrNullToParcel(val, p);
finishVariableData(p, start);
}
/** Writes a list. */
public static void writeList(
@NonNull Parcel p,
int id,
@SuppressWarnings("rawtypes") @Nullable List list,
boolean writeNull) {
if (list == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeList(list);
finishVariableData(p, start);
}
/** Writes a {@link SparseBooleanArray}. */
public static void writeSparseBooleanArray(
@NonNull Parcel p, int id, @Nullable SparseBooleanArray val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
p.writeSparseBooleanArray(val);
finishVariableData(p, start);
}
/** Writes a {@link Double} {@link SparseArray}. */
public static void writeDoubleSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<Double> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeDouble(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes a {@link Float} {@link SparseArray}. */
public static void writeFloatSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<Float> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeFloat(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes a {@link SparseIntArray}. */
public static void writeSparseIntArray(
@NonNull Parcel p, int id, @Nullable SparseIntArray val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeInt(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes a {@link SparseLongArray}. */
public static void writeSparseLongArray(
@NonNull Parcel p, int id, @Nullable SparseLongArray val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeLong(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes a {@link String} {@link SparseArray}. */
public static void writeStringSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<String> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeString(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes a {@link Parcel} {@link SparseArray}. */
public static void writeParcelSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<Parcel> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
Parcel item = val.valueAt(i);
if (item != null) {
p.writeInt(item.dataSize());
// custom part
p.appendFrom(item, 0, item.dataSize());
} else {
p.writeInt(0);
}
}
finishVariableData(p, start);
}
/** Writes typed {@link SparseArray}. */
public static <T extends Parcelable> void writeTypedSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<T> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
// We follow the same approach as writeTypedList().
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
T item = val.valueAt(i);
if (item == null) {
p.writeInt(0);
} else {
writeTypedItemWithSize(p, item, 0);
}
}
finishVariableData(p, start);
}
/** Writes {@link IBinder} {@link SparseArray}. */
public static void writeIBinderSparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<IBinder> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeStrongBinder(val.valueAt(i));
}
finishVariableData(p, start);
}
/** Writes byte array {@link SparseArray}. */
public static void writeByteArraySparseArray(
@NonNull Parcel p, int id, @Nullable SparseArray<byte[]> val, boolean writeNull) {
if (val == null) {
if (writeNull) {
writeHeader(p, id, 0);
}
return;
}
int start = beginVariableData(p, id);
final int size = val.size();
p.writeInt(size);
for (int i = 0; i < size; i++) {
p.writeInt(val.keyAt(i));
p.writeByteArray(val.valueAt(i));
}
finishVariableData(p, start);
}
}

View file

@ -0,0 +1,316 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal.safeparcel;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A SafeParcelable is a special {@link Parcelable} interface that marshalls its fields in a
* protobuf-like manner into a {@link Parcel}. The marshalling encodes a unique id for each field
* along with the size in bytes of the field. By doing this, older versions of a SafeParcelable can
* skip over unknown fields, which enables backwards compatibility. Because SafeParcelable extends a
* Parcelable, it is NOT safe for persistence.
*
* <p>To prevent the need to manually write code to marshall fields like in a Parcelable, a
* SafeParcelable implementing class is annotated with several annotations so that a generated
* "creator" class has the metadata it needs to automatically generate the boiler plate marshalling
* and unmarshalling code.
*
* <p>The main annotations are the following:
*
* <ul>
* <li>{@link Class} - This annotates the SafeParcelable implementing class and indicates the name
* of the generated "creator" class that has the boiler plate marshalling/unmarshalling code.
* You can also specify whether to call a method named validateContents() after a new instance
* of this class is constructed.
* <li>{@link VersionField} - This annotation may be on one field in the SafeParcelable to
* indicate the version number for this SafeParcelable. This is purely here for style reasons
* in case it is necessary for code to be dependent on the version of the SafeParcelable. This
* member field must be final.
* <li>{@link Field} - This annotates fields that will be marshalled and unmarshalled by the
* generated "creator" class. You must provide an integer "id" for each field. To ensure
* backwards compatibility, these field id's should never be changed. It is okay to omit field
* id's in future versions, but do not reuse old id's. Member fields annotated with {@link
* Field} may be any visibility (private, protected, etc.) and may also be final. You will
* have to specify the "getter" if member fields are not at least package visible. See details
* in {@link Field}.
* <li>{@link Constructor} - You must annotate one constructor with this annotation, which
* indicates the constructor that the "creator" class will use to construct a new instance of
* this class. Each parameter to this constructor must be annotated with the {@link Param} or
* {@link RemovedParam} annotation indicating the field id that the parameter corresponds to.
* Every {@link Param} must correspond to a {@link Field} or {@link VersionField}, and every
* {@link RemovedParam} must correspond to a {@link Reserved} field. Note that this
* constructor must have at least package visibility because the generated "creator" class
* must be able to use this constructor. (The "creator" class is generated in the same package
* as the SafeParcelable class.).
* <li>{@link Indicator} - This is an annotation on a field that keeps track of which fields are
* actually present in a Parcel that represents the marshalled version of a SafeParcelable.
* This is used in the GMS Core Apiary model class code generation.
* </ul>
*
* <p>Because a SafeParcelable extends Parcelable, you must have a public static final member named
* CREATOR and override writeToParcel() and describeContents(). Here's a typical example.
*
* <pre>
* &#64;Class(creator="MySafeParcelableCreator", validate=true)
* public class MySafeParcelable implements SafeParcelable {
* public static final Parcelable.Creator&#60;MySafeParcelable&#62; CREATOR =
* new MySafeParcelableCreator();
*
* &#64;Field(id=1)
* public final String myString;
*
* &#64;Field(id=2, getter="getInteger")
* private final int myInteger;
*
* &#64;Constructor
* MySafeParcelable(
* &#64;Param(id=1) String string,
* &#64;Param(id=2) int integer) {
* myString = string;
* myInteger = integer;
* )
*
* // Example public constructor (not used by MySafeParcelableCreator)
* public MySafeParcelable(String string, int integer) {
* myString = string;
* myInteger = integer;
* }
*
* // This is only needed if validate=true in &#64;Class annotation.
* public void validateContents() {
* // Add validation here.
* }
*
* // This getter is needed because myInteger is private, and the generated creator class
* // MySafeParcelableCreator can't access private member fields.
* int getInteger() {
* return myInteger;
* }
*
* // This is necessary because SafeParcelable extends Parcelable.
* // {@link AbstractSafeParcelable} implements this for you.
* &#64;Override
* public int describeContents() {
* return MySafeParcelableCreator.CONTENT_DESCRIPTION;
* }
*
* // This is necessary because SafeParcelable extends Parcelable.
* &#64;Override
* public void writeToParcel(Parcel out, int flags) {
* // This invokes the generated MySafeParcelableCreator class's marshalling to a Parcel.
* // In the event you need custom logic when writing to a Parcel, that logic can be
* // inserted here.
* MySafeParcelableCreator.writeToParcel(this, out, flags);
* }
* }
* </pre>
*
* @hide
*/
public interface SafeParcelable extends Parcelable {
/** @hide */
// Note: the field name and value are accessed using reflection for backwards compatibility, and
// must not be changed.
String NULL = "SAFE_PARCELABLE_NULL_STRING";
/**
* This annotates your class and specifies the name of the generated "creator" class for
* marshalling/unmarshalling a SafeParcelable to/from a {@link Parcel}. The "creator" class is
* generated in the same package as the SafeParcelable class. You can also set "validate" to
* true, which will cause the "creator" to invoke the method validateContents() on your class
* after constructing an instance.
*/
@SuppressWarnings("JavaLangClash")
@interface Class {
/**
* Simple name of the generated "creator" class generated in the same package as the
* SafeParceable.
*/
String creator();
/** Whether the generated "creator" class is final. */
boolean creatorIsFinal() default true;
/**
* When set to true, invokes the validateContents() method in this SafeParcelable object
* after constructing a new instance.
*/
boolean validate() default false;
/**
* When set to true, it will not write type default values to the Parcel.
*
* <p>boolean: false byte/char/short/int/long: 0 float: 0.0f double: 0.0 Objects/arrays:
* null
*
* <p>Cannot be used with Field(defaultValue)
*/
boolean doNotParcelTypeDefaultValues() default false;
}
/** Use this annotation on members that you wish to be marshalled in the SafeParcelable. */
@interface Field {
/**
* Valid values for id are between 1 and 65535. This field id is marshalled into a Parcel .
* To maintain backwards compatibility, never reuse old id's. It is okay to no longer use
* old id's and add new ones in subsequent versions of a SafeParcelable.
*/
int id();
/**
* This specifies the name of the getter method for retrieving the value of this field. This
* must be specified for fields that do not have at least package visibility because the
* "creator" class will be unable to access the value when attempting to marshall this
* field. The getter method should take no parameters and return the type of this field
* (unless overridden by the "type" attribute below).
*/
String getter() default NULL;
/**
* For advanced uses, this specifies the type for the field when marshalling and
* unmarshalling by the "creator" class to be something different than the declared type of
* the member variable. This is useful if you want to incorporate an object that is not
* SafeParcelable (or a system Parcelable object). Be sure to enter the fully qualified name
* for the class (i.e., android.os.Bundle and not Bundle). For example,
*
* <pre>
* &#64;Class(creator="MyAdvancedCreator")
* public class MyAdvancedSafeParcelable implements SafeParcelable {
* public static final Parcelable.Creator&#60;MyAdvancedSafeParcelable&#62; CREATOR =
* new MyAdvancedCreator();
*
* &#64;Field(id=1, getter="getObjectAsBundle", type="android.os.Bundle")
* private final MyCustomObject myObject;
*
* &#64;Constructor
* MyAdvancedSafeParcelable(
* &#64;Param(id=1) Bundle objectAsBundle) {
* myObject = myConvertFromBundleToObject(objectAsBundle);
* }
*
* Bundle getObjectAsBundle() {
* // The code here can convert your custom object to one that can be parcelled.
* return myConvertFromObjectToBundle(myObject);
* }
*
* ...
* }
* </pre>
*/
String type() default NULL;
/**
* This can be used to specify the default value for primitive types (e.g., boolean, int,
* long), primitive type object wrappers (e.g., Boolean, Integer, Long) and String in the
* case a value for a field was not explicitly set in the marshalled Parcel. This performs
* compile-time checks for the type of the field and inserts the appropriate quotes or
* double quotes around strings and chars or removes them completely for booleans and
* numbers. To insert a generic string for initializing field, use {@link
* #defaultValueUnchecked()}. You can specify at most one of {@link #defaultValue()} or
* {@link #defaultValueUnchecked()}. For example,
*
* <pre>
* &#64;Field(id=2, defaultValue="true")
* boolean myBoolean;
*
* &#64;Field(id=3, defaultValue="13")
* Integer myInteger;
*
* &#64;Field(id=4, defaultValue="foo")
* String myString;
* </pre>
*/
String defaultValue() default NULL;
/**
* This can be used to specify the default value for any object and the string value is
* literally added to the generated creator class code unchecked. You can specify at most
* one of {@link #defaultValue()} or {@link #defaultValueUnchecked()}. You must fully
* qualify any classes you reference within the string. For example,
*
* <pre>
* &#64;Field(id=2, defaultValueUnchecked="new android.os.Bundle()")
* Bundle myBundle;
* </pre>
*/
String defaultValueUnchecked() default NULL;
}
/**
* There may be exactly one member annotated with VersionField, which represents the version of
* this safe parcelable. The attributes are the same as those of {@link Field}. Note you can use
* any type you want for your version field, although most people use int's.
*/
@interface VersionField {
int id();
String getter() default NULL;
String type() default NULL;
}
/**
* Use this to indicate the member field that holds whether a field was set or not. The member
* field type currently supported is a HashSet&#60;Integer&#62; which is the set of safe
* parcelable field id's that have been explicitly set.
*
* <p>This annotation should also be used to annotate one of the parameters to the constructor
* annotated with &#64;Constructor. Note that this annotation should either be present on
* exactly one member field and one constructor parameter or left out completely.
*/
@interface Indicator {
String getter() default NULL;
}
/**
* Use this to indicate the constructor that the creator should use. The constructor annotated
* with this must be package or public visibility, so that the generated "creator" class can
* invoke this.
*/
@interface Constructor {}
/**
* Use this on each parameter passed in to the Constructor to indicate to which field id each
* formal parameter corresponds.
*/
@interface Param {
int id();
}
/**
* Use this on a parameter passed in to the Constructor to indicate that a removed field should
* be read on construction. If the field is not present when read, the default value will be
* used instead.
*/
@interface RemovedParam {
int id();
String defaultValue() default NULL;
String defaultValueUnchecked() default NULL;
}
/**
* Use this to mark tombstones for removed {@link Field Fields} or {@link VersionField
* VersionFields}.
*/
@interface Reserved {
int[] value();
}
}