1 /* 2 * Copyright (C) 2015 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.support.car.os; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.support.car.annotation.VersionDef; 22 23 /** 24 * ExtendableParcelable helps extending Parcelable in future in safe way. 25 * Basic idea is to write version code and payload length when writing to Parcel. 26 * Reader side reads version and do following actions: 27 * - if writer version <= reader version: Reader should know up to which element is supported by 28 * writer and read only up to that portion. 29 * - if writer version > reader version: Reader reads all members it knows and throw away remaing 30 * data so that additional Parcel reading can be done safely. 31 * 32 * For reader side: 33 * - In constructor with Parcel, call super(Parcel) first so that version number and payload length 34 * is read properly. 35 * - Call {@link #readHeader(Parcel)}. 36 * - After finishing all recognized or available members, call 37 * {@link #completeReading(Parcel, int)} with second argument set to return value from 38 * {@link #readHeader(Parcel)}. This will throw away any unread data. 39 * 40 * For writer side, when implementing writeToParcel: 41 * - call {@link #writeHeader(Parcel)} before writing anything else. 42 * - call {@link #completeWriting(Parcel, int)} with second argument set to return value from 43 * {@link #writeHeader(Parcel)}. 44 */ 45 public abstract class ExtendableParcelable implements Parcelable { 46 /** 47 * Version of this Parcelable. Reader side needs to read only up to the version written. 48 * This does not represent class version but contents version. For example, original 49 * Parcelable (=V1) passed to V2 supporting Parcelable will have version 1 even if 50 * V2 supporting Parcelable may have additional member variables added in V2. 51 */ 52 @VersionDef(version = 1) 53 public final int version; 54 55 /** 56 * Constructor for reading parcel. Always call this first before reading anything else. 57 * @param in 58 * @param maxVersion 59 */ ExtendableParcelable(Parcel in, int version)60 protected ExtendableParcelable(Parcel in, int version) { 61 int writerVersion = in.readInt(); 62 if (version < writerVersion) { // version limited by reader 63 this.version = version; 64 } else { // version limited by writer 65 this.version = writerVersion; 66 } 67 } 68 69 /** 70 * Constructor for writer side. Version should be always set. 71 * @param version 72 */ ExtendableParcelable(int version)73 protected ExtendableParcelable(int version) { 74 this.version = version; 75 } 76 77 /** 78 * Read header of Parcelable from Pacel. This should be done after super(Parcel, int) and 79 * before reading any Parcel. After all reading is done, {@link #completeReading(Parcel, int)} 80 * should be called. 81 * 82 * @param in 83 * @return last position. This should be passed to {@link #completeReading(Parcel, int)}. 84 */ readHeader(Parcel in)85 protected int readHeader(Parcel in) { 86 int payloadLength = in.readInt(); 87 int startingPosition = in.dataPosition(); 88 return startingPosition + payloadLength; 89 } 90 91 /** 92 * Complete reading and safely throw away any unread data. 93 * @param in 94 * @param lastPosition Last position of of this Parcelable in the passed Parcel. 95 * The value is passed from {@link #readHeader(Parcel)}. 96 */ completeReading(Parcel in, int lastPosition)97 protected void completeReading(Parcel in, int lastPosition) { 98 in.setDataPosition(lastPosition); 99 } 100 101 /** 102 * Write header for writing to Parcel. This should be done before writing anything else. 103 * Code to use ths can look like: 104 * int pos = writeHeader(dest); 105 * dest.writeInt(0); // whatever relevant data 106 * ... 107 * completeWrite(dest, pos); 108 * 109 * @param dest 110 * @return startingPosition which should be passed when calling completeWrite. 111 */ writeHeader(Parcel dest)112 protected int writeHeader(Parcel dest) { 113 dest.writeInt(version); 114 // temporary value for playload length. will be replaced in completeWrite 115 dest.writeInt(0); 116 // previous int is 4 bytes before this. 117 return dest.dataPosition(); 118 } 119 120 /** 121 * Complete writing the current Parcelable. No more write to Parcel should be done after 122 * this call. 123 * @param dest 124 * @param startingPosition startingPosition returned from writeHeader 125 */ completeWriting(Parcel dest, int startingPosition)126 protected void completeWriting(Parcel dest, int startingPosition) { 127 int currentPosition = dest.dataPosition(); 128 dest.setDataPosition(startingPosition - 4); 129 int payloadLength = currentPosition - startingPosition; 130 dest.writeInt(payloadLength); 131 dest.setDataPosition(currentPosition); 132 } 133 } 134