Merge "Cancel any recents animation whenever a display's stack order changes" into pi-dev

This commit is contained in:
Winson Chung
2018-05-08 15:51:04 +00:00
committed by Android (Google) Code Review
6 changed files with 227 additions and 24 deletions

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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.
*/

View File

@@ -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: {

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}