diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e0391eee5077d..c2287b460166e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -27,6 +27,7 @@ package android { field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; + field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; @@ -118,6 +119,7 @@ package android.app { public class ActivityOptions { method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); + method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); method public static void setExitTransitionTimeout(long); method public void setLaunchActivityType(int); method public void setLaunchTaskId(int); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 2b5e18d3feece..28da1c3a3eb7e 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; @@ -310,6 +311,9 @@ public class ActivityOptions { private static final String KEY_REMOTE_TRANSITION = "android:activity.remoteTransition"; + private static final String KEY_OVERRIDE_TASK_TRANSITION = + "android:activity.overrideTaskTransition"; + /** * @see #setLaunchCookie * @hide @@ -393,6 +397,7 @@ public class ActivityOptions { private RemoteAnimationAdapter mRemoteAnimationAdapter; private IBinder mLaunchCookie; private IRemoteTransition mRemoteTransition; + private boolean mOverrideTaskTransition; /** * Create an ActivityOptions specifying a custom animation to run when @@ -475,6 +480,40 @@ public class ActivityOptions { return opts; } + /** + * Create an ActivityOptions specifying a custom animation to run when the activity in the + * different task is displayed. + * + * @param context Who is defining this. This is the application that the + * animation resources will be loaded from. + * @param enterResId A resource ID of the animation resource to use for + * the incoming activity. Use 0 for no animation. + * @param exitResId A resource ID of the animation resource to use for + * the outgoing activity. Use 0 for no animation. + * @param handler If listener is non-null this must be a valid + * Handler on which to dispatch the callback; otherwise it should be null. + * @param startedListener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @param finishedListener Optional OnAnimationFinishedListener when the animation + * has finished running. + * + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + * @hide + */ + @RequiresPermission(START_TASKS_FROM_RECENTS) + @TestApi + public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context, + int enterResId, int exitResId, @Nullable Handler handler, + @Nullable OnAnimationStartedListener startedListener, + @Nullable OnAnimationFinishedListener finishedListener) { + ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler, + startedListener, finishedListener); + opts.mOverrideTaskTransition = true; + return opts; + } + /** * Creates an ActivityOptions specifying a custom animation to run in place on an existing * activity. @@ -1107,6 +1146,7 @@ public class ActivityOptions { mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( KEY_REMOTE_TRANSITION)); + mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION); } /** @@ -1561,6 +1601,12 @@ public class ActivityOptions { return mLaunchCookie; } + + /** @hide */ + public boolean getOverrideTaskTransition() { + return mOverrideTaskTransition; + } + /** * Update the current values in this ActivityOptions from those supplied * in otherOptions. Any values @@ -1789,6 +1835,9 @@ public class ActivityOptions { if (mRemoteTransition != null) { b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder()); } + if (mOverrideTaskTransition) { + b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition); + } return b; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 989e25d5483e9..ffbd17414d034 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2538,7 +2538,7 @@ - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index 3584c82bc57df..e2ca349cc5c8c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -72,9 +72,13 @@ public abstract class ActivityOptionsCompat { return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition()); } + /** + * Returns ActivityOptions for overriding task transition animation. + */ public static ActivityOptions makeCustomAnimation(Context context, int enterResId, int exitResId, final Runnable callback, final Handler callbackHandler) { - return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler, + return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId, + callbackHandler, new ActivityOptions.OnAnimationStartedListener() { @Override public void onAnimationStarted() { diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 5fe853a38dd7f..a1ff693202d98 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -963,7 +963,8 @@ class ActivityClientController extends IActivityClientController.Stub { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { r.mDisplayContent.mAppTransition.overridePendingAppTransition( - packageName, enterAnim, exitAnim, null, null); + packageName, enterAnim, exitAnim, null, null, + r.mOverrideTaskTransition); } } Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 36c503703b9c8..81baaa291fc0b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -682,6 +682,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mRequestForceTransition; boolean mEnteringAnimation; + boolean mOverrideTaskTransition; boolean mAppStopped; // A hint to override the window specified rotation animation, or -1 to use the window specified @@ -1627,6 +1628,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (rotationAnimation >= 0) { mRotationAnimationHint = rotationAnimation; } + + mOverrideTaskTransition = options.getOverrideTaskTransition(); } ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService( @@ -3997,7 +4000,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(), pendingOptions.getAnimationStartedListener(), - pendingOptions.getAnimationFinishedListener()); + pendingOptions.getAnimationFinishedListener(), + pendingOptions.getOverrideTaskTransition()); break; case ANIM_CLIP_REVEAL: displayContent.mAppTransition.overridePendingAppTransitionClipReveal( diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 90070c8f50686..eba3f93eac9ae 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -266,6 +266,7 @@ public class AppTransition implements Dump { private final boolean mLowRamRecentsEnabled; private final int mDefaultWindowAnimationStyleResId; + private boolean mOverrideTaskTransition; private RemoteAnimationController mRemoteAnimationController; @@ -971,7 +972,8 @@ public class AppTransition implements Dump { @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, boolean freeform, WindowContainer container) { - if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) { + if (mNextAppTransitionOverrideRequested + && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) { mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; } @@ -1175,7 +1177,8 @@ public class AppTransition implements Dump { } void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, - IRemoteCallback startedCallback, IRemoteCallback endedCallback) { + IRemoteCallback startedCallback, IRemoteCallback endedCallback, + boolean overrideTaskTransaction) { if (canOverridePendingAppTransition()) { clear(); mNextAppTransitionOverrideRequested = true; @@ -1185,6 +1188,7 @@ public class AppTransition implements Dump { postAnimationCallback(); mNextAppTransitionCallback = startedCallback; mAnimationFinishedCallback = endedCallback; + mOverrideTaskTransition = overrideTaskTransaction; } } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 582aeb36b00ba..6e8257bd72447 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -709,6 +709,13 @@ public class AppTransitionController { applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, voiceInteraction); + for (int i = 0; i < openingApps.size(); ++i) { + openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; + } + for (int i = 0; i < closingApps.size(); ++i) { + closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; + } + final AccessibilityController accessibilityController = mDisplayContent.mWmService.mAccessibilityController; if (accessibilityController != null) { diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index 6df4536847344..65671959e8846 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -253,6 +253,20 @@ public class SafeActivityOptions { throw new SecurityException(msg); } + // Check if the caller is allowed to override any app transition animation. + final boolean overrideTaskTransition = options.getOverrideTaskTransition(); + if (aInfo != null && overrideTaskTransition) { + final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission( + START_TASKS_FROM_RECENTS, callingPid, callingUid); + if (startTasksFromRecentsPerm != PERMISSION_GRANTED) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with overrideTaskTransition=true"; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + // Check permission for remote animations final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); if (adapter != null && supervisor.mService.checkPermission(