Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18f2b65a authored by Philip Junker's avatar Philip Junker Committed by Android (Google) Code Review
Browse files

Merge "Allow ACQUIRE_CAUSES_WAKEUP for apps targeting pre-U sdks" into udc-dev

parents 06b3270f d92935f0
Loading
Loading
Loading
Loading
+102 −16
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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";

@@ -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;
@@ -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();
@@ -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();

@@ -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;
        }
@@ -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);
@@ -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) {
+86 −32
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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,
@@ -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;
@@ -589,6 +599,7 @@ public class PowerManagerServiceTest {
    }

    @Test
    @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION})
    public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() {
        createService();
        startSystem();
@@ -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;
@@ -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();
@@ -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)) {