/* * 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 android.app.appsearch.safeparcel; import android.annotation.NonNull; import android.annotation.Nullable; 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 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. * *
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
*/
// Include the SafeParcel source code directly in AppSearch until it gets officially open-sourced.
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