Merge "Adjust cancel back navigation conditions." into udc-dev am: 67182c205b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21470967

Change-Id: I6ce721595c0c352e14768baf2527a7f101240ad5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Wei Sheng Shih
2023-02-24 04:26:11 +00:00
committed by Automerger Merge Worker
9 changed files with 217 additions and 63 deletions

View File

@@ -65,12 +65,12 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController;
@@ -349,12 +349,13 @@ interface IActivityTaskManager {
/**
* Prepare the back navigation in the server. This setups the leashed for sysui to animate
* the back gesture and returns the data needed for the animation.
* @param focusObserver a remote callback to nofify shell when the focused window lost focus.
* @param navigationObserver a remote callback to nofify shell when the focused window is gone,
or an unexpected transition has happened on the navigation target.
* @param adaptor a remote animation to be run for the back navigation plays the animation.
* @return Returns the back navigation info.
*/
android.window.BackNavigationInfo startBackNavigation(
in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor);
in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/**
* registers a callback to be invoked when the screen is captured.

View File

@@ -29,8 +29,9 @@ import android.content.Context;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -39,7 +40,6 @@ import android.provider.Settings.Global;
import android.util.Log;
import android.util.SparseArray;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -121,23 +121,22 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private IOnBackInvokedCallback mActiveCallback;
@VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
@Override
public void focusGained(IBinder inputToken) { }
@Override
public void focusLost(IBinder inputToken) {
mShellExecutor.execute(() -> {
if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
// If an uninterruptible animation is already in progress, we should ignore
// this due to it may cause focus lost. (alpha = 0)
return;
final RemoteCallback mNavigationObserver = new RemoteCallback(
new RemoteCallback.OnResultListener() {
@Override
public void onResult(@Nullable Bundle result) {
mShellExecutor.execute(() -> {
if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
// If an uninterruptible animation is already in progress, we should
// ignore this due to it may cause focus lost. (alpha = 0)
return;
}
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
setTriggerBack(false);
onGestureFinished(false);
});
}
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
setTriggerBack(false);
onGestureFinished(false);
});
}
};
private final BackAnimationBackground mAnimationBackground;
@@ -351,7 +350,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
try {
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
mFocusObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
onBackNavigationInfoReceived(mBackNavigationInfo);
} catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException);

View File

@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -40,7 +39,6 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
@@ -341,8 +339,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController.setTriggerBack(true); // Fake trigger back
// In case the focus has been changed.
IBinder token = mock(IBinder.class);
mController.mFocusObserver.focusLost(token);
mController.mNavigationObserver.sendResult(null);
mShellExecutor.flushAll();
verify(mAnimatorCallback).onBackCancelled();

View File

@@ -202,6 +202,7 @@ import android.os.Parcel;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -228,7 +229,6 @@ import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
@@ -1853,11 +1853,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public BackNavigationInfo startBackNavigation(
IWindowFocusObserver observer, BackAnimationAdapter adapter) {
RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()");
return mBackNavigationController.startBackNavigation(observer, adapter);
return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
}
/**

View File

@@ -253,7 +253,7 @@ public class AppTransitionController {
ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) {
if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringTransition()) {
tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController

View File

@@ -41,7 +41,6 @@ import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowInsets;
@@ -66,11 +65,11 @@ import java.util.function.Consumer;
class BackNavigationController {
private static final String TAG = "BackNavigationController";
private WindowManagerService mWindowManagerService;
private IWindowFocusObserver mFocusObserver;
private boolean mBackAnimationInProgress;
private @BackNavigationInfo.BackTargetType int mLastBackType;
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
private AnimationHandler mAnimationHandler;
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
@@ -86,6 +85,11 @@ class BackNavigationController {
return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
}
// Notify focus window changed
void onFocusChanged(WindowState newFocus) {
mNavigationMonitor.onFocusWindowChanged(newFocus);
}
/**
* Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
* back gesture animation.
@@ -96,13 +100,12 @@ class BackNavigationController {
*/
@VisibleForTesting
@Nullable
BackNavigationInfo startBackNavigation(
IWindowFocusObserver observer, BackAnimationAdapter adapter) {
BackNavigationInfo startBackNavigation(@NonNull RemoteCallback navigationObserver,
BackAnimationAdapter adapter) {
if (!sPredictBackEnable) {
return null;
}
final WindowManagerService wmService = mWindowManagerService;
mFocusObserver = observer;
int backType = BackNavigationInfo.TYPE_UNDEFINED;
@@ -202,9 +205,7 @@ class BackNavigationController {
backType = BackNavigationInfo.TYPE_CALLBACK;
}
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
if (mFocusObserver != null) {
window.registerFocusObserver(mFocusObserver);
}
mNavigationMonitor.startMonitor(window, navigationObserver);
}
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -228,10 +229,8 @@ class BackNavigationController {
|| currentActivity.isActivityTypeHome()
|| currentActivity.mHasSceneTransition) {
infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK);
final WindowState finalFocusedWindow = window;
infoBuilder.setOnBackNavigationDone(new RemoteCallback(result ->
onBackNavigationDone(result, finalFocusedWindow,
BackNavigationInfo.TYPE_CALLBACK)));
onBackNavigationDone(result, BackNavigationInfo.TYPE_CALLBACK)));
mLastBackType = BackNavigationInfo.TYPE_CALLBACK;
return infoBuilder.build();
}
@@ -334,16 +333,19 @@ class BackNavigationController {
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
if (finalRemovedWindowContainer != null) {
final int finalBackType = backType;
final WindowState finalFocusedWindow = window;
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
result, finalFocusedWindow, finalBackType));
result, finalBackType));
infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
}
mLastBackType = backType;
return infoBuilder.build();
}
boolean isWaitBackTransition() {
boolean isMonitoringTransition() {
return isWaitBackTransition() || mNavigationMonitor.isMonitoring();
}
private boolean isWaitBackTransition() {
return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition;
}
@@ -363,11 +365,23 @@ class BackNavigationController {
*/
boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
ArraySet<ActivityRecord> closeApps) {
if (!isWaitBackTransition()) {
if (!isMonitoringTransition()) {
return false;
}
mTmpCloseApps.addAll(closeApps);
boolean result = false;
final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps);
if (!matchAnimationTargets) {
mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
}
mTmpCloseApps.clear();
return matchAnimationTargets;
}
boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps,
ArraySet<ActivityRecord> closeApps) {
if (!isWaitBackTransition()) {
return false;
}
// Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
// mOpeningApps if there is no visibility change.
if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
@@ -386,10 +400,76 @@ class BackNavigationController {
closeApps.removeAt(i);
}
}
result = true;
return true;
}
return false;
}
private static class NavigationMonitor {
// The window which triggering the back navigation.
private WindowState mNavigatingWindow;
private RemoteCallback mObserver;
void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
mNavigatingWindow = window;
mObserver = observer;
}
void stopMonitor() {
mNavigatingWindow = null;
mObserver = null;
}
boolean isMonitoring() {
return mNavigatingWindow != null && mObserver != null;
}
/**
* Notify focus window changed during back navigation. This will cancel the gesture for
* scenarios like: a system window popup, or when an activity add a new window.
*
* This method should only be used to check window-level change, otherwise it may cause
* misjudgment in multi-window mode. For example: in split-screen, when user is
* navigating on the top task, bottom task can start a new task, which will gain focus for
* a short time, but we should not cancel the navigation.
*/
private void onFocusWindowChanged(WindowState newFocus) {
if (!isMonitoring() || !atSameDisplay(newFocus)) {
return;
}
// Keep navigating if either new focus == navigating window or null.
if (newFocus != null && newFocus != mNavigatingWindow
&& (newFocus.mActivityRecord == null
|| (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) {
EventLogTags.writeWmBackNaviCanceled("focusWindowChanged");
mObserver.sendResult(null /* result */);
}
}
/**
* Notify an unexpected transition has happened during back navigation.
*/
private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
ArrayList<WindowContainer> closing) {
if (!isMonitoring()) {
return;
}
final ArrayList<WindowContainer> all = new ArrayList<>(opening);
all.addAll(closing);
for (WindowContainer app : all) {
if (app.hasChild(mNavigatingWindow)) {
EventLogTags.writeWmBackNaviCanceled("transitionHappens");
mObserver.sendResult(null /* result */);
break;
}
}
}
private boolean atSameDisplay(WindowState newFocus) {
final int navigatingDisplayId = mNavigatingWindow.getDisplayId();
return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId;
}
mTmpCloseApps.clear();
return result;
}
// For shell transition
@@ -403,8 +483,7 @@ class BackNavigationController {
* animations, and shouldn't join next transition.
*/
boolean containsBackAnimationTargets(Transition transition) {
if (!mAnimationHandler.mComposed
|| (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) {
if (!isMonitoringTransition()) {
return false;
}
final ArraySet<WindowContainer> targets = transition.mParticipants;
@@ -420,19 +499,19 @@ class BackNavigationController {
mTmpCloseApps.add(wc);
}
}
final boolean result = mAnimationHandler.containsBackAnimationTargets(
mTmpOpenApps, mTmpCloseApps);
if (result) {
mAnimationHandler.mOpenTransitionTargetMatch =
mAnimationHandler.containTarget(mTmpOpenApps, true);
final boolean matchAnimationTargets = isWaitBackTransition()
&& (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK)
&& mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
if (!matchAnimationTargets) {
mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
}
mTmpOpenApps.clear();
mTmpCloseApps.clear();
return result;
return matchAnimationTargets;
}
boolean isMonitorTransitionTarget(WindowContainer wc) {
if (!mAnimationHandler.mComposed || !mAnimationHandler.mWaitTransition) {
if (!isWaitBackTransition()) {
return false;
}
return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
@@ -951,16 +1030,13 @@ class BackNavigationController {
}
}
private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) {
private void onBackNavigationDone(Bundle result, int backType) {
boolean triggerBack = result != null && result.getBoolean(
BackNavigationInfo.KEY_TRIGGER_BACK);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ "triggerBack=%b", backType, triggerBack);
if (mFocusObserver != null) {
focusedWindow.unregisterFocusObserver(mFocusObserver);
mFocusObserver = null;
}
mNavigationMonitor.stopMonitor();
mBackAnimationInProgress = false;
mShowWallpaper = false;
}

View File

@@ -3809,6 +3809,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
mAtmService.mBackNavigationController.onFocusChanged(newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.

View File

@@ -66,6 +66,8 @@ option java_package com.android.server.wm
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
# Back navigation.
31100 wm_back_navi_canceled (Reason|3)
# IME surface parent is updated.
32003 imf_update_ime_parent (surface name|3)

View File

@@ -17,8 +17,11 @@
package com.android.server.wm;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.window.BackNavigationInfo.typeToString;
@@ -42,8 +45,10 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.WindowManager;
import android.window.BackAnimationAdapter;
import android.window.BackMotionEvent;
@@ -320,6 +325,64 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(backNavigationInfo).isNull();
}
@Test
public void testTransitionHappensCancelNavigation() {
// Create a floating task and a fullscreen task, then navigating on fullscreen task.
// The navigation should not been cancelled when transition happens on floating task, and
// only be cancelled when transition happens on the navigating task.
final Task floatingTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD);
final ActivityRecord baseFloatingActivity = createActivityRecord(floatingTask);
final Task fullscreenTask = createTopTaskWithActivity();
withSystemCallback(fullscreenTask);
final ActivityRecord baseFullscreenActivity = fullscreenTask.getTopMostActivity();
final CountDownLatch navigationObserver = new CountDownLatch(1);
startBackNavigation(navigationObserver);
final ArraySet<ActivityRecord> opening = new ArraySet<>();
final ArraySet<ActivityRecord> closing = new ArraySet<>();
final ActivityRecord secondFloatingActivity = createActivityRecord(floatingTask);
opening.add(secondFloatingActivity);
closing.add(baseFloatingActivity);
mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
assertEquals("Transition happen on an irrelevant task, callback should not been called",
1, navigationObserver.getCount());
// Create a new activity above navigation target, the transition should cancel navigation.
final ActivityRecord topFullscreenActivity = createActivityRecord(fullscreenTask);
opening.clear();
closing.clear();
opening.add(topFullscreenActivity);
closing.add(baseFullscreenActivity);
mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
assertEquals("Transition happen on navigation task, callback should have been called",
0, navigationObserver.getCount());
}
@Test
public void testWindowFocusChangeCancelNavigation() {
Task task = createTopTaskWithActivity();
withSystemCallback(task);
WindowState focusWindow = task.getTopVisibleAppMainWindow();
final CountDownLatch navigationObserver = new CountDownLatch(1);
startBackNavigation(navigationObserver);
mBackNavigationController.onFocusChanged(null);
assertEquals("change focus to null, callback should not have been called",
1, navigationObserver.getCount());
mBackNavigationController.onFocusChanged(focusWindow);
assertEquals("change focus back, callback should not have been called",
1, navigationObserver.getCount());
WindowState newWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlayWindow");
addToWindowMap(newWindow, true);
mBackNavigationController.onFocusChanged(newWindow);
assertEquals("Focus change, callback should have been called",
0, navigationObserver.getCount());
}
private IOnBackInvokedCallback withSystemCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
@@ -336,7 +399,14 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Nullable
private BackNavigationInfo startBackNavigation() {
return mBackNavigationController.startBackNavigation(null, mBackAnimationAdapter);
return mBackNavigationController.startBackNavigation(
createNavigationObserver(null), mBackAnimationAdapter);
}
@Nullable
private BackNavigationInfo startBackNavigation(CountDownLatch navigationObserverLatch) {
return mBackNavigationController.startBackNavigation(
createNavigationObserver(navigationObserverLatch), mBackAnimationAdapter);
}
@NonNull
@@ -371,6 +441,14 @@ public class BackNavigationControllerTests extends WindowTestsBase {
};
}
private RemoteCallback createNavigationObserver(CountDownLatch latch) {
return new RemoteCallback(result -> {
if (latch != null) {
latch.countDown();
}
});
}
private Task initHomeActivity() {
final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
task.forAllLeafTasks((t) -> {