Loading core/java/android/window/flags/windowing_frontend.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,17 @@ flag { bug: "291870756" } 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" Loading services/core/java/com/android/server/policy/PhoneWindowManager.java +23 −2 Original line number Diff line number Diff line Loading @@ -530,6 +530,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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; Loading Loading @@ -5464,6 +5472,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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); Loading @@ -5487,6 +5504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 Loading Loading @@ -5522,7 +5540,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } EventLogTags.writeScreenToggled(1); mIsGoingToSleepDefaultDisplay = false; mDefaultDisplayPolicy.setAwake(true); // Since goToSleep performs these functions synchronously, we must Loading Loading @@ -5624,7 +5642,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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) { Loading services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +79 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -33,18 +37,27 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 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; Loading @@ -64,16 +77,27 @@ public class PhoneWindowManagerTests { 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 Loading @@ -98,6 +122,60 @@ public class PhoneWindowManagerTests { verify(mPhoneWindowManager).createHomeDockIntent(); } @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 Loading Loading @@ -130,11 +208,8 @@ public class PhoneWindowManagerTests { 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); } } Loading
core/java/android/window/flags/windowing_frontend.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,17 @@ flag { bug: "291870756" } 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" Loading
services/core/java/com/android/server/policy/PhoneWindowManager.java +23 −2 Original line number Diff line number Diff line Loading @@ -530,6 +530,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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; Loading Loading @@ -5464,6 +5472,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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); Loading @@ -5487,6 +5504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 Loading Loading @@ -5522,7 +5540,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } EventLogTags.writeScreenToggled(1); mIsGoingToSleepDefaultDisplay = false; mDefaultDisplayPolicy.setAwake(true); // Since goToSleep performs these functions synchronously, we must Loading Loading @@ -5624,7 +5642,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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) { Loading
services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +79 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -33,18 +37,27 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 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; Loading @@ -64,16 +77,27 @@ public class PhoneWindowManagerTests { 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 Loading @@ -98,6 +122,60 @@ public class PhoneWindowManagerTests { verify(mPhoneWindowManager).createHomeDockIntent(); } @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 Loading Loading @@ -130,11 +208,8 @@ public class PhoneWindowManagerTests { 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); } }