/* * Copyright (C) 2016 The CyanogenMod 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 cyanogenmod.os; import android.os.Parcel; import cyanogenmod.os.Build.CM_VERSION_CODES; /** * Simply, Concierge handles your parcels and makes sure they get marshalled and unmarshalled * correctly when cross IPC boundaries even when there is a version mismatch between the client * sdk level and the framework implementation. * *

On incoming parcel (to be unmarshalled): * *

 *     ParcelInfo incomingParcelInfo = Concierge.receiveParcel(incomingParcel);
 *     int parcelableVersion = incomingParcelInfo.getParcelVersion();
 *
 *     // Do unmarshalling steps here iterating over every plausible version
 *
 *     // Complete the process
 *     incomingParcelInfo.complete();
 * 
* *

On outgoing parcel (to be marshalled): * *

 *     ParcelInfo outgoingParcelInfo = Concierge.prepareParcel(incomingParcel);
 *
 *     // Do marshalling steps here iterating over every plausible version
 *
 *     // Complete the process
 *     outgoingParcelInfo.complete();
 * 
*/ public final class Concierge { /** Not instantiable */ private Concierge() { // Don't instantiate } /** * Since there might be a case where new versions of the cm framework use applications running * old versions of the protocol (and thus old versions of this class), we need a versioning * system for the parcels sent between the core framework and its sdk users. * * This parcelable version should be the latest version API version listed in * {@link CM_VERSION_CODES} * @hide */ public static final int PARCELABLE_VERSION = CM_VERSION_CODES.ELDERBERRY; /** * Tell the concierge to receive our parcel, so we can get information from it. * * MUST CALL {@link ParcelInfo#complete()} AFTER UNMARSHALLING. * * @param parcel Incoming parcel to be unmarshalled * @return {@link ParcelInfo} containing parcel information, specifically the version. */ public static ParcelInfo receiveParcel(Parcel parcel) { return new ParcelInfo(parcel); } /** * Prepare a parcel for the Concierge. * * MUST CALL {@link ParcelInfo#complete()} AFTER MARSHALLING. * * @param parcel Outgoing parcel to be marshalled * @return {@link ParcelInfo} containing parcel information, specifically the version. */ public static ParcelInfo prepareParcel(Parcel parcel) { return new ParcelInfo(parcel, PARCELABLE_VERSION); } /** * Parcel header info specific to the Parcel object that is passed in via * {@link #prepareParcel(Parcel)} or {@link #receiveParcel(Parcel)}. The exposed method * of {@link #getParcelVersion()} gets the api level of the parcel object. */ public final static class ParcelInfo { private Parcel mParcel; private int mParcelableVersion; private int mParcelableSize; private int mStartPosition; private int mSizePosition; private boolean mCreation = false; ParcelInfo(Parcel parcel) { mCreation = false; mParcel = parcel; mParcelableVersion = parcel.readInt(); mParcelableSize = parcel.readInt(); mStartPosition = parcel.dataPosition(); } ParcelInfo(Parcel parcel, int parcelableVersion) { mCreation = true; mParcel = parcel; mParcelableVersion = parcelableVersion; // Write parcelable version, make sure to define explicit changes // within {@link #PARCELABLE_VERSION); mParcel.writeInt(mParcelableVersion); // Inject a placeholder that will store the parcel size from this point on // (not including the size itself). mSizePosition = parcel.dataPosition(); mParcel.writeInt(0); mStartPosition = parcel.dataPosition(); } /** * Get the parcel version from the {@link Parcel} received by the Concierge. * @return {@link #PARCELABLE_VERSION} of the {@link Parcel} */ public int getParcelVersion() { return mParcelableVersion; } /** * Complete the {@link ParcelInfo} for the Concierge. */ public void complete() { if (mCreation) { // Go back and write size mParcelableSize = mParcel.dataPosition() - mStartPosition; mParcel.setDataPosition(mSizePosition); mParcel.writeInt(mParcelableSize); mParcel.setDataPosition(mStartPosition + mParcelableSize); } else { mParcel.setDataPosition(mStartPosition + mParcelableSize); } } } }