1 /* 2 * Copyright (C) 2020 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.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.util.MathUtils; 23 24 /** 25 * ParcelableHolder is a Parcelable which can contain another Parcelable. 26 * The main use case of ParcelableHolder is to make a Parcelable extensible. 27 * For example, an AOSP-defined Parcelable <code>AospDefinedParcelable</code> 28 * is expected to be extended by device implementers for their value-add features. 29 * Previously without ParcelableHolder, the device implementers had to 30 * directly modify the Parcelable to add more fields: 31 * <pre> {@code 32 * parcelable AospDefinedParcelable { 33 * int a; 34 * String b; 35 * String x; // added by a device implementer 36 * int[] y; // added by a device implementer 37 * }}</pre> 38 * 39 * This practice is very error-prone because the fields added by the device implementer 40 * might have a conflict when the Parcelable is revisioned in the next releases of Android. 41 * 42 * Using ParcelableHolder, one can define an extension point in a Parcelable. 43 * <pre> {@code 44 * parcelable AospDefinedParcelable { 45 * int a; 46 * String b; 47 * ParcelableHolder extension; 48 * }}</pre> 49 * Then the device implementers can define their own Parcelable for their extension. 50 * 51 * <pre> {@code 52 * parcelable OemDefinedParcelable { 53 * String x; 54 * int[] y; 55 * }}</pre> 56 * Finally, the new Parcelable can be attached to the original Parcelable via 57 * the ParcelableHolder field. 58 * 59 * <pre> {@code 60 * AospDefinedParcelable ap = ...; 61 * OemDefinedParcelable op = new OemDefinedParcelable(); 62 * op.x = ...; 63 * op.y = ...; 64 * ap.extension.setParcelable(op);}</pre> 65 * 66 * <p class="note">ParcelableHolder is <strong>not</strong> thread-safe.</p> 67 * 68 * @hide 69 */ 70 @SystemApi 71 public final class ParcelableHolder implements Parcelable { 72 /** 73 * This is set by {@link #setParcelable}. 74 * {@link #mParcelable} and {@link #mParcel} are mutually exclusive 75 * if {@link ParcelableHolder} contains value, otherwise, both are null. 76 */ 77 private Parcelable mParcelable; 78 /** 79 * This is set by {@link #readFromParcel}. 80 * {@link #mParcelable} and {@link #mParcel} are mutually exclusive 81 * if {@link ParcelableHolder} contains value, otherwise, both are null. 82 */ 83 private Parcel mParcel; 84 private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL; 85 ParcelableHolder(@arcelable.Stability int stability)86 public ParcelableHolder(@Parcelable.Stability int stability) { 87 mStability = stability; 88 } 89 ParcelableHolder()90 private ParcelableHolder() { 91 92 } 93 94 /** 95 * {@link ParcelableHolder}'s stability is determined by the parcelable 96 * which contains this ParcelableHolder. 97 * For more detail refer to {@link Parcelable#getStability}. 98 */ 99 @Override getStability()100 public @Parcelable.Stability int getStability() { 101 return mStability; 102 } 103 104 @NonNull 105 public static final Parcelable.Creator<ParcelableHolder> CREATOR = 106 new Parcelable.Creator<ParcelableHolder>() { 107 @NonNull 108 @Override 109 public ParcelableHolder createFromParcel(@NonNull Parcel parcel) { 110 ParcelableHolder parcelable = new ParcelableHolder(); 111 parcelable.readFromParcel(parcel); 112 return parcelable; 113 } 114 115 @NonNull 116 @Override 117 public ParcelableHolder[] newArray(int size) { 118 return new ParcelableHolder[size]; 119 } 120 }; 121 122 123 /** 124 * Write a parcelable into ParcelableHolder, the previous parcelable will be removed. 125 * (@link #setParcelable} and (@link #getParcelable} are not thread-safe. 126 * @throws BadParcelableException if the parcelable's stability is more unstable 127 * ParcelableHolder. 128 */ setParcelable(@ullable Parcelable p)129 public void setParcelable(@Nullable Parcelable p) { 130 // A ParcelableHolder can only hold things at its stability or higher. 131 if (p != null && this.getStability() > p.getStability()) { 132 throw new BadParcelableException( 133 "A ParcelableHolder can only hold things at its stability or higher. " 134 + "The ParcelableHolder's stability is " + this.getStability() 135 + ", but the parcelable's stability is " + p.getStability()); 136 } 137 mParcelable = p; 138 if (mParcel != null) { 139 mParcel.recycle(); 140 mParcel = null; 141 } 142 } 143 144 /** 145 * Read a parcelable from ParcelableHolder. 146 * (@link #setParcelable} and (@link #getParcelable} are not thread-safe. 147 * @return the parcelable that was written by {@link #setParcelable} or {@link #readFromParcel}, 148 * or {@code null} if the parcelable has not been written. 149 * @throws BadParcelableException if T is different from the type written by 150 * (@link #setParcelable}. 151 */ 152 @Nullable getParcelable(@onNull Class<T> clazz)153 public <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) { 154 if (mParcel == null) { 155 if (mParcelable != null && !clazz.isInstance(mParcelable)) { 156 throw new BadParcelableException( 157 "The ParcelableHolder has " + mParcelable.getClass().getName() 158 + ", but the requested type is " + clazz.getName()); 159 } 160 return (T) mParcelable; 161 } 162 163 mParcel.setDataPosition(0); 164 165 T parcelable = mParcel.readParcelable(clazz.getClassLoader()); 166 if (parcelable != null && !clazz.isInstance(parcelable)) { 167 throw new BadParcelableException( 168 "The ParcelableHolder has " + parcelable.getClass().getName() 169 + ", but the requested type is " + clazz.getName()); 170 } 171 mParcelable = parcelable; 172 173 mParcel.recycle(); 174 mParcel = null; 175 return parcelable; 176 } 177 178 /** 179 * Read ParcelableHolder from a parcel. 180 */ readFromParcel(@onNull Parcel parcel)181 public void readFromParcel(@NonNull Parcel parcel) { 182 int wireStability = parcel.readInt(); 183 if (this.mStability != wireStability) { 184 throw new IllegalArgumentException("Expected stability " + this.mStability 185 + " but got " + wireStability); 186 } 187 188 mParcelable = null; 189 190 int dataSize = parcel.readInt(); 191 if (dataSize < 0) { 192 throw new IllegalArgumentException("dataSize from parcel is negative"); 193 } else if (dataSize == 0) { 194 if (mParcel != null) { 195 mParcel.recycle(); 196 mParcel = null; 197 } 198 return; 199 } 200 if (mParcel == null) { 201 mParcel = Parcel.obtain(); 202 } 203 mParcel.setDataPosition(0); 204 mParcel.setDataSize(0); 205 int dataStartPos = parcel.dataPosition(); 206 207 mParcel.appendFrom(parcel, dataStartPos, dataSize); 208 parcel.setDataPosition(MathUtils.addOrThrow(dataStartPos, dataSize)); 209 } 210 211 @Override writeToParcel(@onNull Parcel parcel, int flags)212 public void writeToParcel(@NonNull Parcel parcel, int flags) { 213 parcel.writeInt(this.mStability); 214 215 if (mParcel != null) { 216 parcel.writeInt(mParcel.dataSize()); 217 parcel.appendFrom(mParcel, 0, mParcel.dataSize()); 218 return; 219 } 220 221 if (mParcelable == null) { 222 parcel.writeInt(0); 223 return; 224 } 225 226 int sizePos = parcel.dataPosition(); 227 parcel.writeInt(0); 228 int dataStartPos = parcel.dataPosition(); 229 parcel.writeParcelable(mParcelable, 0); 230 int dataSize = parcel.dataPosition() - dataStartPos; 231 232 parcel.setDataPosition(sizePos); 233 parcel.writeInt(dataSize); 234 parcel.setDataPosition(MathUtils.addOrThrow(parcel.dataPosition(), dataSize)); 235 } 236 237 @Override describeContents()238 public int describeContents() { 239 if (mParcel != null) { 240 return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0; 241 } 242 if (mParcelable != null) { 243 return mParcelable.describeContents(); 244 } 245 return 0; 246 } 247 } 248