Merge changes from topic "presubmit-am-b87ddd3bbf2947f1aa639cddcee6ecb8" into tm-dev

* changes:
  Remove callback before WindowState removal
  Improve startBackNavigation stability
This commit is contained in:
Vadim Caen 2022-03-28 18:53:26 +00:00 committed by Android (Google) Code Review
commit 09f7f6f69b
11 changed files with 491 additions and 267 deletions

View File

@ -149,6 +149,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@ -8398,6 +8399,7 @@ public final class ViewRootImpl implements ViewParent,
return;
}
mRemoved = true;
mOnBackInvokedDispatcher.detachFromWindow();
if (mAdded) {
dispatchDetachedFromWindow();
}
@ -8432,8 +8434,6 @@ public final class ViewRootImpl implements ViewParent,
mAdded = false;
}
unregisterCompatOnBackInvokedCallback();
mOnBackInvokedDispatcher.detachFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@ -10739,6 +10739,12 @@ public final class ViewRootImpl implements ViewParent,
* {@link OnBackInvokedCallback} to be called to the server.
*/
private void registerBackCallbackOnWindow() {
if (OnBackInvokedDispatcher.DEBUG) {
Log.d(OnBackInvokedDispatcher.TAG, TextUtils.formatSimple(
"ViewRootImpl.registerBackCallbackOnWindow. Callback:%s Package:%s "
+ "IWindow:%s Session:%s",
mOnBackInvokedDispatcher, mBasePackageName, mWindow, mWindowSession));
}
mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}

View File

@ -81,7 +81,9 @@ public final class BackNavigationInfo implements Parcelable {
TYPE_DIALOG_CLOSE,
TYPE_RETURN_TO_HOME,
TYPE_CROSS_ACTIVITY,
TYPE_CROSS_TASK})
TYPE_CROSS_TASK,
TYPE_CALLBACK
})
@interface BackTargetType {
}
@ -121,8 +123,8 @@ public final class BackNavigationInfo implements Parcelable {
@Nullable SurfaceControl screenshotSurface,
@Nullable HardwareBuffer screenshotBuffer,
@Nullable WindowConfiguration taskWindowConfiguration,
@NonNull RemoteCallback onBackNavigationDone,
@NonNull IOnBackInvokedCallback onBackInvokedCallback) {
@Nullable RemoteCallback onBackNavigationDone,
@Nullable IOnBackInvokedCallback onBackInvokedCallback) {
mType = type;
mDepartingAnimationTarget = departingAnimationTarget;
mScreenshotSurface = screenshotSurface;
@ -278,7 +280,98 @@ public final class BackNavigationInfo implements Parcelable {
return "TYPE_CROSS_ACTIVITY";
case TYPE_CROSS_TASK:
return "TYPE_CROSS_TASK";
case TYPE_CALLBACK:
return "TYPE_CALLBACK";
}
return String.valueOf(type);
}
/**
* @hide
*/
@SuppressWarnings("UnusedReturnValue") // Builder pattern
public static class Builder {
private int mType = TYPE_UNDEFINED;
@Nullable
private RemoteAnimationTarget mDepartingAnimationTarget = null;
@Nullable
private SurfaceControl mScreenshotSurface = null;
@Nullable
private HardwareBuffer mScreenshotBuffer = null;
@Nullable
private WindowConfiguration mTaskWindowConfiguration = null;
@Nullable
private RemoteCallback mOnBackNavigationDone = null;
@Nullable
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
/**
* @see BackNavigationInfo#getType()
*/
public Builder setType(@BackTargetType int type) {
mType = type;
return this;
}
/**
* @see BackNavigationInfo#getDepartingAnimationTarget
*/
public Builder setDepartingAnimationTarget(
@Nullable RemoteAnimationTarget departingAnimationTarget) {
mDepartingAnimationTarget = departingAnimationTarget;
return this;
}
/**
* @see BackNavigationInfo#getScreenshotSurface
*/
public Builder setScreenshotSurface(@Nullable SurfaceControl screenshotSurface) {
mScreenshotSurface = screenshotSurface;
return this;
}
/**
* @see BackNavigationInfo#getScreenshotHardwareBuffer()
*/
public Builder setScreenshotBuffer(@Nullable HardwareBuffer screenshotBuffer) {
mScreenshotBuffer = screenshotBuffer;
return this;
}
/**
* @see BackNavigationInfo#getTaskWindowConfiguration
*/
public Builder setTaskWindowConfiguration(
@Nullable WindowConfiguration taskWindowConfiguration) {
mTaskWindowConfiguration = taskWindowConfiguration;
return this;
}
/**
* @see BackNavigationInfo#onBackNavigationFinished(boolean)
*/
public Builder setOnBackNavigationDone(@Nullable RemoteCallback onBackNavigationDone) {
mOnBackNavigationDone = onBackNavigationDone;
return this;
}
/**
* @see BackNavigationInfo#getOnBackInvokedCallback
*/
public Builder setOnBackInvokedCallback(
@Nullable IOnBackInvokedCallback onBackInvokedCallback) {
mOnBackInvokedCallback = onBackInvokedCallback;
return this;
}
/**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
return new BackNavigationInfo(mType, mDepartingAnimationTarget, mScreenshotSurface,
mScreenshotBuffer, mTaskWindowConfiguration, mOnBackNavigationDone,
mOnBackInvokedCallback);
}
}
}

View File

@ -73,7 +73,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
public void unregisterOnBackInvokedCallback(
@NonNull OnBackInvokedCallback callback) {
if (DEBUG) {
Log.v(TAG, String.format("Pending unregister %s. Actual=%s", callback,
Log.v(TAG, String.format("Proxy unregister %s. Actual=%s", callback,
mActualDispatcherOwner));
}
synchronized (mLock) {
@ -109,8 +109,8 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
OnBackInvokedDispatcher dispatcher =
mActualDispatcherOwner.getOnBackInvokedDispatcher();
if (DEBUG) {
Log.v(TAG, String.format("Pending transferring %d callbacks to %s", mCallbacks.size(),
dispatcher));
Log.v(TAG, String.format("Proxy: transferring %d pending callbacks to %s",
mCallbacks.size(), dispatcher));
}
for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
int priority = callbackPair.second;
@ -144,7 +144,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
*/
public void reset() {
if (DEBUG) {
Log.v(TAG, "Pending reset callbacks");
Log.v(TAG, "Proxy: reset callbacks");
}
synchronized (mLock) {
mCallbacks.clear();
@ -165,7 +165,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
public void setActualDispatcherOwner(
@Nullable OnBackInvokedDispatcherOwner actualDispatcherOwner) {
if (DEBUG) {
Log.v(TAG, String.format("Pending setActual %s. Current %s",
Log.v(TAG, String.format("Proxy setActual %s. Current %s",
actualDispatcherOwner, mActualDispatcherOwner));
}
synchronized (mLock) {

View File

@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
@ -35,11 +36,11 @@ import java.util.TreeMap;
/**
* Provides window based implementation of {@link OnBackInvokedDispatcher}.
*
* <p>
* Callbacks with higher priorities receive back dispatching first.
* Within the same priority, callbacks receive back dispatching in the reverse order
* in which they are added.
*
* <p>
* When the top priority callback is updated, the new callback is propagated to the Window Manager
* if the window the instance is associated with has been attached. It is allowed to register /
* unregister {@link OnBackInvokedCallback}s before the window is attached, although
@ -166,6 +167,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
mWindowSession.setOnBackInvokedCallback(
mWindow, new OnBackInvokedCallbackWrapper(callback), priority);
}
if (DEBUG && callback == null) {
Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s",
Debug.getCallers(5, " ")));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
}
@ -243,7 +248,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
/**
* Returns if the legacy back behavior should be used.
*
* <p>
* Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered
* {@link OnBackInvokedCallback}.
*/

View File

@ -397,6 +397,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-1717147904": {
"message": "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-1715268616": {
"message": "Last window, removing starting window %s",
"level": "VERBOSE",
@ -1087,6 +1093,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"-1010850753": {
"message": "No focused window, defaulting to top task's window",
"level": "WARN",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-1009117329": {
"message": "isFetchingAppTransitionSpecs=true",
"level": "VERBOSE",
@ -1105,6 +1117,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"-997565097": {
"message": "Focused window found using getFocusedWindowToken",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-993378225": {
"message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s",
"level": "VERBOSE",
@ -1327,6 +1345,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"-767349300": {
"message": "%s: Setting back callback %s. Client IWindow %s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
"-766059044": {
"message": "Display id=%d selected orientation %s (%d), got rotation %s (%d)",
"level": "VERBOSE",
@ -1669,12 +1693,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
"-432881038": {
"message": "startBackNavigation task=%s, topRunningActivity=%s, applicationBackCallback=%s, systemBackCallback=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@ -1885,12 +1903,6 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
"-228813488": {
"message": "%s: Setting back callback %s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
"-208825711": {
"message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
"level": "DEBUG",
@ -2359,6 +2371,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
"250620778": {
"message": "startBackNavigation task=%s, topRunningActivity=%s, applicationBackCallback=%s, systemBackCallback=%s, currentFocus=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"251812577": {
"message": "Register display organizer=%s uid=%d",
"level": "VERBOSE",
@ -2677,6 +2695,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowContainerThumbnail.java"
},
"531891870": {
"message": "Previous Destination is Activity:%s Task:%s removedContainer:%s, backType=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"535103992": {
"message": "Wallpaper may change! Adjusting",
"level": "VERBOSE",
@ -2911,6 +2935,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
"716528224": {
"message": "Focused window found using wmService.getFocusedWindowLocked()",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"726205185": {
"message": "Moving to DESTROYED: %s (destroy skipped)",
"level": "VERBOSE",

View File

@ -44,6 +44,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@ -109,6 +110,7 @@ public class BackAnimationControllerTest {
}
@Test
@Ignore("b/207481538")
public void crossActivity_screenshotAttachedAndVisible() {
SurfaceControl screenshotSurface = new SurfaceControl();
HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);

View File

@ -1798,7 +1798,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (mBackNavigationController == null) {
return null;
}
return mBackNavigationController.startBackNavigation(getTopDisplayFocusedRootTask());
return mBackNavigationController.startBackNavigation(mWindowManager);
}
/**

View File

@ -26,6 +26,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@ -38,6 +39,7 @@ import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
/**
* Controller to handle actions related to the back gesture on the server side.
@ -73,24 +75,24 @@ class BackNavigationController {
* Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
* back gesture animation.
*
* @param task the currently focused {@link Task}.
* @return a {@link BackNavigationInfo} instance containing the required leashes and metadata
* for the animation.
* for the animation, or null if we don't know how to animate the current window and need to
* fallback on dispatching the key event.
*/
@Nullable
BackNavigationInfo startBackNavigation(@NonNull Task task) {
return startBackNavigation(task, null);
BackNavigationInfo startBackNavigation(@NonNull WindowManagerService wmService) {
return startBackNavigation(wmService, null);
}
/**
* @param tx, a transaction to be used for the attaching the animation leash.
* This is used in tests. If null, the object will be initialized with a new {@link
* android.view.SurfaceControl.Transaction}
* @see #startBackNavigation(Task)
* SurfaceControl.Transaction}
* @see #startBackNavigation(WindowManagerService)
*/
@VisibleForTesting
@Nullable
BackNavigationInfo startBackNavigation(@NonNull Task task,
BackNavigationInfo startBackNavigation(WindowManagerService wmService,
@Nullable SurfaceControl.Transaction tx) {
if (tx == null) {
@ -98,88 +100,125 @@ class BackNavigationController {
}
int backType = BackNavigationInfo.TYPE_UNDEFINED;
Task prevTask = task;
Task prevTask = null;
ActivityRecord prev;
WindowContainer<?> removedWindowContainer;
ActivityRecord activityRecord;
WindowContainer<?> removedWindowContainer = null;
ActivityRecord activityRecord = null;
ActivityRecord prevTaskTopActivity = null;
SurfaceControl animationLeashParent;
WindowConfiguration taskWindowConfiguration;
Task task = null;
SurfaceControl animationLeashParent = null;
HardwareBuffer screenshotBuffer = null;
SurfaceControl screenshotSurface;
RemoteAnimationTarget topAppTarget = null;
int prevTaskId;
int prevUserId;
RemoteAnimationTarget topAppTarget;
SurfaceControl animLeash;
IOnBackInvokedCallback applicationCallback = null;
IOnBackInvokedCallback systemCallback = null;
synchronized (task.mWmService.mGlobalLock) {
BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder();
synchronized (wmService.mGlobalLock) {
WindowState window;
WindowConfiguration taskWindowConfiguration;
WindowManagerInternal windowManagerInternal =
LocalServices.getService(WindowManagerInternal.class);
IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
window = wmService.windowForClientLocked(null, focusedWindowToken,
false /* throwOnError */);
// TODO Temp workaround for Sysui until b/221071505 is fixed
WindowState window = task.mWmService.getFocusedWindowLocked();
if (window == null) {
activityRecord = task.topRunningActivity();
removedWindowContainer = activityRecord;
taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
window = task.getWindow(WindowState::isFocused);
} else {
activityRecord = window.mActivityRecord;
removedWindowContainer = activityRecord;
taskWindowConfiguration = window.getWindowConfiguration();
}
if (window != null) {
applicationCallback = window.getApplicationOnBackInvokedCallback();
systemCallback = window.getSystemOnBackInvokedCallback();
}
if (applicationCallback == null && systemCallback == null) {
// Return null when either there's no window, or apps have just initialized and
// have not finished registering callbacks.
return null;
}
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, "
+ "topRunningActivity=%s, applicationBackCallback=%s, "
+ "systemBackCallback=%s",
task, activityRecord, applicationCallback, systemCallback);
// TODO Temp workaround for Sysui until b/221071505 is fixed
if (activityRecord == null && applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
applicationCallback /* onBackInvokedCallback */);
}
// For IME and Home, either a callback is registered, or we do nothing. In both cases,
// we don't need to pass the leashes below.
if (activityRecord == null || task.getDisplayContent().getImeContainer().isVisible()
|| activityRecord.isActivityTypeHome()) {
if (applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
applicationCallback /* onBackInvokedCallback */);
} else {
EmbeddedWindowController.EmbeddedWindow embeddedWindow =
wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken);
if (embeddedWindow != null) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.");
return null;
}
}
prev = task.getActivity(
(r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
// Lets first gather the states of things
// - What is our current window ?
// - Does it has an Activity and a Task ?
// TODO Temp workaround for Sysui until b/221071505 is fixed
if (window != null) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Focused window found using getFocusedWindowToken");
}
if (applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
applicationCallback /* onBackInvokedCallback */);
if (window == null) {
window = wmService.getFocusedWindowLocked();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Focused window found using wmService.getFocusedWindowLocked()");
}
if (window == null) {
// We don't have any focused window, fallback ont the top task of the focused
// display.
ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
"No focused window, defaulting to top task's window");
task = wmService.mAtmService.getTopDisplayFocusedRootTask();
window = task.getWindow(WindowState::isFocused);
}
// Now let's find if this window has a callback from the client side.
IOnBackInvokedCallback applicationCallback = null;
IOnBackInvokedCallback systemCallback = null;
if (window != null) {
activityRecord = window.mActivityRecord;
task = window.getTask();
applicationCallback = window.getApplicationOnBackInvokedCallback();
if (applicationCallback != null) {
backType = BackNavigationInfo.TYPE_CALLBACK;
infoBuilder.setOnBackInvokedCallback(applicationCallback);
} else {
systemCallback = window.getSystemOnBackInvokedCallback();
infoBuilder.setOnBackInvokedCallback(systemCallback);
}
}
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, "
+ "topRunningActivity=%s, applicationBackCallback=%s, "
+ "systemBackCallback=%s, currentFocus=%s",
task, activityRecord, applicationCallback, systemCallback, window);
if (window == null) {
Slog.e(TAG, "Window is null, returning null.");
return null;
}
if (systemCallback == null && applicationCallback == null) {
Slog.e(TAG, "No callback registered, returning null.");
return null;
}
// If we don't need to set up the animation, we return early. This is the case when
// - We have an application callback.
// - We don't have any ActivityRecord or Task to animate.
// - The IME is opened, and we just need to close it.
// - The home activity is the focused activity.
if (backType == BackNavigationInfo.TYPE_CALLBACK
|| activityRecord == null
|| task == null
|| task.getDisplayContent().getImeContainer().isVisible()
|| activityRecord.isActivityTypeHome()) {
return infoBuilder
.setType(backType)
.build();
}
// We don't have an application callback, let's find the destination of the back gesture
Task finalTask = task;
prev = task.getActivity(
(r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity());
if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0)
!= window) {
// Are we the top window of our parent? If not, we are a window on top of the
// activity, we won't close the activity.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
removedWindowContainer = window;
} else if (prev != null) {
// We have another Activity in the same task to go to
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
removedWindowContainer = activityRecord;
} else if (task.returnsToHomeRootTask()) {
prevTask = null;
// Our Task should bring back to home
removedWindowContainer = task;
backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
} else if (activityRecord.isRootOfTask()) {
@ -195,12 +234,42 @@ class BackNavigationController {
backType = BackNavigationInfo.TYPE_CROSS_TASK;
}
}
infoBuilder.setType(backType);
prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
prevUserId = prevTask != null ? prevTask.mUserId : 0;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s. "
+ "Back type is %s", prev != null ? prev.mActivityComponent : null, backType);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s "
+ "removedContainer:%s, backType=%s",
prev != null ? prev.mActivityComponent : null,
prevTask != null ? prevTask.getName() : null,
removedWindowContainer,
BackNavigationInfo.typeToString(backType));
// For now, we only animate when going home.
boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
// Only create a new leash if no leash has been created.
// Otherwise return null for animation target to avoid conflict.
&& !removedWindowContainer.hasCommittedReparentToAnimationLeash();
if (prepareAnimation) {
taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration);
// Prepare a leash to animate the current top window
// TODO(b/220934562): Use surface animator to better manage animation conflicts.
SurfaceControl animLeash = removedWindowContainer.makeAnimationLeash()
.setName("BackPreview Leash for " + removedWindowContainer)
.setHidden(false)
.setBLASTLayer()
.build();
removedWindowContainer.reparentSurfaceControl(tx, animLeash);
animationLeashParent = removedWindowContainer.getAnimationLeashParent();
topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer,
activityRecord,
task, animLeash);
infoBuilder.setDepartingAnimationTarget(topAppTarget);
}
//TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
// implemented. For now we simply have the mBackScreenshots hash map that dumbly
@ -209,101 +278,85 @@ class BackNavigationController {
screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent);
}
// Only create a new leash if no leash has been created.
// Otherwise return null for animation target to avoid conflict.
if (removedWindowContainer.hasCommittedReparentToAnimationLeash()) {
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
task.mBackGestureStarted = true;
// Make launcher show from behind by marking its top activity as visible and
// launch-behind to bump its visibility for the duration of the back gesture.
prevTaskTopActivity = prevTask.getTopNonFinishingActivity();
if (prevTaskTopActivity != null) {
if (!prevTaskTopActivity.mVisibleRequested) {
prevTaskTopActivity.setVisibility(true);
}
prevTaskTopActivity.mLaunchTaskBehind = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to true. Activity=%s",
prevTaskTopActivity);
prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible(
null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
}
}
} // Release wm Lock
// Find a screenshot of the previous activity if we actually have an animation
if (topAppTarget != null && needsScreenshot(backType) && prevTask != null
&& screenshotBuffer == null) {
SurfaceControl.Builder builder = new SurfaceControl.Builder()
.setName("BackPreview Screenshot for " + prev)
.setParent(animationLeashParent)
.setHidden(false)
.setBLASTLayer();
infoBuilder.setScreenshotSurface(builder.build());
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
infoBuilder.setScreenshotBuffer(screenshotBuffer);
// The Animation leash needs to be above the screenshot surface, but the animation leash
// needs to be added before to be in the synchronized block.
tx.setLayer(topAppTarget.leash, 1);
tx.apply();
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
try {
activityRecord.token.linkToDeath(
() -> resetSurfaces(finalRemovedWindowContainer), 0);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death", e);
resetSurfaces(removedWindowContainer);
return null;
}
// Prepare a leash to animate the current top window
// TODO(b/220934562): Use surface animator to better manage animation conflicts.
animLeash = removedWindowContainer.makeAnimationLeash()
.setName("BackPreview Leash for " + removedWindowContainer)
.setHidden(false)
.setBLASTLayer()
.build();
removedWindowContainer.reparentSurfaceControl(tx, animLeash);
animationLeashParent = removedWindowContainer.getAnimationLeashParent();
topAppTarget = new RemoteAnimationTarget(
task.mTaskId,
RemoteAnimationTarget.MODE_CLOSING,
animLeash,
false /* isTransluscent */,
new Rect() /* clipRect */,
new Rect() /* contentInsets */,
activityRecord.getPrefixOrderIndex(),
new Point(0, 0) /* position */,
new Rect() /* localBounds */,
new Rect() /* screenSpaceBounds */,
removedWindowContainer.getWindowConfiguration(),
true /* isNotInRecent */,
null,
null,
task.getTaskInfo(),
false,
activityRecord.windowType);
RemoteCallback onBackNavigationDone = new RemoteCallback(
result -> resetSurfaces(finalRemovedWindowContainer
));
infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
}
return infoBuilder.build();
}
screenshotSurface = new SurfaceControl.Builder()
.setName("BackPreview Screenshot for " + prev)
.setParent(animationLeashParent)
.setHidden(false)
.setBLASTLayer()
.build();
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
task.mBackGestureStarted = true;
// Make launcher show from behind by marking its top activity as visible and
// launch-behind to bump its visibility for the duration of the back gesture.
prevTaskTopActivity = prevTask.getTopNonFinishingActivity();
if (prevTaskTopActivity != null) {
if (!prevTaskTopActivity.mVisibleRequested) {
prevTaskTopActivity.setVisibility(true);
}
prevTaskTopActivity.mLaunchTaskBehind = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to true. Activity=%s",
prevTaskTopActivity);
prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible(
null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
}
}
// Find a screenshot of the previous activity
if (needsScreenshot(backType) && prevTask != null) {
if (screenshotBuffer == null) {
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
}
}
// The Animation leash needs to be above the screenshot surface, but the animation leash
// needs to be added before to be in the synchronized block.
tx.setLayer(topAppTarget.leash, 1);
tx.apply();
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
try {
activityRecord.token.linkToDeath(() -> resetSurfaces(finalRemovedWindowContainer), 0);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death", e);
resetSurfaces(removedWindowContainer);
return null;
}
int finalBackType = backType;
final IOnBackInvokedCallback callback =
applicationCallback != null ? applicationCallback : systemCallback;
ActivityRecord finalPrevTaskTopActivity = prevTaskTopActivity;
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
result, finalRemovedWindowContainer, finalBackType, task,
finalPrevTaskTopActivity));
return new BackNavigationInfo(backType,
topAppTarget,
screenshotSurface,
screenshotBuffer,
taskWindowConfiguration,
onBackNavigationDone,
callback);
@NonNull
private static RemoteAnimationTarget createRemoteAnimationTargetLocked(
WindowContainer<?> removedWindowContainer,
ActivityRecord activityRecord, Task task, SurfaceControl animLeash) {
return new RemoteAnimationTarget(
task.mTaskId,
RemoteAnimationTarget.MODE_CLOSING,
animLeash,
false /* isTransluscent */,
new Rect() /* clipRect */,
new Rect() /* contentInsets */,
activityRecord.getPrefixOrderIndex(),
new Point(0, 0) /* position */,
new Rect() /* localBounds */,
new Rect() /* screenSpaceBounds */,
removedWindowContainer.getWindowConfiguration(),
true /* isNotInRecent */,
null,
null,
task.getTaskInfo(),
false,
activityRecord.windowType);
}
private void onBackNavigationDone(
@ -360,6 +413,9 @@ class BackNavigationController {
}
private boolean needsScreenshot(int backType) {
if (!isScreenshotEnabled()) {
return false;
}
switch (backType) {
case BackNavigationInfo.TYPE_RETURN_TO_HOME:
case BackNavigationInfo.TYPE_DIALOG_CLOSE:

View File

@ -940,12 +940,13 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public void setOnBackInvokedCallback(
IWindow window,
IOnBackInvokedCallback onBackInvokedCallback,
@OnBackInvokedDispatcher.Priority int priority) throws RemoteException {
@OnBackInvokedDispatcher.Priority int priority) {
synchronized (mService.mGlobalLock) {
WindowState windowState = mService.windowForClientLocked(this, window, false);
if (windowState == null) {
Slog.e(TAG_WM,
"setOnBackInvokedCallback(): Can't find window state for window:" + window);
"setOnBackInvokedCallback(): No window state for package:"
+ mPackageName);
} else {
windowState.setOnBackInvokedCallback(onBackInvokedCallback, priority);
}

View File

@ -1125,8 +1125,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
void setOnBackInvokedCallback(
@Nullable IOnBackInvokedCallback onBackInvokedCallback, int priority) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s",
this, onBackInvokedCallback);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s. Client IWindow %s",
this, onBackInvokedCallback, mClient);
if (priority >= 0) {
mApplicationOnBackInvokedCallback = onBackInvokedCallback;
mSystemOnBackInvokedCallback = null;

View File

@ -21,21 +21,27 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.window.BackNavigationInfo.typeToString;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
import android.window.BackEvent;
import android.window.BackNavigationInfo;
import android.window.IOnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.TaskSnapshot;
import com.android.server.LocalServices;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -45,22 +51,27 @@ import org.junit.runner.RunWith;
public class BackNavigationControllerTests extends WindowTestsBase {
private BackNavigationController mBackNavigationController;
private IOnBackInvokedCallback mOnBackInvokedCallback;
private WindowManagerInternal mWindowManagerInternal;
@Before
public void setUp() throws Exception {
mBackNavigationController = new BackNavigationController();
mOnBackInvokedCallback = createBackCallback();
LocalServices.removeServiceForTest(WindowManagerInternal.class);
mWindowManagerInternal = mock(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
mBackNavigationController.setTaskSnapshotController(taskSnapshotController);
}
@Test
public void backTypeHomeWhenBackToLauncher() {
Task task = createTopTaskWithActivity();
registerSystemOnBackInvokedCallback();
public void backNavInfo_HomeWhenBackToLauncher() {
IOnBackInvokedCallback callback = withSystemCallback(createTopTaskWithActivity());
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull();
assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
}
@ -69,12 +80,9 @@ public class BackNavigationControllerTests extends WindowTestsBase {
public void backTypeCrossTaskWhenBackToPreviousTask() {
Task taskA = createTask(mDefaultDisplay);
createActivityRecord(taskA);
Task task = createTopTaskWithActivity();
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
withSystemCallback(createTopTaskWithActivity());
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK));
}
@ -82,48 +90,49 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Test
public void backTypeCrossActivityWhenBackToPreviousActivity() {
Task task = createTopTaskWithActivity();
mAtm.setFocusedTask(task.mTaskId,
createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord);
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
WindowState window = createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
addToWindowMap(window, true);
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
window.setOnBackInvokedCallback(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
assertWithMessage("Activity callback").that(
backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
// Until b/207481538 is implemented, this should be null
assertThat(backNavigationInfo.getScreenshotSurface()).isNull();
assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNull();
}
/**
* Checks that we are able to fill all the field of the {@link BackNavigationInfo} object.
*/
@Test
public void backNavInfoFullyPopulated() {
Task task = createTopTaskWithActivity();
createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
registerSystemOnBackInvokedCallback();
public void backInfoWithNullWindow() {
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertThat(backNavigationInfo).isNull();
}
// We need a mock screenshot so
TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
@Test
public void backInfoWindowWithNoActivity() {
WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER,
"Wallpaper");
addToWindowMap(window, true);
mBackNavigationController.setTaskSnapshotController(taskSnapshotController);
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
window.setOnBackInvokedCallback(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull();
assertThat(backNavigationInfo.getScreenshotSurface()).isNotNull();
assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNotNull();
assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK);
assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
}
@Test
public void preparesForBackToHome() {
Task task = createTopTaskWithActivity();
ActivityRecord activity = task.getTopActivity(false, false);
registerSystemOnBackInvokedCallback();
withSystemCallback(task);
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
}
@ -131,13 +140,52 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Test
public void backTypeCallback() {
Task task = createTopTaskWithActivity();
ActivityRecord activity = task.getTopActivity(false, false);
registerApplicationOnBackInvokedCallback();
IOnBackInvokedCallback appCallback = withAppCallback(task);
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(appCallback);
}
private IOnBackInvokedCallback withSystemCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallback(callback,
OnBackInvokedDispatcher.PRIORITY_SYSTEM);
return callback;
}
private IOnBackInvokedCallback withAppCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallback(callback,
OnBackInvokedDispatcher.PRIORITY_DEFAULT);
return callback;
}
@Nullable
private BackNavigationInfo startBackNavigation() {
return mBackNavigationController.startBackNavigation(mWm, new StubTransaction());
}
@NonNull
private IOnBackInvokedCallback createOnBackInvokedCallback() {
return new IOnBackInvokedCallback.Stub() {
@Override
public void onBackStarted() {
}
@Override
public void onBackProgressed(BackEvent backEvent) {
}
@Override
public void onBackCancelled() {
}
@Override
public void onBackInvoked() {
}
};
}
@NonNull
@ -157,35 +205,18 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// enable OnBackInvokedCallbacks
record.info.applicationInfo.privateFlagsExt |=
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
when(record.mSurfaceControl.isValid()).thenReturn(true);
mAtm.setFocusedTask(task.mTaskId, record);
addToWindowMap(window, true);
return task;
}
private void registerSystemOnBackInvokedCallback() {
mWm.getFocusedWindowLocked().setOnBackInvokedCallback(
mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
}
private void registerApplicationOnBackInvokedCallback() {
mWm.getFocusedWindowLocked().setOnBackInvokedCallback(
mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
}
private IOnBackInvokedCallback createBackCallback() {
return new IOnBackInvokedCallback.Stub() {
@Override
public void onBackStarted() { }
@Override
public void onBackProgressed(BackEvent backEvent) { }
@Override
public void onBackCancelled() { }
@Override
public void onBackInvoked() { }
};
private void addToWindowMap(WindowState window, boolean focus) {
mWm.mWindowMap.put(window.mClient.asBinder(), window);
if (focus) {
doReturn(window.getWindowInfo().token)
.when(mWindowManagerInternal).getFocusedWindowToken();
}
}
}