Fix problem with AOD when secondary power groups are awake
Bug: 279845136 Test: atest UnlockedScreenOffAnimationControllerTest PowerManagerServiceTest Change-Id: I129ef065f57c494333cbf8b82d3355344fdc8be1
This commit is contained in:
@@ -55,6 +55,7 @@ interface IPowerManager
|
||||
float getBrightnessConstraint(int constraint);
|
||||
@UnsupportedAppUsage
|
||||
boolean isInteractive();
|
||||
boolean isDisplayInteractive(int displayId);
|
||||
boolean areAutoPowerSaveModesEnabled();
|
||||
boolean isPowerSaveMode();
|
||||
PowerSaveState getPowerSaveState(int serviceType);
|
||||
|
||||
@@ -1150,13 +1150,17 @@ public final class PowerManager {
|
||||
}
|
||||
};
|
||||
|
||||
private final PropertyInvalidatedCache<Void, Boolean> mInteractiveCache =
|
||||
new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
|
||||
private final PropertyInvalidatedCache<Integer, Boolean> mInteractiveCache =
|
||||
new PropertyInvalidatedCache<Integer, Boolean>(MAX_CACHE_ENTRIES,
|
||||
CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
|
||||
@Override
|
||||
public Boolean recompute(Void query) {
|
||||
public Boolean recompute(Integer displayId) {
|
||||
try {
|
||||
return mService.isInteractive();
|
||||
if (displayId == null) {
|
||||
return mService.isInteractive();
|
||||
} else {
|
||||
return mService.isDisplayInteractive(displayId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -1802,6 +1806,18 @@ public final class PowerManager {
|
||||
return mInteractiveCache.query(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interactive state for a specific display, which may not be the same as the
|
||||
* global wakefulness (which is true when any display is awake).
|
||||
*
|
||||
* @param displayId
|
||||
* @return whether the given display is present and interactive, or false
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isInteractive(int displayId) {
|
||||
return mInteractiveCache.query(displayId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this device supports rebooting userspace.
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.database.ContentObserver
|
||||
import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.view.Display
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.WindowManager.fixScale
|
||||
@@ -272,7 +273,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
|
||||
// dispatched, a race condition could make it possible for this callback to be run
|
||||
// as the device is waking up. That results in the AOD UI being shown while we wake
|
||||
// up, with unpredictable consequences.
|
||||
if (!powerManager.isInteractive) {
|
||||
if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY)) {
|
||||
aodUiAnimationPlaying = true
|
||||
|
||||
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.Display
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.jank.InteractionJankMonitor
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -29,6 +30,7 @@ import com.android.systemui.shade.ShadeViewController
|
||||
import com.android.systemui.statusbar.LightRevealScrim
|
||||
import com.android.systemui.statusbar.StatusBarStateControllerImpl
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import junit.framework.Assert.assertFalse
|
||||
import org.junit.After
|
||||
@@ -141,7 +143,7 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testAodUiNotShownIfInteractive() {
|
||||
`when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true)
|
||||
`when`(powerManager.isInteractive).thenReturn(true)
|
||||
`when`(powerManager.isInteractive(eq(Display.DEFAULT_DISPLAY))).thenReturn(true)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
|
||||
controller.startAnimation()
|
||||
@@ -152,6 +154,21 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
|
||||
verify(shadeViewController, never()).showAodUi()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAodUiShownIfGloballyInteractiveButDefaultDisplayNotInteractive() {
|
||||
`when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true)
|
||||
`when`(powerManager.isInteractive()).thenReturn(false)
|
||||
`when`(powerManager.isInteractive(eq(Display.DEFAULT_DISPLAY))).thenReturn(false)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
|
||||
controller.startAnimation()
|
||||
|
||||
verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
|
||||
callbackCaptor.value.run()
|
||||
|
||||
verify(shadeViewController).showAodUi()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoAnimationPlaying_dozeParamsCanNotControlScreenOff() {
|
||||
`when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(false)
|
||||
|
||||
@@ -2079,6 +2079,7 @@ public final class PowerManagerService extends SystemService
|
||||
int opUid, String opPackageName, String details) {
|
||||
mPowerGroups.get(groupId).setWakefulnessLocked(wakefulness, eventTime, uid, reason, opUid,
|
||||
opPackageName, details);
|
||||
mInjector.invalidateIsInteractiveCaches();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -3743,12 +3744,32 @@ public final class PowerManagerService extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInteractiveInternal() {
|
||||
private boolean isGloballyInteractiveInternal() {
|
||||
synchronized (mLock) {
|
||||
return PowerManagerInternal.isInteractive(getGlobalWakefulnessLocked());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInteractiveInternal(int displayId, int uid) {
|
||||
synchronized (mLock) {
|
||||
DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
|
||||
if (displayInfo == null) {
|
||||
Slog.w(TAG, "Did not find DisplayInfo for displayId " + displayId);
|
||||
return false;
|
||||
}
|
||||
if (!displayInfo.hasAccess(uid)) {
|
||||
throw new SecurityException(
|
||||
"uid " + uid + " does not have access to display " + displayId);
|
||||
}
|
||||
PowerGroup powerGroup = mPowerGroups.get(displayInfo.displayGroupId);
|
||||
if (powerGroup == null) {
|
||||
Slog.w(TAG, "Did not find PowerGroup for displayId " + displayId);
|
||||
return false;
|
||||
}
|
||||
return PowerManagerInternal.isInteractive(powerGroup.getWakefulnessLocked());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setLowPowerModeInternal(boolean enabled) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
@@ -5805,7 +5826,18 @@ public final class PowerManagerService extends SystemService
|
||||
public boolean isInteractive() {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return isInteractiveInternal();
|
||||
return isGloballyInteractiveInternal();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public boolean isDisplayInteractive(int displayId) {
|
||||
int uid = Binder.getCallingUid();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return isInteractiveInternal(displayId, uid);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ import android.os.IBinder;
|
||||
import android.os.IWakeLockCallback;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
import android.os.UserHandle;
|
||||
import android.os.test.TestLooper;
|
||||
@@ -117,6 +118,7 @@ import org.mockito.stubbing.Answer;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -150,6 +152,7 @@ public class PowerManagerServiceTest {
|
||||
@Mock private SystemPropertiesWrapper mSystemPropertiesMock;
|
||||
@Mock private AppOpsManager mAppOpsManagerMock;
|
||||
@Mock private LowPowerStandbyController mLowPowerStandbyControllerMock;
|
||||
@Mock private Callable<Void> mInvalidateInteractiveCachesMock;
|
||||
|
||||
@Mock
|
||||
private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
|
||||
@@ -310,7 +313,11 @@ public class PowerManagerServiceTest {
|
||||
|
||||
@Override
|
||||
void invalidateIsInteractiveCaches() {
|
||||
// Avoids an SELinux failure.
|
||||
try {
|
||||
mInvalidateInteractiveCachesMock.call();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2317,6 +2324,140 @@ public class PowerManagerServiceTest {
|
||||
eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), eq(eventTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_isInteractive_nonExistentGroup() {
|
||||
createService();
|
||||
startSystem();
|
||||
|
||||
int nonExistentDisplayGroup = 999;
|
||||
BinderService binderService = mService.getBinderServiceInstance();
|
||||
assertThat(binderService.isDisplayInteractive(nonExistentDisplayGroup)).isFalse();
|
||||
}
|
||||
|
||||
private void testMultiDisplay_isInteractive_returnsCorrectValue(
|
||||
boolean defaultDisplayAwake, boolean secondGroupDisplayAwake) {
|
||||
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
|
||||
// We use a display id that does not match the group id, to make sure we aren't accidentally
|
||||
// confusing display id's and display group id's in the implementation.
|
||||
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 17;
|
||||
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
|
||||
new AtomicReference<>();
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
listener.set(invocation.getArgument(0));
|
||||
return null;
|
||||
}).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
|
||||
|
||||
final DisplayInfo defaultDisplayInfo = new DisplayInfo();
|
||||
defaultDisplayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
|
||||
when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(
|
||||
defaultDisplayInfo);
|
||||
|
||||
final DisplayInfo secondDisplayInfo = new DisplayInfo();
|
||||
secondDisplayInfo.displayGroupId = nonDefaultDisplayGroupId;
|
||||
when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(
|
||||
secondDisplayInfo);
|
||||
|
||||
createService();
|
||||
startSystem();
|
||||
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
|
||||
|
||||
if (!defaultDisplayAwake) {
|
||||
mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP,
|
||||
mClock.now(), 0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
|
||||
}
|
||||
if (!secondGroupDisplayAwake) {
|
||||
mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP,
|
||||
mClock.now(), 0,
|
||||
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
|
||||
}
|
||||
assertThat(PowerManagerInternal.isInteractive(
|
||||
mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP))).isEqualTo(
|
||||
defaultDisplayAwake);
|
||||
assertThat(PowerManagerInternal.isInteractive(
|
||||
mService.getWakefulnessLocked(nonDefaultDisplayGroupId))).isEqualTo(
|
||||
secondGroupDisplayAwake);
|
||||
|
||||
BinderService binderService = mService.getBinderServiceInstance();
|
||||
assertThat(binderService.isInteractive()).isEqualTo(
|
||||
defaultDisplayAwake || secondGroupDisplayAwake);
|
||||
assertThat(binderService.isDisplayInteractive(Display.DEFAULT_DISPLAY)).isEqualTo(
|
||||
defaultDisplayAwake);
|
||||
assertThat(binderService.isDisplayInteractive(nonDefaultDisplay)).isEqualTo(
|
||||
secondGroupDisplayAwake);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_isInteractive_defaultGroupIsAwakeSecondGroupIsAwake() {
|
||||
testMultiDisplay_isInteractive_returnsCorrectValue(true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_isInteractive_defaultGroupIsAwakeSecondGroupIsAsleep() {
|
||||
testMultiDisplay_isInteractive_returnsCorrectValue(true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_isInteractive_defaultGroupIsAsleepSecondGroupIsAwake() {
|
||||
testMultiDisplay_isInteractive_returnsCorrectValue(false, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_isInteractive_bothGroupsAreAsleep() {
|
||||
testMultiDisplay_isInteractive_returnsCorrectValue(false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_defaultGroupWakefulnessChange_causesIsInteractiveInvalidate()
|
||||
throws Exception {
|
||||
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
|
||||
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
|
||||
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
|
||||
new AtomicReference<>();
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
listener.set(invocation.getArgument(0));
|
||||
return null;
|
||||
}).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
|
||||
final DisplayInfo info = new DisplayInfo();
|
||||
info.displayGroupId = nonDefaultDisplayGroupId;
|
||||
when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(info);
|
||||
createService();
|
||||
startSystem();
|
||||
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
|
||||
|
||||
verify(mInvalidateInteractiveCachesMock).call();
|
||||
|
||||
mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP,
|
||||
mClock.now(), 0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
|
||||
|
||||
verify(mInvalidateInteractiveCachesMock, times(2)).call();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDisplay_secondGroupWakefulness_causesIsInteractiveInvalidate()
|
||||
throws Exception {
|
||||
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
|
||||
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
|
||||
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
|
||||
new AtomicReference<>();
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
listener.set(invocation.getArgument(0));
|
||||
return null;
|
||||
}).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
|
||||
final DisplayInfo info = new DisplayInfo();
|
||||
info.displayGroupId = nonDefaultDisplayGroupId;
|
||||
when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(info);
|
||||
createService();
|
||||
startSystem();
|
||||
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
|
||||
|
||||
verify(mInvalidateInteractiveCachesMock).call();
|
||||
|
||||
mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, mClock.now(),
|
||||
0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
|
||||
|
||||
verify(mInvalidateInteractiveCachesMock, times(2)).call();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
|
||||
createService();
|
||||
|
||||
Reference in New Issue
Block a user