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

Commit 15078cd0 authored by Alex Johnston's avatar Alex Johnston Committed by Android (Google) Code Review
Browse files

Merge "Implement OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS exemption"

parents ff3fac8f 740b086d
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -397,6 +397,13 @@ public class PowerExemptionManager {
     */
    public static final int REASON_PACKAGE_INSTALLER = 326;

    /**
     * {@link android.app.AppOpsManager#OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS}
     * set to MODE_ALLOWED
     * @hide
     */
    public static final int REASON_SYSTEM_EXEMPT_APP_OP = 327;

    /** @hide The app requests out-out. */
    public static final int REASON_OPT_OUT_REQUESTED = 1000;

+1 −0
Original line number Diff line number Diff line
@@ -1300,6 +1300,7 @@ package android.app.admin {
    field public static final int EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS = 1; // 0x1
    field public static final int EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION = 4; // 0x4
    field public static final int EXEMPT_FROM_HIBERNATION = 3; // 0x3
    field public static final int EXEMPT_FROM_POWER_RESTRICTIONS = 5; // 0x5
    field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
    field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
    field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
+12 −1
Original line number Diff line number Diff line
@@ -3929,6 +3929,16 @@ public class DevicePolicyManager {
    @SystemApi
    public static final int EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION =  4;
    /**
     * Exempt an app from all power-related restrictions, including app standby and doze.
     * In addition, the app will be able to start foreground services from the background,
     * and the user will not be able to stop foreground services run by the app.
     *
     * @hide
     */
    @SystemApi
    public static final int EXEMPT_FROM_POWER_RESTRICTIONS =  5;
    /**
     * Exemptions to platform restrictions, given to an application through
     * {@link #setApplicationExemptions(String, Set)}.
@@ -3940,7 +3950,8 @@ public class DevicePolicyManager {
            EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
            EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
            EXEMPT_FROM_HIBERNATION,
            EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION
            EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION,
            EXEMPT_FROM_POWER_RESTRICTIONS
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ApplicationExemptionConstants {}
+27 −5
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.settingslib.fuelgauge;

import static android.provider.DeviceConfig.NAMESPACE_ACTIVITY_MANAGER;

import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -23,6 +26,7 @@ import android.content.pm.PackageManager;
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.telecom.DefaultDialerManager;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -42,6 +46,10 @@ public class PowerAllowlistBackend {

    private static final String DEVICE_IDLE_SERVICE = "deviceidle";

    private static final String SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED =
            "system_exempt_power_restrictions_enabled";
    private static final boolean DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED = true;

    private static PowerAllowlistBackend sInstance;

    private final Context mAppContext;
@@ -76,12 +84,12 @@ public class PowerAllowlistBackend {
    /**
     * Check if target package is in allow list
     */
    public boolean isAllowlisted(String pkg) {
    public boolean isAllowlisted(String pkg, int uid) {
        if (mAllowlistedApps.contains(pkg)) {
            return true;
        }

        if (isDefaultActiveApp(pkg)) {
        if (isDefaultActiveApp(pkg, uid)) {
            return true;
        }

@@ -91,7 +99,7 @@ public class PowerAllowlistBackend {
    /**
     * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
     */
    public boolean isDefaultActiveApp(String pkg) {
    public boolean isDefaultActiveApp(String pkg, int uid) {
        // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
        // should be automatically allowlisted (otherwise user may be able to set restriction on
        // them, leading to bad device behavior.)
@@ -106,9 +114,23 @@ public class PowerAllowlistBackend {
            return true;
        }

        final AppOpsManager appOpsManager = mAppContext.getSystemService(AppOpsManager.class);
        if (isSystemExemptFlagEnabled() && appOpsManager.checkOpNoThrow(
                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, uid, pkg)
                == AppOpsManager.MODE_ALLOWED) {
            return true;
        }

        return false;
    }

    private static boolean isSystemExemptFlagEnabled() {
        return DeviceConfig.getBoolean(
                NAMESPACE_ACTIVITY_MANAGER,
                SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED,
                DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
    }

    /**
     * Check if target package is in allow list except idle app
     */
@@ -126,12 +148,12 @@ public class PowerAllowlistBackend {
     * @param pkgs a list of packageName
     * @return true when one of package is in allow list
     */
    public boolean isAllowlisted(String[] pkgs) {
    public boolean isAllowlisted(String[] pkgs, int uid) {
        if (ArrayUtils.isEmpty(pkgs)) {
            return false;
        }
        for (String pkg : pkgs) {
            if (isAllowlisted(pkg)) {
            if (isAllowlisted(pkg, uid)) {
                return true;
            }
        }
+39 −21
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -51,11 +52,14 @@ public class PowerAllowlistBackendTest {

    private static final String PACKAGE_ONE = "com.example.packageone";
    private static final String PACKAGE_TWO = "com.example.packagetwo";
    private static final int UID = 12345;

    @Mock
    private IDeviceIdleController mDeviceIdleService;
    @Mock
    private DevicePolicyManager mDevicePolicyManager;
    @Mock
    private AppOpsManager mAppOpsManager;
    private PowerAllowlistBackend mPowerAllowlistBackend;
    private ShadowPackageManager mPackageManager;
    private Context mContext;
@@ -73,6 +77,11 @@ public class PowerAllowlistBackendTest {
        mPackageManager = Shadow.extract(mContext.getPackageManager());
        mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
        doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
        doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
        doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_ONE);
        doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_TWO);

        mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
    }
@@ -82,34 +91,34 @@ public class PowerAllowlistBackendTest {
        doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
        mPowerAllowlistBackend.refreshList();

        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE}, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO}, UID)).isFalse();

        mPowerAllowlistBackend.addApp(PACKAGE_TWO);

        verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(
                new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue();
                new String[] {PACKAGE_ONE, PACKAGE_TWO}, UID)).isTrue();

        mPowerAllowlistBackend.removeApp(PACKAGE_TWO);

        verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE}, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO}, UID)).isFalse();

        mPowerAllowlistBackend.removeApp(PACKAGE_ONE);

        verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(
                new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse();
                new String[] {PACKAGE_ONE, PACKAGE_TWO}, UID)).isFalse();
    }

    @Test
@@ -119,8 +128,8 @@ public class PowerAllowlistBackendTest {

        mPowerAllowlistBackend.refreshList();

        assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(testSms, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms, UID)).isTrue();
    }

    @Test
@@ -130,16 +139,25 @@ public class PowerAllowlistBackendTest {

        mPowerAllowlistBackend.refreshList();

        assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer, UID)).isTrue();
    }

    @Test
    public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() {
        doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);

        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
    }

    @Test
    public void isAllowlisted_shouldAllowlistAppWithSystemExemptAppOp() {
        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(
                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_ONE);

        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
    }

    @Test
@@ -149,7 +167,7 @@ public class PowerAllowlistBackendTest {

        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue();
        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isFalse();
    }

    @Test
Loading