summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/os/PowerManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt19
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java143
6 files changed, 217 insertions, 9 deletions
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 6f4cdcef266a..be7c1e5c0333 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -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);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 38e331c4ac0d..d1063f647c4f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -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.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cdf66526ab63..592d1aa8f43f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 3c644a5e1dc6..ea534bbd0794 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -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()
@@ -153,6 +155,21 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
}
@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)
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f8954b7c7f95..b8c5b3f5524a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -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);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index ae3ceb1203f8..933f00231313 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -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
@@ -2318,6 +2325,140 @@ public class PowerManagerServiceTest {
}
@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();
startSystem();