1 /** 2 * Copyright (c) 2016, 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 package com.android.car.stream; 17 18 import android.os.Bundle; 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.support.annotation.VisibleForTesting; 22 import android.util.Log; 23 24 import java.lang.reflect.Array; 25 26 /** 27 * Base class for Parcelable classes that serialize/deserialize themselves using Bundles for 28 * backward compatibility. 29 * 30 * <p> 31 * By using Bundles which require explicit key-value pairs, unknown fields can be handled 32 * gracefully. Also, by ensuring that custom classes are serialized to a Bundle containing 33 * primitives or system classes only, ClassNotFoundExceptions can be prevented when deserializing. 34 * </p> 35 * 36 * <p> 37 * Subclass must expose a default constructor, implement {@link #writeToBundle(Bundle)} and 38 * {@link #readFromBundle(Bundle)} instead of {@link #writeToParcel(Parcel, int)}. 39 * It should also define a CREATOR, as required of all Parcelables, by instantiating a 40 * {@link BundleableCreator}. 41 * 42 * Example: 43 * 44 * public static final Creator<MyClass> CREATOR = new BundleableCreator<>(MyClass.class); 45 * 46 * @Override 47 * protected void writeToBundle(Bundle bundle) { 48 * bundle.putInt(FIRST_FIELD_KEY, mFirstField); 49 * if (mCustomField != null) { 50 * Bundle customFieldBundle = new Bundle(); 51 * mCustomField.writeToBundle(customFieldBundle); 52 * bundle.putBundle(CUSTOM_FIELD_KEY, customFieldBundle); 53 * } 54 * bundle.putParcelable(INTENT_KEY, mIntent); 55 * } 56 * 57 * @Override 58 * protected void readFromBundle(Bundle bundle) { 59 * mFirstField = bundle.getInt(FIRST_FIELD_KEY); 60 * Bundle customFieldBundle = bundle.getBundle(CUSTOM_FIELD_KEY); 61 * if (customFieldBundle != null) { 62 * mCustomField = new CustomClass(); 63 * mCustomField.readFromBundle(customFieldBundle); 64 * } 65 * mIntent = bundle.getParcelable(INTENT_KEY); 66 * } 67 * </p> 68 * 69 * <p> 70 * All subclasses should be added to BundleableTest#BUNDLEABLE_CLASSES list to be tested. 71 * </p> 72 */ 73 public abstract class AbstractBundleable implements Parcelable { 74 private static final String TAG = "Bundleable"; 75 76 /** 77 * Creator class for unmarshalling subclasses of {@link AbstractBundleable}. 78 */ 79 @VisibleForTesting 80 public static class BundleableCreator<T extends AbstractBundleable> 81 implements Creator<T> { 82 private Class<T> clazz; 83 BundleableCreator(Class<T> bundleableClazz)84 public BundleableCreator(Class<T> bundleableClazz) { 85 clazz = bundleableClazz; 86 } 87 88 @Override createFromParcel(Parcel source)89 public final T createFromParcel(Parcel source) { 90 T instance = null; 91 try { 92 instance = clazz.newInstance(); 93 instance.readFromBundle(source.readBundle()); 94 } catch (Exception e) { 95 Log.e(TAG, "Failed to instantiate " + clazz.getSimpleName(), e); 96 } 97 return instance; 98 } 99 100 @SuppressWarnings("unchecked") 101 @Override newArray(int size)102 public final T[] newArray(int size) { 103 return (T[]) Array.newInstance(clazz, size); 104 } 105 } 106 107 @Override describeContents()108 public final int describeContents() { 109 return 0; 110 } 111 112 @Override writeToParcel(Parcel dest, int flags)113 public final void writeToParcel(Parcel dest, int flags) { 114 Bundle bundle = new Bundle(); 115 writeToBundle(bundle); 116 dest.writeBundle(bundle); 117 } 118 119 @Override toString()120 public String toString() { 121 Bundle bundle = new Bundle(); 122 writeToBundle(bundle); 123 return bundle.toString(); 124 } 125 126 /** 127 * Writes the states of the instance to the given Bundle. Only primitives or system classes 128 * can be written into the Bundle. If a field of a custom class needs to be serialized, 129 * serialize it into a new Bundle, and then write that Bundle into the outer Bundle. A list or 130 * array of custom class instances should similarly be converted into an array of Bundles first. 131 */ writeToBundle(Bundle bundle)132 protected abstract void writeToBundle(Bundle bundle); 133 134 /** 135 * Reads the states saved in the Bundle into the current instance. The implementation should 136 * mirror that of {@link #writeToBundle(Bundle)}. 137 */ readFromBundle(Bundle bundle)138 protected abstract void readFromBundle(Bundle bundle); 139 }