summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java77
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java77
4 files changed, 126 insertions, 34 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5a35ca74e5dc..239747a37ec6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3345,6 +3345,11 @@
<!-- The duration in which a recent task is considered in session and should be visible. -->
<integer name="config_activeTaskDurationHours">6</integer>
+ <!-- Whether this device prefers to show snapshot or splash screen on back predict target.
+ When set true, there will create windowless starting surface for the preview target, so it
+ won't affect activity's lifecycle. This should only be disabled on low-ram device. -->
+ <bool name="config_predictShowStartingSurface">true</bool>
+
<!-- default window ShowCircularMask property -->
<bool name="config_windowShowCircularMask">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8fb7c9d16170..96ac86ad9c37 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -393,6 +393,7 @@
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks" />
<java-symbol type="integer" name="config_activeTaskDurationHours" />
<java-symbol type="bool" name="config_windowShowCircularMask" />
+ <java-symbol type="bool" name="config_predictShowStartingSurface" />
<java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b67bc62e52f1..7e5a08081026 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -32,6 +32,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.res.ResourceId;
import android.graphics.Point;
import android.graphics.Rect;
@@ -75,7 +76,7 @@ class BackNavigationController {
private Runnable mPendingAnimation;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
- private AnimationHandler mAnimationHandler;
+ AnimationHandler mAnimationHandler;
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
@@ -642,7 +643,8 @@ class BackNavigationController {
/**
* Create and handling animations status for an open/close animation targets.
*/
- private static class AnimationHandler {
+ static class AnimationHandler {
+ private final boolean mShowWindowlessSurface;
private final WindowManagerService mWindowManagerService;
private BackWindowAnimationAdaptor mCloseAdaptor;
private BackWindowAnimationAdaptor mOpenAdaptor;
@@ -661,6 +663,9 @@ class BackNavigationController {
AnimationHandler(WindowManagerService wms) {
mWindowManagerService = wms;
+ final Context context = wms.mContext;
+ mShowWindowlessSurface = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_predictShowStartingSurface);
}
private static final int UNKNOWN = 0;
private static final int TASK_SWITCH = 1;
@@ -690,7 +695,8 @@ class BackNavigationController {
}
}
- boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) {
+ private boolean composeAnimations(@NonNull WindowContainer close,
+ @NonNull WindowContainer open) {
clearBackAnimateTarget(null /* cleanupTransaction */);
if (close == null || open == null) {
Slog.e(TAG, "reset animation with null target close: "
@@ -974,21 +980,20 @@ class BackNavigationController {
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
return new ScheduleAnimationBuilder(backType, adapter)
.setComposeTarget(currentActivity, previousActivity)
- .setOpeningSnapshot(getActivitySnapshot(previousActivity));
+ .setIsLaunchBehind(false);
case BackNavigationInfo.TYPE_CROSS_TASK:
return new ScheduleAnimationBuilder(backType, adapter)
.setComposeTarget(currentTask, previousTask)
- .setOpeningSnapshot(getTaskSnapshot(previousTask));
+ .setIsLaunchBehind(false);
}
return null;
}
- private class ScheduleAnimationBuilder {
+ class ScheduleAnimationBuilder {
final int mType;
final BackAnimationAdapter mBackAnimationAdapter;
WindowContainer mCloseTarget;
WindowContainer mOpenTarget;
- TaskSnapshot mOpenSnapshot;
boolean mIsLaunchBehind;
ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) {
@@ -1002,11 +1007,6 @@ class BackNavigationController {
return this;
}
- ScheduleAnimationBuilder setOpeningSnapshot(TaskSnapshot snapshot) {
- mOpenSnapshot = snapshot;
- return this;
- }
-
ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) {
mIsLaunchBehind = launchBehind;
return this;
@@ -1017,17 +1017,32 @@ class BackNavigationController {
|| wc.hasChild(mOpenTarget) || wc.hasChild(mCloseTarget);
}
+ /**
+ * Apply preview strategy on the opening target
+ * @param open The opening target.
+ * @param visibleOpenActivity The visible activity in opening target.
+ * @return If the preview strategy is launch behind, returns the Activity that has
+ * launchBehind set, or null otherwise.
+ */
+ private ActivityRecord applyPreviewStrategy(WindowContainer open,
+ ActivityRecord visibleOpenActivity) {
+ if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
+ createStartingSurface(getSnapshot(open));
+ return null;
+ }
+ setLaunchBehind(visibleOpenActivity);
+ return visibleOpenActivity;
+ }
+
Runnable build() {
if (mOpenTarget == null || mCloseTarget == null) {
return null;
}
- final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface();
- final ActivityRecord launchBehindActivity = !shouldLaunchBehind ? null
- : mOpenTarget.asTask() != null
+ final ActivityRecord openActivity = mOpenTarget.asTask() != null
? mOpenTarget.asTask().getTopNonFinishingActivity()
: mOpenTarget.asActivityRecord() != null
? mOpenTarget.asActivityRecord() : null;
- if (shouldLaunchBehind && launchBehindActivity == null) {
+ if (openActivity == null) {
Slog.e(TAG, "No opening activity");
return null;
}
@@ -1035,11 +1050,8 @@ class BackNavigationController {
if (!composeAnimations(mCloseTarget, mOpenTarget)) {
return null;
}
- if (launchBehindActivity != null) {
- setLaunchBehind(launchBehindActivity);
- } else {
- createStartingSurface(mOpenSnapshot);
- }
+ final ActivityRecord launchBehindActivity =
+ applyPreviewStrategy(mOpenTarget, openActivity);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(
launchBehindActivity != null ? triggerBack -> {
@@ -1162,25 +1174,22 @@ class BackNavigationController {
mPendingAnimationBuilder = null;
}
- private static TaskSnapshot getActivitySnapshot(@NonNull ActivityRecord r) {
+ static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
if (!isScreenshotEnabled()) {
return null;
}
- // Check if we have a screenshot of the previous activity, indexed by its
- // component name.
- // TODO return TaskSnapshot when feature complete.
-// final HardwareBuffer hw = r.getTask().getSnapshotForActivityRecord(r);
- return null;
- }
+ if (w.asTask() != null) {
+ final Task task = w.asTask();
+ return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
+ task.mTaskId, task.mUserId, false /* restoreFromDisk */,
+ false /* isLowResolution */);
+ }
- private static TaskSnapshot getTaskSnapshot(Task task) {
- if (!isScreenshotEnabled()) {
+ if (w.asActivityRecord() != null) {
+ // TODO (b/259497289) return TaskSnapshot when feature complete.
return null;
}
- // Don't read from disk!!
- return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
- task.mTaskId, task.mUserId, false /* restoreFromDisk */,
- false /* isLowResolution */);
+ return null;
}
void setWindowManager(WindowManagerService wm) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index b80c3e84198b..d0628f12336c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -26,25 +26,31 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.window.BackNavigationInfo.typeToString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -58,6 +64,7 @@ import android.window.IOnBackInvokedCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedCallbackInfo;
import android.window.OnBackInvokedDispatcher;
+import android.window.TaskSnapshot;
import android.window.WindowOnBackInvokedDispatcher;
import com.android.server.LocalServices;
@@ -66,6 +73,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -408,6 +417,25 @@ public class BackNavigationControllerTests extends WindowTestsBase {
0, navigationObserver.getCount());
}
+
+ /**
+ * Test with
+ * config_predictShowStartingSurface = true
+ */
+ @Test
+ public void testEnableWindowlessSurface() {
+ testPrepareAnimation(true);
+ }
+
+ /**
+ * Test with
+ * config_predictShowStartingSurface = false
+ */
+ @Test
+ public void testDisableWindowlessSurface() {
+ testPrepareAnimation(false);
+ }
+
private IOnBackInvokedCallback withSystemCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
@@ -492,6 +520,55 @@ public class BackNavigationControllerTests extends WindowTestsBase {
doReturn(true).when(kc).isDisplayOccluded(anyInt());
}
+ private void testPrepareAnimation(boolean preferWindowlessSurface) {
+ final TaskSnapshot taskSnapshot = mock(TaskSnapshot.class);
+ final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext));
+ final Resources resourcesSpy = Mockito.spy(contextSpy.getResources());
+
+ when(contextSpy.getResources()).thenReturn(resourcesSpy);
+
+ MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
+ .strictness(Strictness.LENIENT).startMocking();
+ doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any()));
+ when(resourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_predictShowStartingSurface))
+ .thenReturn(preferWindowlessSurface);
+
+ final BackNavigationController.AnimationHandler animationHandler =
+ Mockito.spy(new BackNavigationController.AnimationHandler(mWm));
+ doReturn(true).when(animationHandler).isSupportWindowlessSurface();
+ testWithConfig(animationHandler, preferWindowlessSurface);
+ mockitoSession.finishMocking();
+ }
+
+ private void testWithConfig(BackNavigationController.AnimationHandler animationHandler,
+ boolean preferWindowlessSurface) {
+ final Task task = createTask(mDefaultDisplay);
+ final ActivityRecord bottomActivity = createActivityRecord(task);
+ final ActivityRecord homeActivity = mRootHomeTask.getTopNonFinishingActivity();
+
+ final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toHomeBuilder =
+ animationHandler.prepareAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
+ assertTrue(toHomeBuilder.mIsLaunchBehind);
+ toHomeBuilder.build();
+ verify(animationHandler, never()).createStartingSurface(any());
+
+ // Back to ACTIVITY and TASK have the same logic, just with different target.
+ final ActivityRecord topActivity = createActivityRecord(task);
+ final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toActivityBuilder =
+ animationHandler.prepareAnimation(
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY, mBackAnimationAdapter, task, task,
+ topActivity, bottomActivity);
+ assertFalse(toActivityBuilder.mIsLaunchBehind);
+ toActivityBuilder.build();
+ if (preferWindowlessSurface) {
+ verify(animationHandler).createStartingSurface(any());
+ } else {
+ verify(animationHandler, never()).createStartingSurface(any());
+ }
+ }
+
@NonNull
private Task createTopTaskWithActivity() {
Task task = createTask(mDefaultDisplay);