/* * Copyright 2017 The Android Open Source 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 android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityClient; import android.app.ActivityOptions.SceneTransitionInfo; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.app.IActivityClientController; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.BaseBundle; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.PersistableBundle; import android.os.Trace; import android.window.ActivityWindowInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Request to launch an activity. * @hide */ public class LaunchActivityItem extends ClientTransactionItem { private IBinder mActivityToken; @UnsupportedAppUsage private Intent mIntent; private int mIdent; @UnsupportedAppUsage private ActivityInfo mInfo; private Configuration mCurConfig; private Configuration mOverrideConfig; private int mDeviceId; private String mReferrer; private IVoiceInteractor mVoiceInteractor; private int mProcState; private Bundle mState; private PersistableBundle mPersistentState; private List mPendingResults; private List mPendingNewIntents; private SceneTransitionInfo mSceneTransitionInfo; private boolean mIsForward; private ProfilerInfo mProfilerInfo; private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; private IBinder mTaskFragmentToken; private IBinder mInitialCallerInfoAccessToken; private ActivityWindowInfo mActivityWindowInfo; /** * It is only non-null if the process is the first time to launch activity. It is only an * optimization for quick look up of the interface so the field is ignored for comparison. */ private IActivityClientController mActivityClientController; @Override public void preExecute(@NonNull ClientTransactionHandler client) { client.countLaunchingActivities(1); client.updateProcessState(mProcState, false); CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig); CompatibilityInfo.applyOverrideScaleIfNeeded(mOverrideConfig); client.updatePendingConfiguration(mCurConfig); if (mActivityClientController != null) { ActivityClient.setActivityClientController(mActivityClientController); } } @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo, mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward, mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken, mInitialCallerInfoAccessToken, mActivityWindowInfo); client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @Override public void postExecute(@NonNull ClientTransactionHandler client, @NonNull PendingTransactionActions pendingActions) { client.countLaunchingActivities(-1); } // ObjectPoolItem implementation private LaunchActivityItem() {} /** Obtain an instance initialized with provided params. */ @NonNull public static LaunchActivityItem obtain(@NonNull IBinder activityToken, @NonNull Intent intent, int ident, @NonNull ActivityInfo info, @NonNull Configuration curConfig, @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer, @Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState, @Nullable List pendingResults, @Nullable List pendingNewIntents, @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken, @Nullable IActivityClientController activityClientController, @NonNull IBinder shareableActivityToken, boolean launchedFromBubble, @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken, @NonNull ActivityWindowInfo activityWindowInfo) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); } setValues(instance, activityToken, new Intent(intent), ident, new ActivityInfo(info), new Configuration(curConfig), new Configuration(overrideConfig), deviceId, referrer, voiceInteractor, procState, state != null ? new Bundle(state) : null, persistentState != null ? new PersistableBundle(persistentState) : null, pendingResults != null ? new ArrayList<>(pendingResults) : null, pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null, sceneTransitionInfo, isForward, profilerInfo != null ? new ProfilerInfo(profilerInfo) : null, assistToken, activityClientController, shareableActivityToken, launchedFromBubble, taskFragmentToken, initialCallerInfoAccessToken, new ActivityWindowInfo(activityWindowInfo)); return instance; } @VisibleForTesting(visibility = PACKAGE) @NonNull @Override public IBinder getActivityToken() { return mActivityToken; } @Override public void recycle() { setValues(this, null, null, 0, null, null, null, 0, null, null, 0, null, null, null, null, null, false, null, null, null, null, false, null, null, null); ObjectPool.recycle(this); } // Parcelable implementation /** Write from Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mActivityToken); dest.writeTypedObject(mIntent, flags); dest.writeInt(mIdent); dest.writeTypedObject(mInfo, flags); dest.writeTypedObject(mCurConfig, flags); dest.writeTypedObject(mOverrideConfig, flags); dest.writeInt(mDeviceId); dest.writeString(mReferrer); dest.writeStrongInterface(mVoiceInteractor); dest.writeInt(mProcState); dest.writeBundle(mState); dest.writePersistableBundle(mPersistentState); dest.writeTypedList(mPendingResults, flags); dest.writeTypedList(mPendingNewIntents, flags); dest.writeTypedObject(mSceneTransitionInfo, flags); dest.writeBoolean(mIsForward); dest.writeTypedObject(mProfilerInfo, flags); dest.writeStrongBinder(mAssistToken); dest.writeStrongInterface(mActivityClientController); dest.writeStrongBinder(mShareableActivityToken); dest.writeBoolean(mLaunchedFromBubble); dest.writeStrongBinder(mTaskFragmentToken); dest.writeStrongBinder(mInitialCallerInfoAccessToken); dest.writeTypedObject(mActivityWindowInfo, flags); } /** Read from Parcel. */ private LaunchActivityItem(@NonNull Parcel in) { setValues(this, in.readStrongBinder(), in.readTypedObject(Intent.CREATOR), in.readInt(), in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR), in.readTypedObject(Configuration.CREATOR), in.readInt(), in.readString(), IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(), in.readBundle(getClass().getClassLoader()), in.readPersistableBundle(getClass().getClassLoader()), in.createTypedArrayList(ResultInfo.CREATOR), in.createTypedArrayList(ReferrerIntent.CREATOR), in.readTypedObject(SceneTransitionInfo.CREATOR), in.readBoolean(), in.readTypedObject(ProfilerInfo.CREATOR), in.readStrongBinder(), IActivityClientController.Stub.asInterface(in.readStrongBinder()), in.readStrongBinder(), in.readBoolean(), in.readStrongBinder(), in.readStrongBinder(), in.readTypedObject(ActivityWindowInfo.CREATOR)); } public static final @NonNull Creator CREATOR = new Creator<>() { public LaunchActivityItem createFromParcel(@NonNull Parcel in) { return new LaunchActivityItem(in); } public LaunchActivityItem[] newArray(int size) { return new LaunchActivityItem[size]; } }; @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final LaunchActivityItem other = (LaunchActivityItem) o; final boolean intentsEqual = (mIntent == null && other.mIntent == null) || (mIntent != null && mIntent.filterEquals(other.mIntent)); return intentsEqual && Objects.equals(mActivityToken, other.mActivityToken) && mIdent == other.mIdent && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig) && Objects.equals(mOverrideConfig, other.mOverrideConfig) && mDeviceId == other.mDeviceId && Objects.equals(mReferrer, other.mReferrer) && mProcState == other.mProcState && areBundlesEqualRoughly(mState, other.mState) && areBundlesEqualRoughly(mPersistentState, other.mPersistentState) && Objects.equals(mPendingResults, other.mPendingResults) && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) && (mSceneTransitionInfo == null) == (other.mSceneTransitionInfo == null) && mIsForward == other.mIsForward && Objects.equals(mProfilerInfo, other.mProfilerInfo) && Objects.equals(mAssistToken, other.mAssistToken) && Objects.equals(mShareableActivityToken, other.mShareableActivityToken) && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken) && Objects.equals(mInitialCallerInfoAccessToken, other.mInitialCallerInfoAccessToken) && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo); } @Override public int hashCode() { int result = 17; result = 31 * result + Objects.hashCode(mActivityToken); result = 31 * result + mIntent.filterHashCode(); result = 31 * result + mIdent; result = 31 * result + Objects.hashCode(mCurConfig); result = 31 * result + Objects.hashCode(mOverrideConfig); result = 31 * result + mDeviceId; result = 31 * result + Objects.hashCode(mReferrer); result = 31 * result + Objects.hashCode(mProcState); result = 31 * result + getRoughBundleHashCode(mState); result = 31 * result + getRoughBundleHashCode(mPersistentState); result = 31 * result + Objects.hashCode(mPendingResults); result = 31 * result + Objects.hashCode(mPendingNewIntents); result = 31 * result + (mSceneTransitionInfo != null ? 1 : 0); result = 31 * result + (mIsForward ? 1 : 0); result = 31 * result + Objects.hashCode(mProfilerInfo); result = 31 * result + Objects.hashCode(mAssistToken); result = 31 * result + Objects.hashCode(mShareableActivityToken); result = 31 * result + Objects.hashCode(mTaskFragmentToken); result = 31 * result + Objects.hashCode(mInitialCallerInfoAccessToken); result = 31 * result + Objects.hashCode(mActivityWindowInfo); return result; } private boolean activityInfoEqual(@Nullable ActivityInfo other) { if (mInfo == null) { return other == null; } return other != null && mInfo.flags == other.flags && mInfo.getMaxAspectRatio() == other.getMaxAspectRatio() && Objects.equals(mInfo.launchToken, other.launchToken) && Objects.equals(mInfo.getComponentName(), other.getComponentName()); } /** * This method may be used to compare a parceled item with another unparceled item, and the * parceled bundle may contain customized class that will raise BadParcelableException when * unparceling if a customized class loader is not set to the bundle. So the hash code is * simply determined by the bundle is empty or not. */ private static int getRoughBundleHashCode(@Nullable BaseBundle bundle) { return (bundle == null || bundle.isDefinitelyEmpty()) ? 0 : 1; } /** Compares the bundles without unparceling them (avoid BadParcelableException). */ private static boolean areBundlesEqualRoughly(@Nullable BaseBundle a, @Nullable BaseBundle b) { return getRoughBundleHashCode(a) == getRoughBundleHashCode(b); } @Override public String toString() { return "LaunchActivityItem{activityToken=" + mActivityToken + ",intent=" + mIntent + ",ident=" + mIdent + ",info=" + mInfo + ",curConfig=" + mCurConfig + ",overrideConfig=" + mOverrideConfig + ",deviceId=" + mDeviceId + ",referrer=" + mReferrer + ",procState=" + mProcState + ",state=" + mState + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults + ",pendingNewIntents=" + mPendingNewIntents + ",sceneTransitionInfo=" + mSceneTransitionInfo + ",profilerInfo=" + mProfilerInfo + ",assistToken=" + mAssistToken + ",shareableActivityToken=" + mShareableActivityToken + ",activityWindowInfo=" + mActivityWindowInfo + "}"; } // Using the same method to set and clear values to make sure we don't forget anything private static void setValues(@Nullable LaunchActivityItem instance, @Nullable IBinder activityToken, @Nullable Intent intent, int ident, @Nullable ActivityInfo info, @Nullable Configuration curConfig, @Nullable Configuration overrideConfig, int deviceId, @Nullable String referrer, @Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState, @Nullable List pendingResults, @Nullable List pendingNewIntents, @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward, @Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken, @Nullable IActivityClientController activityClientController, @Nullable IBinder shareableActivityToken, boolean launchedFromBubble, @Nullable IBinder taskFragmentToken, @Nullable IBinder initialCallerInfoAccessToken, @Nullable ActivityWindowInfo activityWindowInfo) { instance.mActivityToken = activityToken; instance.mIntent = intent; instance.mIdent = ident; instance.mInfo = info; instance.mCurConfig = curConfig; instance.mOverrideConfig = overrideConfig; instance.mDeviceId = deviceId; instance.mReferrer = referrer; instance.mVoiceInteractor = voiceInteractor; instance.mProcState = procState; instance.mState = state; instance.mPersistentState = persistentState; instance.mPendingResults = pendingResults; instance.mPendingNewIntents = pendingNewIntents; instance.mSceneTransitionInfo = sceneTransitionInfo; instance.mIsForward = isForward; instance.mProfilerInfo = profilerInfo; instance.mAssistToken = assistToken; instance.mActivityClientController = activityClientController; instance.mShareableActivityToken = shareableActivityToken; instance.mLaunchedFromBubble = launchedFromBubble; instance.mTaskFragmentToken = taskFragmentToken; instance.mInitialCallerInfoAccessToken = initialCallerInfoAccessToken; instance.mActivityWindowInfo = activityWindowInfo; } }