Loading services/core/java/com/android/server/power/PowerManagerService.java +102 −16 Original line number Diff line number Diff line Loading @@ -40,13 +40,17 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.SynchronousUserSwitchObserver; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; Loading @@ -62,6 +66,7 @@ import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.BatterySaverPolicyConfig; import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; Loading Loading @@ -277,6 +282,18 @@ public final class PowerManagerService extends SystemService */ private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L; /** * Apps targeting Android U and above need to define * {@link android.Manifest.permission#TURN_SCREEN_ON} in their manifest for * {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP} to have any effect. * Note that most applications should use {@link android.R.attr#turnScreenOn} or * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the * previous foreground app from being resumed first when the screen turns on. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long REQUIRE_TURN_SCREEN_ON_PERMISSION = 216114297L; /** Reason ID for holding display suspend blocker. */ private static final String HOLDING_DISPLAY_SUSPEND_BLOCKER = "holding display"; Loading Loading @@ -304,8 +321,9 @@ public final class PowerManagerService extends SystemService private final SystemPropertiesWrapper mSystemProperties; private final Clock mClock; private final Injector mInjector; private final PermissionCheckerWrapper mPermissionCheckerWrapper; private final PowerPropertiesWrapper mPowerPropertiesWrapper; private AppOpsManager mAppOpsManager; private LightsManager mLightsManager; private BatteryManagerInternal mBatteryManagerInternal; private DisplayManagerInternal mDisplayManagerInternal; Loading Loading @@ -1029,11 +1047,66 @@ public final class PowerManagerService extends SystemService return new LowPowerStandbyController(context, looper); } AppOpsManager createAppOpsManager(Context context) { return context.getSystemService(AppOpsManager.class); PermissionCheckerWrapper createPermissionCheckerWrapper() { return PermissionChecker::checkPermissionForDataDelivery; } PowerPropertiesWrapper createPowerPropertiesWrapper() { return new PowerPropertiesWrapper() { @Override public boolean waive_target_sdk_check_for_turn_screen_on() { return PowerProperties.waive_target_sdk_check_for_turn_screen_on().orElse( false); } @Override public boolean permissionless_turn_screen_on() { return PowerProperties.permissionless_turn_screen_on().orElse(false); } }; } } /** Interface for checking an app op permission */ @VisibleForTesting interface PermissionCheckerWrapper { /** * Checks whether a given data access chain described by the given {@link AttributionSource} * has a given permission and whether the app op that corresponds to this permission * is allowed. * See {@link PermissionChecker#checkPermissionForDataDelivery} for more details. * * @param context Context for accessing resources. * @param permission The permission to check. * @param pid The process id for which to check. Use {@link PermissionChecker#PID_UNKNOWN} * if the PID is not known. * @param attributionSource the permission identity * @param message A message describing the reason the permission was checked * @return The permission check result which is any of * {@link PermissionChecker#PERMISSION_GRANTED}, * {@link PermissionChecker#PERMISSION_SOFT_DENIED}, * or {@link PermissionChecker#PERMISSION_HARD_DENIED}. */ int checkPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, int pid, @NonNull AttributionSource attributionSource, @Nullable String message); } /** Interface for querying {@link PowerProperties} */ @VisibleForTesting interface PowerPropertiesWrapper { /** * Waives the minimum target-sdk check for android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP * and only allows the flag for apps holding android.permission.TURN_SCREEN_ON */ boolean waive_target_sdk_check_for_turn_screen_on(); /** * Allows apps to turn the screen on with android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP * without being granted android.app.AppOpsManager#OP_TURN_SCREEN_ON. */ boolean permissionless_turn_screen_on(); } final Constants mConstants; private native void nativeInit(); Loading Loading @@ -1086,8 +1159,8 @@ public final class PowerManagerService extends SystemService Looper.getMainLooper()); mInattentiveSleepWarningOverlayController = mInjector.createInattentiveSleepWarningController(); mAppOpsManager = injector.createAppOpsManager(mContext); mPermissionCheckerWrapper = mInjector.createPermissionCheckerWrapper(); mPowerPropertiesWrapper = mInjector.createPowerPropertiesWrapper(); mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener(); Loading Loading @@ -1562,19 +1635,29 @@ public final class PowerManagerService extends SystemService } @RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true) private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid) { private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid, int opPid) { if (opPackageName == null) { return false; } if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TURN_SCREEN_ON) == PackageManager.PERMISSION_GRANTED) { if (PermissionChecker.PERMISSION_GRANTED == mPermissionCheckerWrapper.checkPermissionForDataDelivery(mContext, android.Manifest.permission.TURN_SCREEN_ON, opPid, new AttributionSource(opUid, opPackageName, /* attributionTag= */ null), /* message= */ "ACQUIRE_CAUSES_WAKEUP for " + opPackageName)) { Slog.i(TAG, "Allowing device wake-up from app " + opPackageName); return true; } // CompatChanges#isChangeEnabled() returns false for apps with targetSdk < UDC and ensures // backwards compatibility. // waive_target_sdk_check_for_turn_screen_on() returns false by default and may be set to // true on form factors with a more strict policy (e.g. TV) if (!CompatChanges.isChangeEnabled(REQUIRE_TURN_SCREEN_ON_PERMISSION, opUid) && !mPowerPropertiesWrapper.waive_target_sdk_check_for_turn_screen_on()) { Slog.i(TAG, "Allowing device wake-up without android.permission.TURN_SCREEN_ON for " + opPackageName); return true; } if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { if (mPowerPropertiesWrapper.permissionless_turn_screen_on()) { Slog.d(TAG, "Device wake-up allowed by debug.power.permissionless_turn_screen_on"); return true; } Loading @@ -1589,6 +1672,7 @@ public final class PowerManagerService extends SystemService && isScreenLock(wakeLock)) { String opPackageName; int opUid; int opPid = PermissionChecker.PID_UNKNOWN; if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) { WorkSource workSource = wakeLock.mWorkSource; WorkChain workChain = getFirstNonEmptyWorkChain(workSource); Loading @@ -1603,10 +1687,12 @@ public final class PowerManagerService extends SystemService } else { opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; opPid = wakeLock.mOwnerPid; } Integer powerGroupId = wakeLock.getPowerGroupId(); // powerGroupId is null if the wakelock associated display is no longer available if (powerGroupId != null && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid)) { if (powerGroupId != null && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid, opPid)) { if (powerGroupId == Display.INVALID_DISPLAY_GROUP) { // wake up all display groups if (DEBUG_SPEW) { Loading services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +86 −32 Original line number Diff line number Diff line Loading @@ -18,8 +18,6 @@ package com.android.server.power; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; Loading @@ -44,6 +42,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -51,13 +50,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.attention.AttentionManagerInternal; import android.compat.testing.PlatformCompatChangeRule; import android.content.AttributionSource; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.PermissionChecker; import android.content.res.Resources; import android.hardware.SensorManager; import android.hardware.display.AmbientDisplayConfiguration; Loading Loading @@ -95,7 +95,6 @@ import com.android.server.lights.LightsManager; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.PowerManagerService.BatteryReceiver; import com.android.server.power.PowerManagerService.BinderService; import com.android.server.power.PowerManagerService.Injector; import com.android.server.power.PowerManagerService.NativeWrapper; import com.android.server.power.PowerManagerService.UserSwitchedReceiver; import com.android.server.power.PowerManagerService.WakeLock; Loading @@ -105,9 +104,14 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; Loading Loading @@ -150,12 +154,13 @@ public class PowerManagerServiceTest { @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; @Mock private SystemPropertiesWrapper mSystemPropertiesMock; @Mock private AppOpsManager mAppOpsManagerMock; @Mock private LowPowerStandbyController mLowPowerStandbyControllerMock; @Mock private Callable<Void> mInvalidateInteractiveCachesMock; @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock; @Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper; @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); private PowerManagerService mService; private ContextWrapper mContextSpy; Loading Loading @@ -231,7 +236,7 @@ public class PowerManagerServiceTest { } private PowerManagerService createService() { mService = new PowerManagerService(mContextSpy, new Injector() { mService = new PowerManagerService(mContextSpy, new PowerManagerService.Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, Loading Loading @@ -327,8 +332,13 @@ public class PowerManagerServiceTest { } @Override AppOpsManager createAppOpsManager(Context context) { return mAppOpsManagerMock; PowerManagerService.PermissionCheckerWrapper createPermissionCheckerWrapper() { return mPermissionCheckerWrapperMock; } @Override PowerManagerService.PowerPropertiesWrapper createPowerPropertiesWrapper() { return mPowerPropertiesWrapper; } }); return mService; Loading Loading @@ -589,6 +599,7 @@ public class PowerManagerServiceTest { } @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() { createService(); startSystem(); Loading @@ -597,11 +608,12 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); when(mContextSpy.checkCallingOrSelfPermission( android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( PackageManager.PERMISSION_GRANTED); AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_GRANTED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); // First, ensure that a normal full wake lock does not cause a wakeup int flags = PowerManager.FULL_WAKE_LOCK; Loading @@ -626,7 +638,8 @@ public class PowerManagerServiceTest { } @Test public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() { @DisableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnAllowed() { createService(); startSystem(); forceSleep(); Loading @@ -634,30 +647,71 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ERRORED); AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); // verify that the wakeup is allowed for apps targeting older sdks, and therefore won't have // the TURN_SCREEN_ON permission granted doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); // Verify that flag has no effect when OP_TURN_SCREEN_ON is not allowed int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); } else { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); } @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() { createService(); startSystem(); forceSleep(); IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); doReturn(false).when(mPowerPropertiesWrapper).permissionless_turn_screen_on(); // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); } when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); when(mContextSpy.checkCallingOrSelfPermission( android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( PackageManager.PERMISSION_DENIED); @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnDenied() { createService(); startSystem(); forceSleep(); // Verify that the flag has no effect when OP_TURN_SCREEN_ON is allowed but // android.permission.TURN_SCREEN_ON is denied flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(true).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { Loading Loading
services/core/java/com/android/server/power/PowerManagerService.java +102 −16 Original line number Diff line number Diff line Loading @@ -40,13 +40,17 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.SynchronousUserSwitchObserver; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; Loading @@ -62,6 +66,7 @@ import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.BatterySaverPolicyConfig; import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; Loading Loading @@ -277,6 +282,18 @@ public final class PowerManagerService extends SystemService */ private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L; /** * Apps targeting Android U and above need to define * {@link android.Manifest.permission#TURN_SCREEN_ON} in their manifest for * {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP} to have any effect. * Note that most applications should use {@link android.R.attr#turnScreenOn} or * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the * previous foreground app from being resumed first when the screen turns on. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long REQUIRE_TURN_SCREEN_ON_PERMISSION = 216114297L; /** Reason ID for holding display suspend blocker. */ private static final String HOLDING_DISPLAY_SUSPEND_BLOCKER = "holding display"; Loading Loading @@ -304,8 +321,9 @@ public final class PowerManagerService extends SystemService private final SystemPropertiesWrapper mSystemProperties; private final Clock mClock; private final Injector mInjector; private final PermissionCheckerWrapper mPermissionCheckerWrapper; private final PowerPropertiesWrapper mPowerPropertiesWrapper; private AppOpsManager mAppOpsManager; private LightsManager mLightsManager; private BatteryManagerInternal mBatteryManagerInternal; private DisplayManagerInternal mDisplayManagerInternal; Loading Loading @@ -1029,11 +1047,66 @@ public final class PowerManagerService extends SystemService return new LowPowerStandbyController(context, looper); } AppOpsManager createAppOpsManager(Context context) { return context.getSystemService(AppOpsManager.class); PermissionCheckerWrapper createPermissionCheckerWrapper() { return PermissionChecker::checkPermissionForDataDelivery; } PowerPropertiesWrapper createPowerPropertiesWrapper() { return new PowerPropertiesWrapper() { @Override public boolean waive_target_sdk_check_for_turn_screen_on() { return PowerProperties.waive_target_sdk_check_for_turn_screen_on().orElse( false); } @Override public boolean permissionless_turn_screen_on() { return PowerProperties.permissionless_turn_screen_on().orElse(false); } }; } } /** Interface for checking an app op permission */ @VisibleForTesting interface PermissionCheckerWrapper { /** * Checks whether a given data access chain described by the given {@link AttributionSource} * has a given permission and whether the app op that corresponds to this permission * is allowed. * See {@link PermissionChecker#checkPermissionForDataDelivery} for more details. * * @param context Context for accessing resources. * @param permission The permission to check. * @param pid The process id for which to check. Use {@link PermissionChecker#PID_UNKNOWN} * if the PID is not known. * @param attributionSource the permission identity * @param message A message describing the reason the permission was checked * @return The permission check result which is any of * {@link PermissionChecker#PERMISSION_GRANTED}, * {@link PermissionChecker#PERMISSION_SOFT_DENIED}, * or {@link PermissionChecker#PERMISSION_HARD_DENIED}. */ int checkPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, int pid, @NonNull AttributionSource attributionSource, @Nullable String message); } /** Interface for querying {@link PowerProperties} */ @VisibleForTesting interface PowerPropertiesWrapper { /** * Waives the minimum target-sdk check for android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP * and only allows the flag for apps holding android.permission.TURN_SCREEN_ON */ boolean waive_target_sdk_check_for_turn_screen_on(); /** * Allows apps to turn the screen on with android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP * without being granted android.app.AppOpsManager#OP_TURN_SCREEN_ON. */ boolean permissionless_turn_screen_on(); } final Constants mConstants; private native void nativeInit(); Loading Loading @@ -1086,8 +1159,8 @@ public final class PowerManagerService extends SystemService Looper.getMainLooper()); mInattentiveSleepWarningOverlayController = mInjector.createInattentiveSleepWarningController(); mAppOpsManager = injector.createAppOpsManager(mContext); mPermissionCheckerWrapper = mInjector.createPermissionCheckerWrapper(); mPowerPropertiesWrapper = mInjector.createPowerPropertiesWrapper(); mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener(); Loading Loading @@ -1562,19 +1635,29 @@ public final class PowerManagerService extends SystemService } @RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true) private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid) { private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid, int opPid) { if (opPackageName == null) { return false; } if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TURN_SCREEN_ON) == PackageManager.PERMISSION_GRANTED) { if (PermissionChecker.PERMISSION_GRANTED == mPermissionCheckerWrapper.checkPermissionForDataDelivery(mContext, android.Manifest.permission.TURN_SCREEN_ON, opPid, new AttributionSource(opUid, opPackageName, /* attributionTag= */ null), /* message= */ "ACQUIRE_CAUSES_WAKEUP for " + opPackageName)) { Slog.i(TAG, "Allowing device wake-up from app " + opPackageName); return true; } // CompatChanges#isChangeEnabled() returns false for apps with targetSdk < UDC and ensures // backwards compatibility. // waive_target_sdk_check_for_turn_screen_on() returns false by default and may be set to // true on form factors with a more strict policy (e.g. TV) if (!CompatChanges.isChangeEnabled(REQUIRE_TURN_SCREEN_ON_PERMISSION, opUid) && !mPowerPropertiesWrapper.waive_target_sdk_check_for_turn_screen_on()) { Slog.i(TAG, "Allowing device wake-up without android.permission.TURN_SCREEN_ON for " + opPackageName); return true; } if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { if (mPowerPropertiesWrapper.permissionless_turn_screen_on()) { Slog.d(TAG, "Device wake-up allowed by debug.power.permissionless_turn_screen_on"); return true; } Loading @@ -1589,6 +1672,7 @@ public final class PowerManagerService extends SystemService && isScreenLock(wakeLock)) { String opPackageName; int opUid; int opPid = PermissionChecker.PID_UNKNOWN; if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) { WorkSource workSource = wakeLock.mWorkSource; WorkChain workChain = getFirstNonEmptyWorkChain(workSource); Loading @@ -1603,10 +1687,12 @@ public final class PowerManagerService extends SystemService } else { opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; opPid = wakeLock.mOwnerPid; } Integer powerGroupId = wakeLock.getPowerGroupId(); // powerGroupId is null if the wakelock associated display is no longer available if (powerGroupId != null && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid)) { if (powerGroupId != null && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid, opPid)) { if (powerGroupId == Display.INVALID_DISPLAY_GROUP) { // wake up all display groups if (DEBUG_SPEW) { Loading
services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +86 −32 Original line number Diff line number Diff line Loading @@ -18,8 +18,6 @@ package com.android.server.power; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; Loading @@ -44,6 +42,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -51,13 +50,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.attention.AttentionManagerInternal; import android.compat.testing.PlatformCompatChangeRule; import android.content.AttributionSource; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.PermissionChecker; import android.content.res.Resources; import android.hardware.SensorManager; import android.hardware.display.AmbientDisplayConfiguration; Loading Loading @@ -95,7 +95,6 @@ import com.android.server.lights.LightsManager; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.PowerManagerService.BatteryReceiver; import com.android.server.power.PowerManagerService.BinderService; import com.android.server.power.PowerManagerService.Injector; import com.android.server.power.PowerManagerService.NativeWrapper; import com.android.server.power.PowerManagerService.UserSwitchedReceiver; import com.android.server.power.PowerManagerService.WakeLock; Loading @@ -105,9 +104,14 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; Loading Loading @@ -150,12 +154,13 @@ public class PowerManagerServiceTest { @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; @Mock private SystemPropertiesWrapper mSystemPropertiesMock; @Mock private AppOpsManager mAppOpsManagerMock; @Mock private LowPowerStandbyController mLowPowerStandbyControllerMock; @Mock private Callable<Void> mInvalidateInteractiveCachesMock; @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock; @Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper; @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); private PowerManagerService mService; private ContextWrapper mContextSpy; Loading Loading @@ -231,7 +236,7 @@ public class PowerManagerServiceTest { } private PowerManagerService createService() { mService = new PowerManagerService(mContextSpy, new Injector() { mService = new PowerManagerService(mContextSpy, new PowerManagerService.Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, Loading Loading @@ -327,8 +332,13 @@ public class PowerManagerServiceTest { } @Override AppOpsManager createAppOpsManager(Context context) { return mAppOpsManagerMock; PowerManagerService.PermissionCheckerWrapper createPermissionCheckerWrapper() { return mPermissionCheckerWrapperMock; } @Override PowerManagerService.PowerPropertiesWrapper createPowerPropertiesWrapper() { return mPowerPropertiesWrapper; } }); return mService; Loading Loading @@ -589,6 +599,7 @@ public class PowerManagerServiceTest { } @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() { createService(); startSystem(); Loading @@ -597,11 +608,12 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); when(mContextSpy.checkCallingOrSelfPermission( android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( PackageManager.PERMISSION_GRANTED); AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_GRANTED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); // First, ensure that a normal full wake lock does not cause a wakeup int flags = PowerManager.FULL_WAKE_LOCK; Loading @@ -626,7 +638,8 @@ public class PowerManagerServiceTest { } @Test public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() { @DisableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnAllowed() { createService(); startSystem(); forceSleep(); Loading @@ -634,30 +647,71 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ERRORED); AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); // verify that the wakeup is allowed for apps targeting older sdks, and therefore won't have // the TURN_SCREEN_ON permission granted doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); // Verify that flag has no effect when OP_TURN_SCREEN_ON is not allowed int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); } else { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); } @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() { createService(); startSystem(); forceSleep(); IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); doReturn(false).when(mPowerPropertiesWrapper).permissionless_turn_screen_on(); // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); } when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); when(mContextSpy.checkCallingOrSelfPermission( android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( PackageManager.PERMISSION_DENIED); @Test @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnDenied() { createService(); startSystem(); forceSleep(); // Verify that the flag has no effect when OP_TURN_SCREEN_ON is allowed but // android.permission.TURN_SCREEN_ON is denied flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), packageName, /* attributionTag= */ null); doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); doReturn(true).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { Loading