Merge "Skip sleep-token when switching display which will be on" into main
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 14fb17c..65bf241 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,17 @@
}
flag {
+ name: "skip_sleeping_when_switching_display"
+ namespace: "windowing_frontend"
+ description: "Reduce unnecessary visibility or lifecycle changes when changing fold state"
+ bug: "303241079"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "introduce_smoother_dimmer"
namespace: "windowing_frontend"
description: "Refactor dim to fix flickers"
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 12a5892..f655455 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -530,6 +530,14 @@
// TODO(b/178103325): Track sleep/requested sleep for every display.
volatile boolean mRequestedOrSleepingDefaultDisplay;
+ /**
+ * This is used to check whether to invoke {@link #updateScreenOffSleepToken} when screen is
+ * turned off. E.g. if it is false when screen is turned off and the display is swapping, it
+ * is expected that the screen will be on in a short time. Then it is unnecessary to acquire
+ * screen-off-sleep-token, so it can avoid intermediate visibility or lifecycle changes.
+ */
+ volatile boolean mIsGoingToSleepDefaultDisplay;
+
volatile boolean mRecentsVisible;
volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
volatile boolean mPictureInPictureVisible;
@@ -5470,6 +5478,15 @@
}
mRequestedOrSleepingDefaultDisplay = true;
+ mIsGoingToSleepDefaultDisplay = true;
+
+ // In case startedGoingToSleep is called after screenTurnedOff (the source caller is in
+ // order but the methods run on different threads) and updateScreenOffSleepToken was
+ // skipped. Then acquire sleep token if screen was off.
+ if (!mDefaultDisplayPolicy.isScreenOnFully() && !mDefaultDisplayPolicy.isScreenOnEarly()
+ && com.android.window.flags.Flags.skipSleepingWhenSwitchingDisplay()) {
+ updateScreenOffSleepToken(true /* acquire */, false /* isSwappingDisplay */);
+ }
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
@@ -5493,6 +5510,7 @@
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
mRequestedOrSleepingDefaultDisplay = false;
+ mIsGoingToSleepDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(false);
// We must get this work done here because the power manager will drop
@@ -5528,7 +5546,7 @@
}
EventLogTags.writeScreenToggled(1);
-
+ mIsGoingToSleepDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(true);
// Since goToSleep performs these functions synchronously, we must
@@ -5630,7 +5648,10 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
- updateScreenOffSleepToken(true, isSwappingDisplay);
+ if (!isSwappingDisplay || mIsGoingToSleepDefaultDisplay
+ || !com.android.window.flags.Flags.skipSleepingWhenSwitchingDisplay()) {
+ updateScreenOffSleepToken(true /* acquire */, isSwappingDisplay);
+ }
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 29467f2..a80e2f8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -16,10 +16,14 @@
package com.android.server.policy;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.ADD_OKAY;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -33,18 +37,27 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.PowerManager;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.DisplayPolicy;
+import com.android.server.wm.DisplayRotation;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -64,16 +77,27 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
PhoneWindowManager mPhoneWindowManager;
+ private ActivityTaskManagerInternal mAtmInternal;
+ private Context mContext;
@Before
public void setUp() {
mPhoneWindowManager = spy(new PhoneWindowManager());
spyOn(ActivityManager.getService());
+ mContext = getInstrumentation().getTargetContext();
+ spyOn(mContext);
+ mAtmInternal = mock(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mAtmInternal);
+ mPhoneWindowManager.mActivityTaskManagerInternal = mAtmInternal;
+ LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
}
@After
public void tearDown() {
reset(ActivityManager.getService());
+ reset(mContext);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
}
@Test
@@ -99,6 +123,60 @@
}
@Test
+ public void testScreenTurnedOff() {
+ mSetFlagsRule.enableFlags(com.android.window.flags.Flags
+ .FLAG_SKIP_SLEEPING_WHEN_SWITCHING_DISPLAY);
+ doNothing().when(mPhoneWindowManager).updateSettings(any());
+ doNothing().when(mPhoneWindowManager).initializeHdmiState();
+ final boolean[] isScreenTurnedOff = { false };
+ final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+ doAnswer(invocation -> isScreenTurnedOff[0] = true).when(displayPolicy).screenTurnedOff();
+ doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnEarly();
+ doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnFully();
+
+ mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
+ mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
+ final ActivityTaskManagerInternal.SleepTokenAcquirer tokenAcquirer =
+ mock(ActivityTaskManagerInternal.SleepTokenAcquirer.class);
+ doReturn(tokenAcquirer).when(mAtmInternal).createSleepTokenAcquirer(anyString());
+ final PowerManager pm = mock(PowerManager.class);
+ doReturn(true).when(pm).isInteractive();
+ doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+
+ mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init(
+ new PhoneWindowManager.Injector(mContext,
+ mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);
+ assertThat(isScreenTurnedOff[0]).isFalse();
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
+
+ // Skip sleep-token for non-sleep-screen-off.
+ clearInvocations(tokenAcquirer);
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer, never()).acquire(anyInt(), anyBoolean());
+ assertThat(isScreenTurnedOff[0]).isTrue();
+
+ // Apply sleep-token for sleep-screen-off.
+ mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isTrue();
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY), eq(true));
+
+ mPhoneWindowManager.finishedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
+
+ // Simulate unexpected reversed order: screenTurnedOff -> startedGoingToSleep. The sleep
+ // token can still be acquired.
+ isScreenTurnedOff[0] = false;
+ clearInvocations(tokenAcquirer);
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer, never()).acquire(anyInt(), anyBoolean());
+ assertThat(displayPolicy.isScreenOnEarly()).isFalse();
+ assertThat(displayPolicy.isScreenOnFully()).isFalse();
+ mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY), eq(false));
+ }
+
+ @Test
public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
@@ -130,11 +208,8 @@
private void mockStartDockOrHome() throws Exception {
doNothing().when(ActivityManager.getService()).stopAppSwitches();
- ActivityTaskManagerInternal mMockActivityTaskManagerInternal =
- mock(ActivityTaskManagerInternal.class);
- when(mMockActivityTaskManagerInternal.startHomeOnDisplay(
+ when(mAtmInternal.startHomeOnDisplay(
anyInt(), anyString(), anyInt(), anyBoolean(), anyBoolean())).thenReturn(false);
- mPhoneWindowManager.mActivityTaskManagerInternal = mMockActivityTaskManagerInternal;
mPhoneWindowManager.mUserManagerInternal = mock(UserManagerInternal.class);
}
}