| /* |
| * 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. |
| * |
| * <p>On incoming parcel (to be unmarshalled): |
| * |
| * <pre class="prettyprint"> |
| * ParcelInfo incomingParcelInfo = Concierge.receiveParcel(incomingParcel); |
| * int parcelableVersion = incomingParcelInfo.getParcelVersion(); |
| * |
| * // Do unmarshalling steps here iterating over every plausible version |
| * |
| * // Complete the process |
| * incomingParcelInfo.complete(); |
| * </pre> |
| * |
| * <p>On outgoing parcel (to be marshalled): |
| * |
| * <pre class="prettyprint"> |
| * ParcelInfo outgoingParcelInfo = Concierge.prepareParcel(incomingParcel); |
| * |
| * // Do marshalling steps here iterating over every plausible version |
| * |
| * // Complete the process |
| * outgoingParcelInfo.complete(); |
| * </pre> |
| */ |
| 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); |
| } |
| } |
| } |
| } |