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

Commit 3b24bafb authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Handle corner cases in "Alarms and Reminders" page" into udc-dev

parents c81a93c8 5aeef97c
Loading
Loading
Loading
Loading
+52 −27
Original line number Diff line number Diff line
@@ -18,11 +18,11 @@ package com.android.settings.applications;

import android.Manifest;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.os.RemoteException;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerExemptionManager;
import android.os.UserHandle;
import android.util.Log;

@@ -32,8 +32,6 @@ import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;

import libcore.util.EmptyArray;

import java.util.List;

/**
@@ -42,26 +40,24 @@ import java.util.List;
 * Also provides app filters that can use the info.
 */
public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
    private static final String PERMISSION = Manifest.permission.SCHEDULE_EXACT_ALARM;
    private static final String SEA_PERMISSION = Manifest.permission.SCHEDULE_EXACT_ALARM;
    private static final String UEA_PERMISSION = Manifest.permission.USE_EXACT_ALARM;
    private static final String TAG = "AlarmsAndRemindersBridge";

    @VisibleForTesting
    AlarmManager mAlarmManager;
    @VisibleForTesting
    String[] mRequesterPackages;
    PowerExemptionManager mPowerExemptionManager;
    @VisibleForTesting
    PackageManager mPackageManager;

    public AppStateAlarmsAndRemindersBridge(Context context, ApplicationsState appState,
            Callback callback) {
        super(appState, callback);

        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
        mAlarmManager = context.getSystemService(AlarmManager.class);
        final IPackageManager iPm = AppGlobals.getPackageManager();
        try {
            mRequesterPackages = iPm.getAppOpPermissionPackages(PERMISSION, context.getUserId());
        } catch (RemoteException re) {
            Log.e(TAG, "Cannot reach package manager", re);
            mRequesterPackages = EmptyArray.STRING;
        }
        mPackageManager = context.getPackageManager();
    }

    private boolean isChangeEnabled(String packageName, int userId) {
@@ -69,6 +65,22 @@ public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
                packageName, UserHandle.of(userId));
    }

    private boolean isUeaChangeEnabled(String packageName, int userId) {
        return CompatChanges.isChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, packageName,
                UserHandle.of(userId));
    }

    private String[] getRequestedPermissions(String packageName, int userId) {
        try {
            final PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName,
                    PackageManager.GET_PERMISSIONS, userId);
            return info.requestedPermissions;
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Could not find package " + packageName, e);
        }
        return null;
    }

    /**
     * Returns information regarding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} for the given
     * package and uid.
@@ -76,10 +88,17 @@ public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
    public AlarmsAndRemindersState createPermissionState(String packageName, int uid) {
        final int userId = UserHandle.getUserId(uid);

        final boolean permissionRequested = ArrayUtils.contains(mRequesterPackages, packageName)
        final String[] requestedPermissions = getRequestedPermissions(packageName, userId);

        final boolean seaRequested = ArrayUtils.contains(requestedPermissions, SEA_PERMISSION)
                && isChangeEnabled(packageName, userId);
        final boolean permissionGranted = mAlarmManager.hasScheduleExactAlarm(packageName, userId);
        return new AlarmsAndRemindersState(permissionRequested, permissionGranted);
        final boolean ueaRequested = ArrayUtils.contains(requestedPermissions, UEA_PERMISSION)
                && isUeaChangeEnabled(packageName, userId);

        final boolean seaGranted = mAlarmManager.hasScheduleExactAlarm(packageName, userId);
        final boolean allowListed = mPowerExemptionManager.isAllowListed(packageName, true);

        return new AlarmsAndRemindersState(seaRequested, ueaRequested, seaGranted, allowListed);
    }

    @Override
@@ -113,26 +132,32 @@ public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
    };

    /**
     * Class to denote the state of an app regarding
     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
     * Class to denote the state of an app regarding "Alarms and Reminders" permission.
     * This permission state is a combination of {@link Manifest.permission#SCHEDULE_EXACT_ALARM},
     * {@link Manifest.permission#USE_EXACT_ALARM} and the power allowlist state.
     */
    public static class AlarmsAndRemindersState {
        private boolean mPermissionRequested;
        private boolean mPermissionGranted;

        AlarmsAndRemindersState(boolean permissionRequested, boolean permissionGranted) {
            mPermissionRequested = permissionRequested;
            mPermissionGranted = permissionGranted;
        private boolean mSeaPermissionRequested;
        private boolean mUeaPermissionRequested;
        private boolean mSeaPermissionGranted;
        private boolean mAllowListed;

        AlarmsAndRemindersState(boolean seaPermissionRequested, boolean ueaPermissionRequested,
                boolean seaPermissionGranted, boolean allowListed) {
            mSeaPermissionRequested = seaPermissionRequested;
            mUeaPermissionRequested = ueaPermissionRequested;
            mSeaPermissionGranted = seaPermissionGranted;
            mAllowListed = allowListed;
        }

        /** Should the app associated with this state appear on the Settings screen */
        public boolean shouldBeVisible() {
            return mPermissionRequested;
            return mSeaPermissionRequested && !mUeaPermissionRequested && !mAllowListed;
        }

        /** Is the permission granted to the app associated with this state */
        public boolean isAllowed() {
            return mPermissionGranted;
            return mSeaPermissionGranted || mUeaPermissionRequested || mAllowListed;
        }
    }
}
+169 −55
Original line number Diff line number Diff line
@@ -16,15 +16,24 @@
package com.android.settings.applications;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;

import android.Manifest;
import android.app.AlarmManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerExemptionManager;
import android.os.UserHandle;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import libcore.util.EmptyArray;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,13 +43,15 @@ import org.mockito.MockitoAnnotations;

@RunWith(AndroidJUnit4.class)
public class AppStateAlarmsAndRemindersBridgeTest {
    private static final String TEST_PACKAGE_1 = "com.example.test.1";
    private static final String TEST_PACKAGE_2 = "com.example.test.2";
    private static final int UID_1 = 12345;
    private static final int UID_2 = 7654321;
    private static final String TEST_PACKAGE = "com.example.test.1";
    private static final int TEST_UID = 12345;

    @Mock
    private AlarmManager mAlarmManager;
    @Mock
    private PowerExemptionManager mPowerExemptionManager;
    @Mock
    private PackageManager mPackageManager;
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private Context mContext;

@@ -50,65 +61,168 @@ public class AppStateAlarmsAndRemindersBridgeTest {
    }

    @Test
    public void shouldBeVisible_permissionRequestedIsTrue_isTrue() {
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                true /* permissionRequested */,
                true /* permissionGranted */)
                .shouldBeVisible()).isTrue();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                true /* permissionRequested */,
                false /* permissionGranted */)
                .shouldBeVisible()).isTrue();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                false /* permissionRequested */,
                true /* permissionGranted */)
                .shouldBeVisible()).isFalse();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                false /* permissionRequested */,
                false /* permissionGranted */)
                .shouldBeVisible()).isFalse();
    public void alarmsAndRemindersState_shouldBeVisible() {
        boolean seaPermissionRequested;
        boolean ueaPermissionRequested;
        boolean seaPermissionGranted;
        boolean allowListed;

        for (int i = 0; i < (1 << 4); i++) {
            seaPermissionRequested = (i & 1) != 0;
            ueaPermissionRequested = (i & (1 << 1)) != 0;
            seaPermissionGranted = (i & (1 << 2)) != 0;
            allowListed = (i & (1 << 3)) != 0;

            final boolean visible = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                    seaPermissionRequested,
                    ueaPermissionRequested,
                    seaPermissionGranted,
                    allowListed).shouldBeVisible();

            assertWithMessage("Wrong return value " + visible
                    + " for {seaPermissionRequested = " + seaPermissionRequested
                    + ", ueaPermissionRequested = " + ueaPermissionRequested
                    + ", seaPermissionGranted = " + seaPermissionGranted
                    + ", allowListed = " + allowListed + "}")
                    .that(visible)
                    .isEqualTo(seaPermissionRequested && !ueaPermissionRequested && !allowListed);
        }
    }

    @Test
    public void alarmsAndRemindersState_isAllowed() {
        boolean seaPermissionRequested;
        boolean ueaPermissionRequested;
        boolean seaPermissionGranted;
        boolean allowListed;

        for (int i = 0; i < (1 << 4); i++) {
            seaPermissionRequested = (i & 1) != 0;
            ueaPermissionRequested = (i & (1 << 1)) != 0;
            seaPermissionGranted = (i & (1 << 2)) != 0;
            allowListed = (i & (1 << 3)) != 0;

            final boolean allowed = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                    seaPermissionRequested,
                    ueaPermissionRequested,
                    seaPermissionGranted,
                    allowListed).isAllowed();

            assertWithMessage("Wrong return value " + allowed
                    + " for {seaPermissionRequested = " + seaPermissionRequested
                    + ", ueaPermissionRequested = " + ueaPermissionRequested
                    + ", seaPermissionGranted = " + seaPermissionGranted
                    + ", allowListed = " + allowListed + "}")
                    .that(allowed)
                    .isEqualTo(seaPermissionGranted || ueaPermissionRequested || allowListed);
        }
    }

    private PackageInfo createPackageInfoWithPermissions(String... requestedPermissions) {
        final PackageInfo info = new PackageInfo();
        info.requestedPermissions = requestedPermissions;
        return info;
    }

    @Test
    public void isAllowed_permissionGrantedIsTrue_isTrue() {
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                true /* permissionRequested */,
                true /* permissionGranted */)
                .isAllowed()).isTrue();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                true /* permissionRequested */,
                false /* permissionGranted */)
                .isAllowed()).isFalse();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                false /* permissionRequested */,
                true /* permissionGranted */)
                .isAllowed()).isTrue();
        assertThat(new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
                false /* permissionRequested */,
                false /* permissionGranted */)
                .isAllowed()).isFalse();
    public void createPermissionState_SeaGrantedNoUeaNoAllowlist() throws Exception {
        AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
                null, null);
        bridge.mAlarmManager = mAlarmManager;
        bridge.mPackageManager = mPackageManager;
        bridge.mPowerExemptionManager = mPowerExemptionManager;

        doReturn(true).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
                UserHandle.getUserId(TEST_UID));
        doReturn(createPackageInfoWithPermissions(Manifest.permission.SCHEDULE_EXACT_ALARM))
                .when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
        doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
                bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
        assertThat(state.shouldBeVisible()).isTrue();
        assertThat(state.isAllowed()).isTrue();
    }

    @Test
    public void createPermissionState() {
    public void createPermissionState_requestsBothSeaDeniedNoAllowlist() throws Exception {
        AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
                null, null);
        bridge.mAlarmManager = mAlarmManager;
        bridge.mRequesterPackages = new String[]{TEST_PACKAGE_1, "some.other.package"};

        doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE_1,
                UserHandle.getUserId(UID_1));
        doReturn(true).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE_2,
                UserHandle.getUserId(UID_2));

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state1 =
                bridge.createPermissionState(TEST_PACKAGE_1, UID_1);
        assertThat(state1.shouldBeVisible()).isTrue();
        assertThat(state1.isAllowed()).isFalse();

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state2 =
                bridge.createPermissionState(TEST_PACKAGE_2, UID_2);
        assertThat(state2.shouldBeVisible()).isFalse();
        assertThat(state2.isAllowed()).isTrue();
        bridge.mPackageManager = mPackageManager;
        bridge.mPowerExemptionManager = mPowerExemptionManager;

        doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
                UserHandle.getUserId(TEST_UID));
        doReturn(createPackageInfoWithPermissions(
                Manifest.permission.SCHEDULE_EXACT_ALARM,
                Manifest.permission.USE_EXACT_ALARM))
                .when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
        doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
                bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
        assertThat(state.shouldBeVisible()).isFalse();
        assertThat(state.isAllowed()).isTrue();
    }

    @Test
    public void createPermissionState_requestsNoneNoAllowlist() throws Exception {
        AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
                null, null);
        bridge.mAlarmManager = mAlarmManager;
        bridge.mPackageManager = mPackageManager;
        bridge.mPowerExemptionManager = mPowerExemptionManager;

        doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
                UserHandle.getUserId(TEST_UID));
        doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
                .when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
        doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
                bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
        assertThat(state.shouldBeVisible()).isFalse();
        assertThat(state.isAllowed()).isFalse();
    }

    @Test
    public void createPermissionState_requestsOnlyUeaNoAllowlist() throws Exception {
        AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
                null, null);
        bridge.mAlarmManager = mAlarmManager;
        bridge.mPackageManager = mPackageManager;
        bridge.mPowerExemptionManager = mPowerExemptionManager;

        doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
                UserHandle.getUserId(TEST_UID));
        doReturn(createPackageInfoWithPermissions(Manifest.permission.USE_EXACT_ALARM))
                .when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
        doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
                bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
        assertThat(state.shouldBeVisible()).isFalse();
        assertThat(state.isAllowed()).isTrue();
    }

    @Test
    public void createPermissionState_requestsNoneButAllowlisted() throws Exception {
        AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
                null, null);
        bridge.mAlarmManager = mAlarmManager;
        bridge.mPackageManager = mPackageManager;
        bridge.mPowerExemptionManager = mPowerExemptionManager;

        doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
                UserHandle.getUserId(TEST_UID));
        doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
                .when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
        doReturn(true).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);

        AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
                bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
        assertThat(state.shouldBeVisible()).isFalse();
        assertThat(state.isAllowed()).isTrue();
    }
}