Home | History | Annotate | Download | only in stream
      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 
     84         public BundleableCreator(Class<T> bundleableClazz) {
     85             clazz = bundleableClazz;
     86         }
     87 
     88         @Override
     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
    102         public final T[] newArray(int size) {
    103             return (T[]) Array.newInstance(clazz, size);
    104         }
    105     }
    106 
    107     @Override
    108     public final int describeContents() {
    109         return 0;
    110     }
    111 
    112     @Override
    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
    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      */
    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      */
    138     protected abstract void readFromBundle(Bundle bundle);
    139 }