Revert "Revert "2/ Add support for remote Recents animation""

This reverts commit 9f8518e532e41ba57916afc49bba72bc23ad3eda.

Reason for revert: Testing relanding changes with ag/3515280

Change-Id: I410bd752c815a5b998a719453def01e00a9d47c8
This commit is contained in:
Winson Chung 2018-01-25 17:46:20 +00:00
parent 59a47deda3
commit e2d721781f
20 changed files with 881 additions and 51 deletions

View File

@ -331,6 +331,8 @@ java_library {
"core/java/android/view/IPinnedStackController.aidl",
"core/java/android/view/IPinnedStackListener.aidl",
"core/java/android/view/IRemoteAnimationRunner.aidl",
"core/java/android/view/IRecentsAnimationController.aidl",
"core/java/android/view/IRecentsAnimationRunner.aidl",
"core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",

View File

@ -206,6 +206,12 @@ public class ActivityOptions {
private static final String KEY_TASK_OVERLAY_CAN_RESUME =
"android.activity.taskOverlayCanResume";
/**
* See {@link #setAvoidMoveToFront()}.
* @hide
*/
private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront";
/**
* Where the split-screen-primary stack should be positioned.
* @hide
@ -307,6 +313,7 @@ public class ActivityOptions {
private boolean mDisallowEnterPictureInPictureWhileLaunching;
private boolean mTaskOverlay;
private boolean mTaskOverlayCanResume;
private boolean mAvoidMoveToFront;
private AppTransitionAnimationSpec mAnimSpecs[];
private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle;
@ -923,6 +930,7 @@ public class ActivityOptions {
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
@ -1239,6 +1247,25 @@ public class ActivityOptions {
return mTaskOverlayCanResume;
}
/**
* Sets whether the activity launched should not cause the activity stack it is contained in to
* be moved to the front as a part of launching.
*
* @hide
*/
public void setAvoidMoveToFront() {
mAvoidMoveToFront = true;
}
/**
* @return whether the activity launch should prevent moving the associated activity stack to
* the front.
* @hide
*/
public boolean getAvoidMoveToFront() {
return mAvoidMoveToFront;
}
/** @hide */
public int getSplitScreenCreateMode() {
return mSplitScreenCreateMode;
@ -1416,6 +1443,7 @@ public class ActivityOptions {
b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
mDisallowEnterPictureInPictureWhileLaunching);

View File

@ -66,6 +66,7 @@ import android.os.PersistableBundle;
import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationDefinition;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
@ -442,8 +443,9 @@ interface IActivityManager {
in Bundle options, int userId);
int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
in Intent intent, in String resolvedType, in Bundle options, int userId);
int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options,
in Bundle activityOptions, int userId);
void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
in IRecentsAnimationRunner recentsAnimationRunner);
void cancelRecentsAnimation();
int startActivityFromRecents(int taskId, in Bundle options);
Bundle getActivityOptions(in IBinder token);
List<IBinder> getAppTasks(in String callingPackage);

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2018 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.view;
import android.app.ActivityManager;
import android.view.IRemoteAnimationFinishedCallback;
import android.graphics.GraphicBuffer;
/**
* Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
* runner control certain aspects of the recents animation, and to notify window manager when the
* animation has completed.
*
* {@hide}
*/
interface IRecentsAnimationController {
/**
* Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
* current set of task ids provided to the handler.
*/
ActivityManager.TaskSnapshot screenshotTask(int taskId);
/**
* Notifies to the system that the animation into Recents should end, and all leashes associated
* with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
* the home activity should be moved to the top. Otherwise, the home activity is hidden and the
* user is returned to the app.
*/
void finish(boolean moveHomeToTop);
/**
* Called by the handler to indicate that the recents animation input consumer should be
* enabled. This is currently used to work around an issue where registering an input consumer
* mid-animation causes the existing motion event chain to be canceled. Instead, the caller
* may register the recents animation input consumer prior to starting the recents animation
* and then enable it mid-animation to start receiving touch events.
*/
void setInputConsumerEnabled(boolean enabled);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 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.view;
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
/**
* Interface that is used to callback from window manager to the process that runs a recents
* animation to start or cancel it.
*
* {@hide}
*/
oneway interface IRecentsAnimationRunner {
/**
* Called when the system is ready for the handler to start animating all the visible tasks.
*/
void onAnimationStart(in IRecentsAnimationController controller,
in RemoteAnimationTarget[] apps);
/**
* Called when the system needs to cancel the current animation. This can be due to the
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*/
void onAnimationCanceled();
}

View File

@ -17,6 +17,7 @@
package android.view;
import android.annotation.IntDef;
import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Parcel;
@ -98,8 +99,14 @@ public class RemoteAnimationTarget implements Parcelable {
*/
public final Rect sourceContainerBounds;
/**
* The window configuration for the target.
*/
public final WindowConfiguration windowConfiguration;
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
WindowConfiguration windowConfig) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
@ -108,6 +115,7 @@ public class RemoteAnimationTarget implements Parcelable {
this.prefixOrderIndex = prefixOrderIndex;
this.position = new Point(position);
this.sourceContainerBounds = new Rect(sourceContainerBounds);
this.windowConfiguration = windowConfig;
}
public RemoteAnimationTarget(Parcel in) {
@ -119,6 +127,7 @@ public class RemoteAnimationTarget implements Parcelable {
prefixOrderIndex = in.readInt();
position = in.readParcelable(null);
sourceContainerBounds = in.readParcelable(null);
windowConfiguration = in.readParcelable(null);
}
@Override
@ -136,6 +145,7 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeInt(prefixOrderIndex);
dest.writeParcelable(position, 0 /* flags */);
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
dest.writeParcelable(windowConfiguration, 0 /* flags */);
}
public static final Creator<RemoteAnimationTarget> CREATOR

View File

@ -16,6 +16,7 @@
package com.android.server.am;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@ -664,6 +665,64 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
&& (mSupervisor.mService.mRunningVoice == null);
}
/**
* @return the stack currently above the home stack. Can be null if there is no home stack, or
* the home stack is already on top.
*/
ActivityStack getStackAboveHome() {
if (mHomeStack == null) {
// Skip if there is no home stack
return null;
}
final int stackIndex = mStacks.indexOf(mHomeStack) + 1;
return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
}
/**
* Adjusts the home stack behind the last visible stack in the display if necessary. Generally
* used in conjunction with {@link #moveHomeStackBehindStack}.
*/
void moveHomeStackBehindBottomMostVisibleStack() {
if (mHomeStack == null) {
// Skip if there is no home stack
return;
}
// Move the home stack to the bottom to not affect the following visibility checks
positionChildAtBottom(mHomeStack);
// Find the next position where the homes stack should be placed
final int numStacks = mStacks.size();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
final ActivityStack stack = mStacks.get(stackNdx);
if (stack == mHomeStack) {
continue;
}
final int winMode = stack.getWindowingMode();
final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
if (stack.shouldBeVisible(null) && isValidWindowingMode) {
// Move the home stack to behind this stack
positionChildAt(mHomeStack, Math.max(0, stackNdx - 1));
break;
}
}
}
/**
* Moves the home stack behind the given {@param stack} if possible. If {@param stack} is not
* currently in the display, then then the home stack is moved to the back. Generally used in
* conjunction with {@link #moveHomeStackBehindBottomMostVisibleStack}.
*/
void moveHomeStackBehindStack(ActivityStack behindStack) {
if (behindStack == null) {
return;
}
positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1));
}
boolean isSleeping() {
return mSleeping;
}

View File

@ -39,6 +39,7 @@ import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@ -377,6 +378,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
import android.view.Gravity;
import android.view.IRecentsAnimationRunner;
import android.view.LayoutInflater;
import android.view.RemoteAnimationDefinition;
import android.view.View;
@ -450,6 +452,7 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.utils.PriorityDump;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RecentsAnimationController;
import com.android.server.wm.WindowManagerService;
import dalvik.system.VMRuntime;
@ -5096,23 +5099,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle options,
Bundle activityOptions, int userId) {
if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
String msg = "Permission Denial: startRecentsActivity() from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " not recent tasks package";
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
final int recentsUid = mRecentTasks.getRecentsComponentUid();
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
final String recentsPackage = recentsComponent.getPackageName();
public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
IRecentsAnimationRunner recentsAnimationRunner) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final int recentsUid = mRecentTasks.getRecentsComponentUid();
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
final String recentsPackage = recentsComponent.getPackageName();
// If provided, kick off the request for the assist data in the background before
// starting the activity
if (assistDataReceiver != null) {
@ -5129,17 +5125,24 @@ public class ActivityManagerService extends IActivityManager.Stub
recentsUid, recentsPackage);
}
final Intent intent = new Intent();
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(recentsComponent);
intent.putExtras(options);
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
mActivityStartController, mWindowManager, mUserController);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
recentsUid);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
.setCallingUid(recentsUid)
.setCallingPackage(recentsPackage)
.setActivityOptions(safeOptions)
.setMayWait(userId)
.execute();
@Override
public void cancelRecentsAnimation() {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
mWindowManager.cancelRecentsAnimation();
}
} finally {
Binder.restoreCallingIdentity(origId);

View File

@ -297,6 +297,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
private RunningTasks mRunningTasks;
final ActivityStackSupervisorHandler mHandler;
final Looper mLooper;
/** Short cut */
WindowManagerService mWindowManager;
@ -581,6 +582,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
mService = service;
mLooper = looper;
mHandler = new ActivityStackSupervisorHandler(looper);
}

View File

@ -302,6 +302,7 @@ class ActivityStarter {
SafeActivityOptions activityOptions;
boolean ignoreTargetSecurity;
boolean componentSpecified;
boolean avoidMoveToFront;
ActivityRecord[] outActivity;
TaskRecord inTask;
String reason;
@ -356,6 +357,7 @@ class ActivityStarter {
userId = 0;
waitResult = null;
mayWait = false;
avoidMoveToFront = false;
}
/**
@ -390,6 +392,7 @@ class ActivityStarter {
userId = request.userId;
waitResult = request.waitResult;
mayWait = request.mayWait;
avoidMoveToFront = request.avoidMoveToFront;
}
}
@ -1485,19 +1488,23 @@ class ActivityStarter {
mDoResume = false;
}
if (mOptions != null && mOptions.getLaunchTaskId() != -1
&& mOptions.getTaskOverlay()) {
r.mTaskOverlay = true;
if (!mOptions.canTaskOverlayResume()) {
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
final ActivityRecord top = task != null ? task.getTopActivity() : null;
if (top != null && top.state != RESUMED) {
if (mOptions != null) {
if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
r.mTaskOverlay = true;
if (!mOptions.canTaskOverlayResume()) {
final TaskRecord task = mSupervisor.anyTaskForIdLocked(
mOptions.getLaunchTaskId());
final ActivityRecord top = task != null ? task.getTopActivity() : null;
if (top != null && top.state != RESUMED) {
// The caller specifies that we'd like to be avoided to be moved to the front,
// so be it!
mDoResume = false;
mAvoidMoveToFront = true;
// The caller specifies that we'd like to be avoided to be moved to the
// front, so be it!
mDoResume = false;
mAvoidMoveToFront = true;
}
}
} else if (mOptions.getAvoidMoveToFront()) {
mAvoidMoveToFront = true;
}
}
@ -1838,7 +1845,7 @@ class ActivityStarter {
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
mTargetStack = intentActivity.getStack();
if (!mMovedToFront && mDoResume) {
if (!mAvoidMoveToFront && !mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
mTargetStack.moveToFront("intentActivityFound");

View File

@ -0,0 +1,159 @@
/*
* Copyright (C) 2018 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 com.android.server.am;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
import android.view.IRecentsAnimationRunner;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import com.android.server.wm.WindowManagerService;
/**
* Manages the recents animation, including the reordering of the stacks for the transition and
* cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
*/
class RecentsAnimation implements RecentsAnimationCallbacks {
private static final String TAG = RecentsAnimation.class.getSimpleName();
private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
private final UserController mUserController;
private final Handler mHandler;
private final Runnable mCancelAnimationRunnable;
// The stack to restore the home stack behind when the animation is finished
private ActivityStack mRestoreHomeBehindStack;
RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
UserController userController) {
mService = am;
mStackSupervisor = stackSupervisor;
mActivityStartController = activityStartController;
mHandler = new Handler(mStackSupervisor.mLooper);
mWindowManager = wm;
mUserController = userController;
mCancelAnimationRunnable = () -> {
// The caller has not finished the animation in a predefined amount of time, so
// force-cancel the animation
mWindowManager.cancelRecentsAnimation();
};
}
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
ComponentName recentsComponent, int recentsUid) {
// Cancel the previous recents animation if necessary
mWindowManager.cancelRecentsAnimation();
final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
if (!hasExistingHomeActivity) {
// No home activity
final ActivityOptions opts = ActivityOptions.makeBasic();
opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
opts.setAvoidMoveToFront();
intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity")
.setCallingUid(recentsUid)
.setCallingPackage(recentsComponent.getPackageName())
.setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
.setMayWait(mUserController.getCurrentUserId())
.execute();
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
// TODO: Maybe wait for app to draw in this particular case?
}
final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
final ActivityDisplay display = homeActivity.getDisplay();
// Save the initial position of the home activity stack to be restored to after the
// animation completes
mRestoreHomeBehindStack = hasExistingHomeActivity
? display.getStackAboveHome()
: null;
// Move the home activity into place for the animation
display.moveHomeStackBehindBottomMostVisibleStack();
// Mark the home activity as launch-behind to bump its visibility for the
// duration of the gesture that is driven by the recents component
homeActivity.mLaunchTaskBehind = true;
// Fetch all the surface controls and pass them to the client to get the animation
// started
mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);
// If we updated the launch-behind state, update the visibility of the activities after we
// fetch the visible tasks to be controlled by the animation
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
// Post a timeout for the animation
mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
}
@Override
public void onAnimationFinished(boolean moveHomeToTop) {
mHandler.removeCallbacks(mCancelAnimationRunnable);
synchronized (mService) {
if (mWindowManager.getRecentsAnimationController() == null) return;
mWindowManager.inSurfaceTransaction(() -> {
mWindowManager.cleanupRecentsAnimation();
// Move the home stack to the front
final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
if (homeActivity == null) {
return;
}
// Restore the launched-behind state
homeActivity.mLaunchTaskBehind = false;
if (moveHomeToTop) {
// Bring the home stack to the front
final ActivityStack homeStack = homeActivity.getStack();
homeStack.mNoAnimActivities.add(homeActivity);
homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
} else {
// Restore the home stack to its previous position
final ActivityDisplay display = homeActivity.getDisplay();
display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
}
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
mStackSupervisor.resumeFocusedStackTopActivityLocked();
});
}
}
}

View File

@ -1508,6 +1508,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mTaskStackContainers.getTopStack();
}
ArrayList<Task> getVisibleTasks() {
return mTaskStackContainers.getVisibleTasks();
}
void onStackWindowingModeChanged(TaskStack stack) {
mTaskStackContainers.onStackWindowingModeChanged(stack);
}
@ -3260,6 +3264,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mSplitScreenPrimaryStack;
}
ArrayList<Task> getVisibleTasks() {
final ArrayList<Task> visibleTasks = new ArrayList<>();
forAllTasks(task -> {
if (task.isVisible()) {
visibleTasks.add(task);
}
});
return visibleTasks;
}
/**
* Adds the stack to this container.
* @see DisplayContent#createStack(int, boolean, StackWindowController)

View File

@ -0,0 +1,373 @@
/*
* Copyright (C) 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 com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.WindowConfiguration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Controls a single instance of the remote driven recents animation. In particular, this allows
* the calling SystemUI to animate the visible task windows as a part of the transition. The remote
* runner is provided an animation controller which allows it to take screenshots and to notify
* window manager when the animation is completed. In addition, window manager may also notify the
* app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
*/
public class RecentsAnimationController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
private static final boolean DEBUG = false;
private final WindowManagerService mService;
private final IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
// We start the RecentsAnimationController in a pending-start state since we need to wait for
// the wallpaper/activity to draw before we can give control to the handler to start animating
// the visible task surfaces
private boolean mPendingStart = true;
// Set when the animation has been canceled
private boolean mCanceled = false;
// Whether or not the input consumer is enabled. The input consumer must be both registered and
// enabled for it to start intercepting touch events.
private boolean mInputConsumerEnabled;
public interface RecentsAnimationCallbacks {
void onAnimationFinished(boolean moveHomeToTop);
}
private final IRecentsAnimationController mController =
new IRecentsAnimationController.Stub() {
@Override
public TaskSnapshot screenshotTask(int taskId) {
if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
return null;
}
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
final Task task = adapter.mTask;
if (task.mTaskId == taskId) {
// TODO: Save this screenshot as the task snapshot?
final Rect taskFrame = new Rect();
task.getBounds(taskFrame);
final GraphicBuffer buffer = SurfaceControl.captureLayers(
task.getSurfaceControl().getHandle(), taskFrame, 1f);
final AppWindowToken topChild = task.getTopChild();
final WindowState mainWindow = topChild.findMainWindow();
return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
mainWindow.mStableInsets,
ActivityManager.isLowRamDeviceStatic() /* reduced */,
1.0f /* scale */);
}
}
return null;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void finish(boolean moveHomeToTop) {
if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
return;
}
}
// Note, the callback will handle its own synchronization, do not lock on WM lock
// prior to calling the callback
mCallbacks.onAnimationFinished(moveHomeToTop);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setInputConsumerEnabled(boolean enabled) {
if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
+ mCanceled);
long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
return;
}
mInputConsumerEnabled = enabled;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.scheduleAnimationLocked();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
/**
* Initializes a new RecentsAnimationController.
*
* @param remoteAnimationRunner The remote runner which should be notified when the animation is
* ready to start or has been canceled
* @param callbacks Callbacks to be made when the animation finishes
* @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
* animation is complete. Will be passed to the callback.
*/
RecentsAnimationController(WindowManagerService service,
IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
int displayId) {
mService = service;
mRunner = remoteAnimationRunner;
mCallbacks = callbacks;
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
if (visibleTasks.isEmpty()) {
cancelAnimation();
return;
}
// Make leashes for each of the visible tasks and add it to the recents animation to be
// started
final int taskCount = visibleTasks.size();
for (int i = 0; i < taskCount; i++) {
final Task task = visibleTasks.get(i);
final WindowConfiguration config = task.getWindowConfiguration();
if (config.tasksAreFloating()
|| config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| config.getActivityType() == ACTIVITY_TYPE_HOME) {
continue;
}
addAnimation(task);
}
// Adjust the wallpaper visibility for the showing home activity
final AppWindowToken recentsComponentAppToken =
dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
if (recentsComponentAppToken != null) {
if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
mHomeAppToken = recentsComponentAppToken;
final WallpaperController wc = dc.mWallpaperController;
if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
}
}
mService.mWindowPlacerLocked.performSurfacePlacement();
}
private void addAnimation(Task task) {
if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
task.commitPendingTransaction();
mPendingAnimations.add(taskAdapter);
}
void startAnimation() {
if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
if (!mPendingStart) {
return;
}
try {
final RemoteAnimationTarget[] appAnimations =
new RemoteAnimationTarget[mPendingAnimations.size()];
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
}
mPendingStart = false;
mRunner.onAnimationStart(mController, appAnimations);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
}
void cancelAnimation() {
if (DEBUG) Log.d(TAG, "cancelAnimation()");
if (mCanceled) {
// We've already canceled the animation
return;
}
mCanceled = true;
try {
mRunner.onAnimationCanceled();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
// Clean up and return to the previous app
mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
}
void cleanupAnimation() {
if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
+ mPendingAnimations.size());
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
}
mPendingAnimations.clear();
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.scheduleAnimationLocked();
}
void checkAnimationReady(WallpaperController wallpaperController) {
if (mPendingStart) {
final boolean wallpaperReady = !isHomeAppOverWallpaper()
|| (wallpaperController.getWallpaperTarget() != null
&& wallpaperController.wallpaperTransitionReady());
if (wallpaperReady) {
mService.getRecentsAnimationController().startAnimation();
}
}
}
boolean isWallpaperVisible(WindowState w) {
return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken
&& isHomeAppOverWallpaper();
}
boolean isHomeAppOverWallpaper() {
if (mHomeAppToken == null) {
return false;
}
return mHomeAppToken.windowsCanBeWallpaperTarget();
}
WindowState getHomeAppMainWindow() {
if (mHomeAppToken == null) {
return null;
}
return mHomeAppToken.findMainWindow();
}
boolean isAnimatingApp(AppWindowToken appToken) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final Task task = mPendingAnimations.get(i).mTask;
for (int j = task.getChildCount() - 1; j >= 0; j--) {
final AppWindowToken app = task.getChildAt(j);
if (app == appToken) {
return true;
}
}
}
return false;
}
boolean isInputConsumerEnabled() {
return mInputConsumerEnabled;
}
private class TaskAnimationAdapter implements AnimationAdapter {
private Task mTask;
private SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
TaskAnimationAdapter(Task task) {
mTask = task;
}
RemoteAnimationTarget createRemoteAnimationApp() {
// TODO: Do we need position and stack bounds?
return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
!mTask.fillsParent(),
mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
mTask.getPrefixOrderIndex(), new Point(), new Rect(),
mTask.getWindowConfiguration());
}
@Override
public boolean getDetachWallpaper() {
return false;
}
@Override
public int getBackgroundColor() {
return 0;
}
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
}
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
cancelAnimation();
}
@Override
public long getDurationHint() {
return 0;
}
@Override
public long getStatusBarTransitionsStartTime() {
return SystemClock.uptimeMillis();
}
}
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
pw.print(innerPrefix); pw.println("mHomeAppToken=" + mHomeAppToken);
}
}

View File

@ -160,7 +160,8 @@ class RemoteAnimationController {
return new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
mainWindow.mWinAnimator.mLastClipRect,
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
task.getWindowConfiguration());
}
private int getMode() {

View File

@ -623,6 +623,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
defaultDisplay.pendingLayoutChanges);
}
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(mWallpaperController);
}
if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
&& !mService.mAppTransition.isReady()) {
// At this point, there was a window with a wallpaper that was force hiding other

View File

@ -62,7 +62,7 @@ class SurfaceAnimator {
* @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
* surfaces in WM. Can be implemented differently during testing.
*/
SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
mAnimatable = animatable;
mService = service;
@ -71,7 +71,8 @@ class SurfaceAnimator {
addAfterPrepareSurfaces);
}
private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback,
private OnAnimationFinishedCallback getFinishedCallback(
@Nullable Runnable animationFinishedCallback,
Consumer<Runnable> addAfterPrepareSurfaces) {
return anim -> {
synchronized (mService.mWindowMap) {
@ -97,7 +98,9 @@ class SurfaceAnimator {
SurfaceControl.openTransaction();
try {
reset(t, true /* destroyLeash */);
animationFinishedCallback.run();
if (animationFinishedCallback != null) {
animationFinishedCallback.run();
}
} finally {
SurfaceControl.mergeToGlobalTransaction(t);
SurfaceControl.closeTransaction();

View File

@ -149,8 +149,17 @@ class WallpaperController {
mFindResults.setUseTopWallpaperAsTarget(true);
}
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
final boolean isRecentsTransitionTarget = (recentsAnimationController != null
&& recentsAnimationController.isWallpaperVisible(w));
if (isRecentsTransitionTarget) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
return true;
} else if (hasWallpaper && w.isOnScreen()
&& (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
@ -199,15 +208,22 @@ class WallpaperController {
}
}
private boolean isWallpaperVisible(WindowState wallpaperTarget) {
private final boolean isWallpaperVisible(WindowState wallpaperTarget) {
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
boolean isAnimatingWithRecentsComponent = recentsAnimationController != null
&& recentsAnimationController.isWallpaperVisible(wallpaperTarget);
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
? wallpaperTarget.mAppToken.isSelfAnimating() : null)
+ " prev=" + mPrevWallpaperTarget);
+ " prev=" + mPrevWallpaperTarget
+ " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
&& wallpaperTarget.mAppToken.isSelfAnimating())))
&& (!wallpaperTarget.mObscured
|| isAnimatingWithRecentsComponent
|| (wallpaperTarget.mAppToken != null
&& wallpaperTarget.mAppToken.isSelfAnimating())))
|| mPrevWallpaperTarget != null;
}
@ -587,6 +603,11 @@ class WallpaperController {
mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
"*** WALLPAPER DRAW TIMEOUT");
// If there was a recents animation in progress, cancel that animation
if (mService.getRecentsAnimationController() != null) {
mService.getRecentsAnimationController().cancelAnimation();
}
return true;
}
return false;

View File

@ -337,9 +337,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/** Returns true if this window container has the input child. */
boolean hasChild(WindowContainer child) {
boolean hasChild(E child) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer current = mChildren.get(i);
final E current = mChildren.get(i);
if (current == child || current.hasChild(child)) {
return true;
}

View File

@ -24,6 +24,8 @@ import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
@ -123,6 +125,7 @@ import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@ -196,6 +199,7 @@ import android.view.IDockedStackListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
import android.view.IRecentsAnimationRunner;
import android.view.IRotationWatcher;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
@ -528,6 +532,7 @@ public class WindowManagerService extends IWindowManager.Stub
IInputMethodManager mInputMethodManager;
AccessibilityController mAccessibilityController;
private RecentsAnimationController mRecentsAnimationController;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
@ -2670,6 +2675,39 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
public void initializeRecentsAnimation(
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) {
synchronized (mWindowMap) {
cancelRecentsAnimation();
mRecentsAnimationController = new RecentsAnimationController(this,
recentsAnimationRunner, callbacks, displayId);
}
}
public RecentsAnimationController getRecentsAnimationController() {
return mRecentsAnimationController;
}
public void cancelRecentsAnimation() {
synchronized (mWindowMap) {
if (mRecentsAnimationController != null) {
// This call will call through to cleanupAnimation() below after the animation is
// canceled
mRecentsAnimationController.cancelAnimation();
}
}
}
public void cleanupRecentsAnimation() {
synchronized (mWindowMap) {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.cleanupAnimation();
mRecentsAnimationController = null;
}
}
}
public void setAppFullscreen(IBinder token, boolean toOpaque) {
synchronized (mWindowMap) {
final AppWindowToken atoken = mRoot.getAppWindowToken(token);
@ -6327,6 +6365,10 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
pw.println(" mLayoutToAnim:");
mAppTransition.dump(pw, " ");
if (mRecentsAnimationController != null) {
pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController);
mRecentsAnimationController.dump(pw, " ");
}
}
}

View File

@ -573,7 +573,8 @@ public class RecentTasksTest extends ActivityTestsBase {
assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
null, 0));
null));
assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation());
}
private void testGetTasksApis(boolean expectCallable) {