summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--core/java/android/app/ActivityOptions.java28
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/view/IRecentsAnimationController.aidl54
-rw-r--r--core/java/android/view/IRecentsAnimationRunner.aidl42
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java12
-rw-r--r--core/java/android/view/WindowManager.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java29
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java)6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java (renamed from packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java)51
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java61
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/OverviewProxyService.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java35
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java76
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java51
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java33
-rw-r--r--services/core/java/com/android/server/am/RecentsAnimation.java159
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java19
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowController.java85
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java28
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java385
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java9
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java2
37 files changed, 1253 insertions, 113 deletions
diff --git a/Android.bp b/Android.bp
index 9645ba6b85a1..5590609659db 100644
--- a/Android.bp
+++ b/Android.bp
@@ -332,6 +332,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",
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4bcd677e1f4e..fee58274a5fc 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -207,6 +207,12 @@ public class ActivityOptions {
"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);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b25deeac75cb..6dcecf197ed2 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -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;
@@ -443,8 +444,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);
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
new file mode 100644
index 000000000000..5607b1134e5b
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -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);
+}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
new file mode 100644
index 000000000000..ea6226b3ea69
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -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();
+}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index f39e618e169d..c28c3894482d 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -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
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3bb3a4c17b8f..1c5e87197750 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -98,11 +98,13 @@ public interface WindowManager extends ViewManager {
int DOCKED_BOTTOM = 4;
/** @hide */
- final static String INPUT_CONSUMER_PIP = "pip_input_consumer";
+ String INPUT_CONSUMER_PIP = "pip_input_consumer";
/** @hide */
- final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
+ String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
/** @hide */
- final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+ String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+ /** @hide */
+ String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer";
/**
* Not set up for a transition.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index cc4bc58fbf9d..da50776708b3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -29,4 +29,9 @@ interface ISystemUiProxy {
*/
GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
int maxLayer, boolean useIdentityTransform, int rotation);
+
+ /**
+ * Called when the overview service has started the recents animation.
+ */
+ void onRecentsAnimationStarted();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c9a6ea9939f5..f9e1069cfe95 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -32,6 +32,7 @@ import android.app.AppGlobals;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration.ActivityType;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -48,7 +49,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
import android.util.Log;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -243,10 +247,9 @@ public class ActivityManagerWrapper {
/**
* Starts the recents activity. The caller should manage the thread on which this is called.
*/
- public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options,
- ActivityOptions opts, int userId, Consumer<Boolean> resultCallback,
+ public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver,
+ RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
- Bundle activityOptions = opts != null ? opts.toBundle() : null;
try {
IAssistDataReceiver receiver = null;
if (assistDataReceiver != null) {
@@ -259,8 +262,24 @@ public class ActivityManagerWrapper {
}
};
}
- ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions,
- userId);
+ IRecentsAnimationRunner runner = null;
+ if (animationHandler != null) {
+ runner = new IRecentsAnimationRunner.Stub() {
+ public void onAnimationStart(IRecentsAnimationController controller,
+ RemoteAnimationTarget[] apps) {
+ final RecentsAnimationControllerCompat controllerCompat =
+ new RecentsAnimationControllerCompat(controller);
+ final RemoteAnimationTargetCompat[] appsCompat =
+ RemoteAnimationTargetCompat.wrap(apps);
+ animationHandler.onAnimationStart(controllerCompat, appsCompat);
+ }
+
+ public void onAnimationCanceled() {
+ animationHandler.onAnimationCanceled();
+ }
+ };
+ }
+ ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
if (resultCallback != null) {
resultCallbackHandler.post(new Runnable() {
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
index cd943f62ea9b..7cd6c512b660 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
@@ -22,7 +22,7 @@ import android.os.Bundle;
/**
* Abstract class for assist data receivers.
*/
-public abstract class AssistDataReceiverCompat {
- public abstract void onHandleAssistData(Bundle resultData);
- public abstract void onHandleAssistScreenshot(Bitmap screenshot);
+public abstract class AssistDataReceiver {
+ public void onHandleAssistData(Bundle resultData) {}
+ public void onHandleAssistScreenshot(Bitmap screenshot) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index db4f988a9122..38b8ae8418af 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.pip.phone;
+package com.android.systemui.shared.system;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import android.os.Binder;
import android.os.IBinder;
@@ -29,11 +30,12 @@ import android.view.InputChannel;
import android.view.InputEvent;
import android.view.IWindowManager;
import android.view.MotionEvent;
+import android.view.WindowManagerGlobal;
import java.io.PrintWriter;
/**
- * Manages the input consumer that allows the SystemUI to control the PiP.
+ * Manages the input consumer that allows the SystemUI to directly receive touch input.
*/
public class InputConsumerController {
@@ -55,12 +57,12 @@ public class InputConsumerController {
}
/**
- * Input handler used for the PiP input consumer. Input events are batched and consumed with the
+ * Input handler used for the input consumer. Input events are batched and consumed with the
* SurfaceFlinger vsync.
*/
- private final class PipInputEventReceiver extends BatchedInputEventReceiver {
+ private final class InputEventReceiver extends BatchedInputEventReceiver {
- public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ public InputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper, Choreographer.getSfInstance());
}
@@ -68,7 +70,6 @@ public class InputConsumerController {
public void onInputEvent(InputEvent event, int displayId) {
boolean handled = true;
try {
- // To be implemented for input handling over Pip windows
if (mListener != null && event instanceof MotionEvent) {
MotionEvent ev = (MotionEvent) event;
handled = mListener.onTouchEvent(ev);
@@ -81,15 +82,35 @@ public class InputConsumerController {
private final IWindowManager mWindowManager;
private final IBinder mToken;
+ private final String mName;
- private PipInputEventReceiver mInputEventReceiver;
+ private InputEventReceiver mInputEventReceiver;
private TouchListener mListener;
private RegistrationListener mRegistrationListener;
- public InputConsumerController(IWindowManager windowManager) {
+ /**
+ * @param name the name corresponding to the input consumer that is defined in the system.
+ */
+ public InputConsumerController(IWindowManager windowManager, String name) {
mWindowManager = windowManager;
mToken = new Binder();
- registerInputConsumer();
+ mName = name;
+ }
+
+ /**
+ * @return A controller for the pip input consumer.
+ */
+ public static InputConsumerController getPipInputConsumer() {
+ return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+ INPUT_CONSUMER_PIP);
+ }
+
+ /**
+ * @return A controller for the recents animation input consumer.
+ */
+ public static InputConsumerController getRecentsAnimationInputConsumer() {
+ return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+ INPUT_CONSUMER_RECENTS_ANIMATION);
}
/**
@@ -125,12 +146,12 @@ public class InputConsumerController {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel);
+ mWindowManager.destroyInputConsumer(mName);
+ mWindowManager.createInputConsumer(mToken, mName, inputChannel);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to create PIP input consumer", e);
+ Log.e(TAG, "Failed to create input consumer", e);
}
- mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+ mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
@@ -143,9 +164,9 @@ public class InputConsumerController {
public void unregisterInputConsumer() {
if (mInputEventReceiver != null) {
try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ mWindowManager.destroyInputConsumer(mName);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ Log.e(TAG, "Failed to destroy input consumer", e);
}
mInputEventReceiver.dispose();
mInputEventReceiver = null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
new file mode 100644
index 000000000000..9a7abf82c56c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * 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.systemui.shared.system;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRecentsAnimationController;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+public class RecentsAnimationControllerCompat {
+
+ private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName();
+
+ private IRecentsAnimationController mAnimationController;
+
+ public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
+ mAnimationController = animationController;
+ }
+
+ public ThumbnailData screenshotTask(int taskId) {
+ try {
+ TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
+ return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to screenshot task", e);
+ return new ThumbnailData();
+ }
+ }
+
+ public void setInputConsumerEnabled(boolean enabled) {
+ try {
+ mAnimationController.setInputConsumerEnabled(enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set input consumer enabled state", e);
+ }
+ }
+
+ public void finish(boolean toHome) {
+ try {
+ mAnimationController.finish(toHome);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to finish recents animation", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
new file mode 100644
index 000000000000..bf6179d70a5e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.shared.system;
+
+public interface RecentsAnimationListener {
+
+ /**
+ * Called when the animation into Recents can start. This call is made on the binder thread.
+ */
+ void onAnimationStart(RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps);
+
+ /**
+ * Called when the animation into Recents was canceled. This call is made on the binder thread.
+ */
+ void onAnimationCanceled();
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 244c1b990448..b6e49ae6cc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -77,6 +77,15 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
Binder.restoreCallingIdentity(token);
}
}
+
+ public void onRecentsAnimationStarted() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ notifyRecentsAnimationStarted();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -214,6 +223,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ private void notifyRecentsAnimationStarted() {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onRecentsAnimationStarted();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG_OPS + " state:");
@@ -224,6 +239,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
public interface OverviewProxyListener {
- void onConnectionChanged(boolean isConnected);
+ default void onConnectionChanged(boolean isConnected) {}
+ default void onRecentsAnimationStarted() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 36531bb727a4..24d0126a1494 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,12 +16,10 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -43,6 +41,7 @@ import com.android.systemui.recents.events.component.ExpandPipEvent;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import java.io.PrintWriter;
@@ -174,7 +173,8 @@ public class PipManager implements BasePipManager {
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- mInputConsumerController = new InputConsumerController(mWindowManager);
+ mInputConsumerController = InputConsumerController.getPipInputConsumer();
+ mInputConsumerController.registerInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager);
mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
mInputConsumerController);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 9fb201b82d8c..26fced307bac 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -23,7 +23,6 @@ import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.RemoteAction;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
@@ -43,6 +42,7 @@ import com.android.systemui.pip.phone.PipMediaController.ActionListener;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.component.HidePipMenuEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.shared.system.InputConsumerController;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c0fed342ef44..b25351731a35 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -46,6 +46,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.systemui.R;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9f4d35e19332..b5fa52378660 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -104,6 +104,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
private final OverviewProxyService mOverviewProxyService;
+ private boolean mRecentsAnimationStarted;
// workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -206,10 +207,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
}
- private final OverviewProxyListener mOverviewProxyListener = isConnected -> {
- updateSlippery();
- setDisabledFlags(mDisabledFlags, true);
- setUpSwipeUpOnboarding(isConnected);
+ private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ updateSlippery();
+ setDisabledFlags(mDisabledFlags, true);
+ setUpSwipeUpOnboarding(isConnected);
+ }
+
+ @Override
+ public void onRecentsAnimationStarted() {
+ mRecentsAnimationStarted = true;
+ }
};
public NavigationBarView(Context context, AttributeSet attrs) {
@@ -273,12 +282,26 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
- return super.onTouchEvent(event);
+ return mRecentsAnimationStarted || super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- return mGestureHelper.onInterceptTouchEvent(event);
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mRecentsAnimationStarted = false;
+ } else if (action == MotionEvent.ACTION_UP) {
+ // If the overview proxy service has not started the recents animation then clean up
+ // after it to ensure that the nav bar buttons still work
+ if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
+ try {
+ ActivityManager.getService().cancelRecentsAnimation();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not cancel recents animation");
+ }
+ }
+ }
+ return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
}
public void abortCurrentGesture() {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 427ccba94fd6..220014fff4ab 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -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;
@@ -50,7 +51,9 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.DisplayWindowController;
+import com.android.server.wm.WindowContainerListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -58,7 +61,8 @@ import java.util.ArrayList;
* Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s.
*/
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends ConfigurationContainer<ActivityStack>
+ implements WindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
@@ -100,6 +104,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
// Used in updating the display size
private Point mTmpDisplaySize = new Point();
+ private DisplayWindowController mWindowContainerController;
+
ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
mSupervisor = supervisor;
mDisplayId = displayId;
@@ -108,10 +114,15 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
throw new IllegalStateException("Display does not exist displayId=" + displayId);
}
mDisplay = display;
+ mWindowContainerController = createWindowContainerController();
updateBounds();
}
+ protected DisplayWindowController createWindowContainerController() {
+ return new DisplayWindowController(mDisplayId, this);
+ }
+
void updateBounds() {
mDisplay.getSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
@@ -148,7 +159,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
private void positionChildAt(ActivityStack stack, int position) {
mStacks.remove(stack);
- mStacks.add(getTopInsertPosition(stack, position), stack);
+ final int insertPosition = getTopInsertPosition(stack, position);
+ mStacks.add(insertPosition, stack);
+ mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+ insertPosition);
}
private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
@@ -661,6 +675,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;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 635b38a35889..796290f26305 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -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;
@@ -378,6 +379,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;
@@ -451,6 +453,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;
@@ -5105,23 +5108,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) {
@@ -5138,17 +5134,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);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9d06b0dbab64..4485590c04df 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -642,7 +642,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// TODO: We should probably resolve the windowing mode for the stack on the new display here
// so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
removeFromDisplay();
+ // Reparent the window container before we try to update the position when adding it to
+ // the new display below
mTmpRect2.setEmpty();
+ mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -650,7 +653,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// windows that are no longer visible.
mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
!PRESERVE_WINDOWS);
- mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
}
/**
@@ -994,12 +996,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
insertTaskAtTop(task, null);
return;
}
-
- task = topTask();
- if (task != null) {
- mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
- true /* includingParents */);
- }
}
/**
@@ -1024,12 +1020,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (task != null) {
insertTaskAtBottom(task);
return;
- } else {
- task = bottomTask();
- if (task != null) {
- mWindowContainerController.positionChildAtBottom(
- task.getWindowContainerController(), true /* includingParents */);
- }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bfb563fd93a8..510a3fa47ec5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -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);
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4dc30ddf4b5b..8fd754af1a0f 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -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) {
-
- // The caller specifies that we'd like to be avoided to be moved to the front,
- // so be it!
- mDoResume = false;
- mAvoidMoveToFront = true;
+ 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;
+ }
}
+ } 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");
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
new file mode 100644
index 000000000000..fe576fdaacbe
--- /dev/null
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -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();
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6dc384a8831e..3f49f0cd5c15 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -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);
}
@@ -1802,6 +1806,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
getParent().positionChildAt(position, this, includingParents);
}
+ void positionStackAt(int position, TaskStack child) {
+ mTaskStackContainers.positionChildAt(position, child, false /* includingParents */);
+ layoutAndAssignWindowLayersIfNeeded();
+ }
+
int taskIdFromPoint(int x, int y) {
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -3255,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)
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
new file mode 100644
index 000000000000..ad4957e4fc6f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -0,0 +1,85 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.res.Configuration;
+import android.util.Slog;
+
+/**
+ * Controller for the display container. This is created by activity manager to link activity
+ * displays to the display content they use in window manager.
+ */
+public class DisplayWindowController
+ extends WindowContainerController<DisplayContent, WindowContainerListener> {
+
+ private final int mDisplayId;
+
+ public DisplayWindowController(int displayId, WindowContainerListener listener) {
+ super(listener, WindowManagerService.getInstance());
+ mDisplayId = displayId;
+
+ synchronized (mWindowMap) {
+ // TODO: Convert to setContainer() from DisplayContent once everything is hooked up.
+ // Currently we are not setup to register for config changes.
+ mContainer = mRoot.getDisplayContentOrCreate(displayId);
+ if (mContainer == null) {
+ throw new IllegalArgumentException("Trying to add displayId=" + displayId);
+ }
+ }
+ }
+
+ @Override
+ public void removeContainer() {
+ // TODO: Pipe through from ActivityDisplay to remove the display
+ throw new UnsupportedOperationException("To be implemented");
+ }
+
+ @Override
+ public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ // TODO: Pipe through from ActivityDisplay to update the configuration for the display
+ throw new UnsupportedOperationException("To be implemented");
+ }
+
+ /**
+ * Positions the task stack at the given position in the task stack container.
+ */
+ public void positionChildAt(StackWindowController child, int position) {
+ synchronized (mWindowMap) {
+ if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
+ + " at " + position);
+ if (mContainer == null) {
+ if (DEBUG_STACK) Slog.i(TAG_WM,
+ "positionTaskStackAt: could not find display=" + mContainer);
+ return;
+ }
+ if (child.mContainer == null) {
+ if (DEBUG_STACK) Slog.i(TAG_WM,
+ "positionTaskStackAt: could not find stack=" + this);
+ return;
+ }
+ mContainer.positionStackAt(position, child.mContainer);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{DisplayWindowController displayId=" + mDisplayId + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88b7a11f02fd..281e0a8441e2 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -86,6 +87,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
private boolean mAddInputConsumerHandle;
private boolean mAddPipInputConsumerHandle;
private boolean mAddWallpaperInputConsumerHandle;
+ private boolean mAddRecentsAnimationInputConsumerHandle;
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
@@ -612,7 +614,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
InputConsumerImpl navInputConsumer;
InputConsumerImpl pipInputConsumer;
InputConsumerImpl wallpaperInputConsumer;
- Rect pipTouchableBounds;
+ InputConsumerImpl recentsAnimationInputConsumer;
boolean inDrag;
WallpaperController wallpaperController;
@@ -622,11 +624,13 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY);
pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY);
wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER, DEFAULT_DISPLAY);
+ recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION,
+ DEFAULT_DISPLAY);
mAddInputConsumerHandle = navInputConsumer != null;
mAddPipInputConsumerHandle = pipInputConsumer != null;
mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
+ mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
mTmpRect.setEmpty();
- pipTouchableBounds = mAddPipInputConsumerHandle ? mTmpRect : null;
mDisableWallpaperTouchEvents = false;
this.inDrag = inDrag;
wallpaperController = mService.mRoot.mWallpaperController;
@@ -659,12 +663,28 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
final boolean hasFocus = w == mInputFocus;
final boolean isVisible = w.isVisibleLw();
+ if (mAddRecentsAnimationInputConsumerHandle) {
+ final RecentsAnimationController recentsAnimationController =
+ mService.getRecentsAnimationController();
+ if (recentsAnimationController != null
+ && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+ if (recentsAnimationController.updateInputConsumerForApp(
+ recentsAnimationInputConsumer, hasFocus)) {
+ addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+ mAddRecentsAnimationInputConsumerHandle = false;
+ }
+ // Skip adding the window below regardless of whether there is an input consumer
+ // to handle it
+ return;
+ }
+ }
+
if (w.inPinnedWindowingMode()) {
if (mAddPipInputConsumerHandle
&& (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
// Update the bounds of the Pip input consumer to match the window bounds.
- w.getBounds(pipTouchableBounds);
- pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
+ w.getBounds(mTmpRect);
+ pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
addInputWindowHandle(pipInputConsumer.mWindowHandle);
mAddPipInputConsumerHandle = false;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
new file mode 100644
index 000000000000..c7d4b8ed0f16
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -0,0 +1,385 @@
+/*
+ * 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 android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+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;
+
+ private Rect mTmpRect = new Rect();
+
+ 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();
+ mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ }
+
+ 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 hasInputConsumerForApp(AppWindowToken appToken) {
+ return mInputConsumerEnabled && isAnimatingApp(appToken);
+ }
+
+ boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer,
+ boolean hasFocus) {
+ // Update the input consumer touchable region to match the home app main window
+ final WindowState homeAppMainWindow = mHomeAppToken != null
+ ? mHomeAppToken.findMainWindow()
+ : null;
+ if (homeAppMainWindow != null) {
+ homeAppMainWindow.getBounds(mTmpRect);
+ recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus;
+ recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isHomeAppOverWallpaper() {
+ if (mHomeAppToken == null) {
+ return false;
+ }
+ return mHomeAppToken.windowsCanBeWallpaperTarget();
+ }
+
+ private 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;
+ }
+
+ 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);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8515dcb69970..7d4eafb07fe9 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -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() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2cc96c9ee7b6..deed7f17e4e6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -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
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 10f1c3a37dcf..0512a08c59db 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -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();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1218d3bc1b9b..f2ad6fb7a888 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -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;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8a500b55b936..1f9255a2b20f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -343,9 +343,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;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index de1e7ecb2bb9..4fb239085e5c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -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, " ");
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 96bf49b288c9..10253c570f3f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
+import com.android.server.wm.DisplayWindowController;
import org.mockito.invocation.InvocationOnMock;
import android.app.IApplicationThread;
@@ -345,7 +346,7 @@ public class ActivityTestsBase {
}
}
- private static class TestActivityDisplay extends ActivityDisplay {
+ protected static class TestActivityDisplay extends ActivityDisplay {
private final ActivityStackSupervisor mSupervisor;
TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
@@ -374,6 +375,11 @@ public class ActivityTestsBase {
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
}
+
+ @Override
+ protected DisplayWindowController createWindowContainerController() {
+ return mock(DisplayWindowController.class);
+ }
}
private static WindowManagerService prepareMockWindowManager() {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5a2110258828..24566fcf8f0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -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) {
@@ -676,8 +677,8 @@ public class RecentTasksTest extends ActivityTestsBase {
@Override
public void initialize() {
super.initialize();
- mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
- mOtherDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+ mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+ mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
attachDisplay(mOtherDisplay);
attachDisplay(mDisplay);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index fc7562869490..c6ce7e1188e8 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -68,7 +68,7 @@ public class RunningTasksTest extends ActivityTestsBase {
// Create a number of stacks with tasks (of incrementing active time)
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final SparseArray<ActivityDisplay> displays = new SparseArray<>();
- final ActivityDisplay display = new ActivityDisplay(supervisor, DEFAULT_DISPLAY);
+ final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
displays.put(DEFAULT_DISPLAY, display);
final int numStacks = 2;