Merge "Cancel any recents animation whenever a display's stack order changes" into pi-dev
This commit is contained in:
@@ -78,9 +78,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
|
||||
int mDisplayId;
|
||||
Display mDisplay;
|
||||
|
||||
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
|
||||
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
|
||||
/**
|
||||
* All of the stacks on this display. Order matters, topmost stack is in front of all other
|
||||
* stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
|
||||
* changing the list should also call {@link #onStackOrderChanged()}.
|
||||
*/
|
||||
private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
|
||||
private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
|
||||
|
||||
/** Array of all UIDs that are present on the display. */
|
||||
private IntArray mDisplayAccessUIDs = new IntArray();
|
||||
@@ -145,6 +149,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
|
||||
mStacks.remove(stack);
|
||||
removeStackReferenceIfNeeded(stack);
|
||||
mSupervisor.mService.updateSleepIfNeededLocked();
|
||||
onStackOrderChanged();
|
||||
}
|
||||
|
||||
void positionChildAtTop(ActivityStack stack) {
|
||||
@@ -163,6 +168,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
|
||||
mStacks.add(insertPosition, stack);
|
||||
mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
|
||||
insertPosition);
|
||||
onStackOrderChanged();
|
||||
}
|
||||
|
||||
private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
|
||||
@@ -770,6 +776,30 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
|
||||
mSleeping = asleep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be notified whenever the stack order in the display changes. Currently
|
||||
* only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
|
||||
* current animation when the system state changes.
|
||||
*/
|
||||
void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
|
||||
if (!mStackOrderChangedCallbacks.contains(listener)) {
|
||||
mStackOrderChangedCallbacks.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously registered stack order change listener.
|
||||
*/
|
||||
void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
|
||||
mStackOrderChangedCallbacks.remove(listener);
|
||||
}
|
||||
|
||||
private void onStackOrderChanged() {
|
||||
for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
|
||||
mStackOrderChangedCallbacks.get(i).onStackOrderChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
|
||||
final String myPrefix = prefix + " ";
|
||||
@@ -806,4 +836,11 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the order of the stacks in the display changes.
|
||||
*/
|
||||
interface OnStackOrderChangedListener {
|
||||
void onStackOrderChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5291,14 +5291,9 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int recentsUid;
|
||||
final String recentsPackage;
|
||||
final List<IBinder> topVisibleActivities;
|
||||
synchronized (this) {
|
||||
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
|
||||
recentsPackage = recentsComponent.getPackageName();
|
||||
recentsUid = mRecentTasks.getRecentsComponentUid();
|
||||
topVisibleActivities = mStackSupervisor.getTopVisibleActivities();
|
||||
final int recentsUid = mRecentTasks.getRecentsComponentUid();
|
||||
|
||||
// Start a new recents animation
|
||||
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
|
||||
@@ -5314,13 +5309,14 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
@Override
|
||||
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
|
||||
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
|
||||
final long callingUid = Binder.getCallingUid();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (this) {
|
||||
// Cancel the recents animation synchronously (do not hold the WM lock)
|
||||
mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
|
||||
? REORDER_MOVE_TO_ORIGINAL_POSITION
|
||||
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation");
|
||||
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
|
||||
@@ -49,7 +49,8 @@ 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 {
|
||||
class RecentsAnimation implements RecentsAnimationCallbacks,
|
||||
ActivityDisplay.OnStackOrderChangedListener {
|
||||
private static final String TAG = RecentsAnimation.class.getSimpleName();
|
||||
// TODO (b/73188263): Reset debugging flags
|
||||
private static final boolean DEBUG = true;
|
||||
@@ -140,13 +141,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
recentsUid, recentsComponent.getPackageName());
|
||||
}
|
||||
|
||||
final ActivityDisplay display;
|
||||
if (hasExistingActivity) {
|
||||
// Move the recents activity into place for the animation if it is not top most
|
||||
display = targetActivity.getDisplay();
|
||||
display.moveStackBehindBottomMostVisibleStack(targetStack);
|
||||
mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
|
||||
if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
|
||||
+ display.getStackAbove(targetStack));
|
||||
+ mDefaultDisplay.getStackAbove(targetStack));
|
||||
|
||||
// If there are multiple tasks in the target stack (ie. the home stack, with 3p
|
||||
// and default launchers coexisting), then move the task to the top as a part of
|
||||
@@ -173,7 +172,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
|
||||
targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
|
||||
mTargetActivityType).getTopActivity();
|
||||
display = targetActivity.getDisplay();
|
||||
|
||||
// TODO: Maybe wait for app to draw in this particular case?
|
||||
|
||||
@@ -190,7 +188,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
|
||||
"startRecentsActivity");
|
||||
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
|
||||
this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
|
||||
this, mDefaultDisplay.mDisplayId,
|
||||
mStackSupervisor.mRecentTasks.getRecentTaskIds());
|
||||
|
||||
// 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
|
||||
@@ -198,6 +197,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
|
||||
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
|
||||
targetActivity);
|
||||
|
||||
// Register for stack order changes
|
||||
mDefaultDisplay.registerStackOrderChangedListener(this);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failed to start recents activity", e);
|
||||
throw e;
|
||||
@@ -219,6 +221,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
mAssistDataRequester = null;
|
||||
}
|
||||
|
||||
// Unregister for stack order changes
|
||||
mDefaultDisplay.unregisterStackOrderChangedListener(this);
|
||||
|
||||
if (mWindowManager.getRecentsAnimationController() == null) return;
|
||||
|
||||
// Just to be sure end the launch hint in case the target activity was never launched.
|
||||
@@ -316,6 +321,14 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStackOrderChanged() {
|
||||
// If the activity display stack order changes, cancel any running recents animation in
|
||||
// place
|
||||
mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
|
||||
"stackOrderChanged");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only when the animation should be canceled prior to starting.
|
||||
*/
|
||||
|
||||
@@ -6092,14 +6092,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
&& (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
|
||||
&& event.getRepeatCount() == 0;
|
||||
|
||||
// Cancel any pending remote recents animations before handling the button itself. In the
|
||||
// case where we are going home and the recents animation has already started, just cancel
|
||||
// the recents animation, leaving the home stack in place for the pending start activity
|
||||
if (isNavBarVirtKey && !down && !canceled) {
|
||||
boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME;
|
||||
mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey);
|
||||
}
|
||||
|
||||
// Handle special keys.
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK: {
|
||||
|
||||
@@ -565,6 +565,47 @@ public class ActivityStackTests extends ActivityTestsBase {
|
||||
false /* displaySleeping */, false /* expected*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackOrderChangedOnRemoveStack() throws Exception {
|
||||
StackOrderChangedListener listener = new StackOrderChangedListener();
|
||||
mDefaultDisplay.registerStackOrderChangedListener(listener);
|
||||
try {
|
||||
mDefaultDisplay.removeChild(mStack);
|
||||
} finally {
|
||||
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
|
||||
}
|
||||
assertTrue(listener.changed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackOrderChangedOnAddPositionStack() throws Exception {
|
||||
mDefaultDisplay.removeChild(mStack);
|
||||
|
||||
StackOrderChangedListener listener = new StackOrderChangedListener();
|
||||
mDefaultDisplay.registerStackOrderChangedListener(listener);
|
||||
try {
|
||||
mDefaultDisplay.addChild(mStack, 0);
|
||||
} finally {
|
||||
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
|
||||
}
|
||||
assertTrue(listener.changed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackOrderChangedOnPositionStack() throws Exception {
|
||||
StackOrderChangedListener listener = new StackOrderChangedListener();
|
||||
try {
|
||||
final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
|
||||
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
|
||||
true /* onTop */);
|
||||
mDefaultDisplay.registerStackOrderChangedListener(listener);
|
||||
mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
|
||||
} finally {
|
||||
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
|
||||
}
|
||||
assertTrue(listener.changed);
|
||||
}
|
||||
|
||||
private void verifyShouldSleepActivities(boolean focusedStack,
|
||||
boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
|
||||
mSupervisor.mFocusedStack = focusedStack ? mStack : null;
|
||||
@@ -578,4 +619,13 @@ public class ActivityStackTests extends ActivityTestsBase {
|
||||
|
||||
assertEquals(expected, mStack.shouldSleepActivities());
|
||||
}
|
||||
|
||||
private class StackOrderChangedListener implements ActivityDisplay.OnStackOrderChangedListener {
|
||||
boolean changed = false;
|
||||
|
||||
@Override
|
||||
public void onStackOrderChanged() {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.am;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.MediumTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import com.android.server.AttributeCache;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* atest FrameworksServicesTests:RecentsAnimationTest
|
||||
*/
|
||||
@MediumTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RecentsAnimationTest extends ActivityTestsBase {
|
||||
private static final int TEST_CALLING_PID = 3;
|
||||
|
||||
private Context mContext = InstrumentationRegistry.getContext();
|
||||
private ActivityManagerService mService;
|
||||
private ComponentName mRecentsComponent;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
|
||||
mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
|
||||
AttributeCache.init(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelAnimationOnStackOrderChange() throws Exception {
|
||||
ActivityStack fullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
|
||||
ActivityRecord recentsActivity = new ActivityBuilder(mService)
|
||||
.setComponent(mRecentsComponent)
|
||||
.setCreateTask(true)
|
||||
.setStack(recentsStack)
|
||||
.build();
|
||||
ActivityStack fullscreenStack2 = mService.mStackSupervisor.getDefaultDisplay().createStack(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
ActivityRecord fsActivity = new ActivityBuilder(mService)
|
||||
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
|
||||
.setCreateTask(true)
|
||||
.setStack(fullscreenStack2)
|
||||
.build();
|
||||
doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
|
||||
|
||||
// Start the recents animation
|
||||
Intent recentsIntent = new Intent();
|
||||
recentsIntent.setComponent(mRecentsComponent);
|
||||
mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
|
||||
|
||||
fullscreenStack.moveToFront("Activity start");
|
||||
|
||||
// Ensure that the recents animation was canceled
|
||||
verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
|
||||
eq(REORDER_KEEP_IN_PLACE), any());
|
||||
}
|
||||
|
||||
private class MyTestActivityManagerService extends TestActivityManagerService {
|
||||
MyTestActivityManagerService(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecentTasks createRecentTasks() {
|
||||
RecentTasks recents = mock(RecentTasks.class);
|
||||
doReturn(mRecentsComponent).when(recents).getRecentsComponent();
|
||||
System.out.println(mRecentsComponent);
|
||||
return recents;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user