248 lines
8.5 KiB
Java
248 lines
8.5 KiB
Java
/*
|
|
* Copyright (C) 2020 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.os;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SystemApi;
|
|
import android.util.MathUtils;
|
|
|
|
/**
|
|
* ParcelableHolder is a Parcelable which can contain another Parcelable.
|
|
* The main use case of ParcelableHolder is to make a Parcelable extensible.
|
|
* For example, an AOSP-defined Parcelable <code>AospDefinedParcelable</code>
|
|
* is expected to be extended by device implementers for their value-add features.
|
|
* Previously without ParcelableHolder, the device implementers had to
|
|
* directly modify the Parcelable to add more fields:
|
|
* <pre> {@code
|
|
* parcelable AospDefinedParcelable {
|
|
* int a;
|
|
* String b;
|
|
* String x; // added by a device implementer
|
|
* int[] y; // added by a device implementer
|
|
* }}</pre>
|
|
*
|
|
* This practice is very error-prone because the fields added by the device implementer
|
|
* might have a conflict when the Parcelable is revisioned in the next releases of Android.
|
|
*
|
|
* Using ParcelableHolder, one can define an extension point in a Parcelable.
|
|
* <pre> {@code
|
|
* parcelable AospDefinedParcelable {
|
|
* int a;
|
|
* String b;
|
|
* ParcelableHolder extension;
|
|
* }}</pre>
|
|
* Then the device implementers can define their own Parcelable for their extension.
|
|
*
|
|
* <pre> {@code
|
|
* parcelable OemDefinedParcelable {
|
|
* String x;
|
|
* int[] y;
|
|
* }}</pre>
|
|
* Finally, the new Parcelable can be attached to the original Parcelable via
|
|
* the ParcelableHolder field.
|
|
*
|
|
* <pre> {@code
|
|
* AospDefinedParcelable ap = ...;
|
|
* OemDefinedParcelable op = new OemDefinedParcelable();
|
|
* op.x = ...;
|
|
* op.y = ...;
|
|
* ap.extension.setParcelable(op);}</pre>
|
|
*
|
|
* <p class="note">ParcelableHolder is <strong>not</strong> thread-safe.</p>
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public final class ParcelableHolder implements Parcelable {
|
|
/**
|
|
* This is set by {@link #setParcelable}.
|
|
* {@link #mParcelable} and {@link #mParcel} are mutually exclusive
|
|
* if {@link ParcelableHolder} contains value, otherwise, both are null.
|
|
*/
|
|
private Parcelable mParcelable;
|
|
/**
|
|
* This is set by {@link #readFromParcel}.
|
|
* {@link #mParcelable} and {@link #mParcel} are mutually exclusive
|
|
* if {@link ParcelableHolder} contains value, otherwise, both are null.
|
|
*/
|
|
private Parcel mParcel;
|
|
private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
|
|
|
|
public ParcelableHolder(@Parcelable.Stability int stability) {
|
|
mStability = stability;
|
|
}
|
|
|
|
private ParcelableHolder() {
|
|
|
|
}
|
|
|
|
/**
|
|
* {@link ParcelableHolder}'s stability is determined by the parcelable
|
|
* which contains this ParcelableHolder.
|
|
* For more detail refer to {@link Parcelable#getStability}.
|
|
*/
|
|
@Override
|
|
public @Parcelable.Stability int getStability() {
|
|
return mStability;
|
|
}
|
|
|
|
@NonNull
|
|
public static final Parcelable.Creator<ParcelableHolder> CREATOR =
|
|
new Parcelable.Creator<ParcelableHolder>() {
|
|
@NonNull
|
|
@Override
|
|
public ParcelableHolder createFromParcel(@NonNull Parcel parcel) {
|
|
ParcelableHolder parcelable = new ParcelableHolder();
|
|
parcelable.readFromParcel(parcel);
|
|
return parcelable;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public ParcelableHolder[] newArray(int size) {
|
|
return new ParcelableHolder[size];
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Write a parcelable into ParcelableHolder, the previous parcelable will be removed.
|
|
* (@link #setParcelable} and (@link #getParcelable} are not thread-safe.
|
|
* @throws BadParcelableException if the parcelable's stability is more unstable
|
|
* ParcelableHolder.
|
|
*/
|
|
public void setParcelable(@Nullable Parcelable p) {
|
|
// A ParcelableHolder can only hold things at its stability or higher.
|
|
if (p != null && this.getStability() > p.getStability()) {
|
|
throw new BadParcelableException(
|
|
"A ParcelableHolder can only hold things at its stability or higher. "
|
|
+ "The ParcelableHolder's stability is " + this.getStability()
|
|
+ ", but the parcelable's stability is " + p.getStability());
|
|
}
|
|
mParcelable = p;
|
|
if (mParcel != null) {
|
|
mParcel.recycle();
|
|
mParcel = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a parcelable from ParcelableHolder.
|
|
* (@link #setParcelable} and (@link #getParcelable} are not thread-safe.
|
|
* @return the parcelable that was written by {@link #setParcelable} or {@link #readFromParcel},
|
|
* or {@code null} if the parcelable has not been written.
|
|
* @throws BadParcelableException if T is different from the type written by
|
|
* (@link #setParcelable}.
|
|
*/
|
|
@Nullable
|
|
public <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
|
|
if (mParcel == null) {
|
|
if (mParcelable != null && !clazz.isInstance(mParcelable)) {
|
|
throw new BadParcelableException(
|
|
"The ParcelableHolder has " + mParcelable.getClass().getName()
|
|
+ ", but the requested type is " + clazz.getName());
|
|
}
|
|
return (T) mParcelable;
|
|
}
|
|
|
|
mParcel.setDataPosition(0);
|
|
|
|
T parcelable = mParcel.readParcelable(clazz.getClassLoader());
|
|
if (parcelable != null && !clazz.isInstance(parcelable)) {
|
|
throw new BadParcelableException(
|
|
"The ParcelableHolder has " + parcelable.getClass().getName()
|
|
+ ", but the requested type is " + clazz.getName());
|
|
}
|
|
mParcelable = parcelable;
|
|
|
|
mParcel.recycle();
|
|
mParcel = null;
|
|
return parcelable;
|
|
}
|
|
|
|
/**
|
|
* Read ParcelableHolder from a parcel.
|
|
*/
|
|
public void readFromParcel(@NonNull Parcel parcel) {
|
|
int wireStability = parcel.readInt();
|
|
if (this.mStability != wireStability) {
|
|
throw new IllegalArgumentException("Expected stability " + this.mStability
|
|
+ " but got " + wireStability);
|
|
}
|
|
|
|
mParcelable = null;
|
|
|
|
int dataSize = parcel.readInt();
|
|
if (dataSize < 0) {
|
|
throw new IllegalArgumentException("dataSize from parcel is negative");
|
|
} else if (dataSize == 0) {
|
|
if (mParcel != null) {
|
|
mParcel.recycle();
|
|
mParcel = null;
|
|
}
|
|
return;
|
|
}
|
|
if (mParcel == null) {
|
|
mParcel = Parcel.obtain();
|
|
}
|
|
mParcel.setDataPosition(0);
|
|
mParcel.setDataSize(0);
|
|
int dataStartPos = parcel.dataPosition();
|
|
|
|
mParcel.appendFrom(parcel, dataStartPos, dataSize);
|
|
parcel.setDataPosition(MathUtils.addOrThrow(dataStartPos, dataSize));
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel parcel, int flags) {
|
|
parcel.writeInt(this.mStability);
|
|
|
|
if (mParcel != null) {
|
|
parcel.writeInt(mParcel.dataSize());
|
|
parcel.appendFrom(mParcel, 0, mParcel.dataSize());
|
|
return;
|
|
}
|
|
|
|
if (mParcelable == null) {
|
|
parcel.writeInt(0);
|
|
return;
|
|
}
|
|
|
|
int sizePos = parcel.dataPosition();
|
|
parcel.writeInt(0);
|
|
int dataStartPos = parcel.dataPosition();
|
|
parcel.writeParcelable(mParcelable, 0);
|
|
int dataSize = parcel.dataPosition() - dataStartPos;
|
|
|
|
parcel.setDataPosition(sizePos);
|
|
parcel.writeInt(dataSize);
|
|
parcel.setDataPosition(MathUtils.addOrThrow(parcel.dataPosition(), dataSize));
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
if (mParcel != null) {
|
|
return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
|
|
}
|
|
if (mParcelable != null) {
|
|
return mParcelable.describeContents();
|
|
}
|
|
return 0;
|
|
}
|
|
}
|