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

Commit a81bce5c authored by Matías Hernández's avatar Matías Hernández
Browse files

Make implicit rules updatable from System callers

Implicit rules don't have a ConditionProviderService nor a ConfigurationActivity, one of which is required for AutomaticZenRules. Allow the system (e.g. the user, from settings) to update them nevertheless.

Note that we still don't allow apps to update such rules, unless they are willing to add the required information.

Test: atest NotificationManagerServiceTest
Fixes: 309506066
Change-Id: Ib934cc967a59aa95643705a6f72e6443244c2335
parent b017819a
Loading
Loading
Loading
Loading
+24 −7
Original line number Diff line number Diff line
@@ -5383,7 +5383,7 @@ public class NotificationManagerService extends SystemService {
        @Override
        public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg,
                boolean fromUser) {
            validateAutomaticZenRule(automaticZenRule);
            validateAutomaticZenRule(/* updateId= */ null, automaticZenRule);
            checkCallerIsSameApp(pkg);
            if (automaticZenRule.getZenPolicy() != null
                    && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
@@ -5409,7 +5409,7 @@ public class NotificationManagerService extends SystemService {
        @Override
        public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
                boolean fromUser) throws RemoteException {
            validateAutomaticZenRule(automaticZenRule);
            validateAutomaticZenRule(id, automaticZenRule);
            enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
            enforceUserOriginOnlyFromSystem(fromUser, "updateAutomaticZenRule");
@@ -5417,14 +5417,31 @@ public class NotificationManagerService extends SystemService {
                    computeZenOrigin(fromUser), "updateAutomaticZenRule", Binder.getCallingUid());
        }
        private void validateAutomaticZenRule(AutomaticZenRule rule) {
        private void validateAutomaticZenRule(@Nullable String updateId, AutomaticZenRule rule) {
            Objects.requireNonNull(rule, "automaticZenRule is null");
            Objects.requireNonNull(rule.getName(), "Name is null");
            rule.validate();
            if (rule.getOwner() == null
            // Implicit rules have no ConditionProvider or Activity. We allow the user to customize
            // them (via Settings), but not the owner app. Should the app want to start using it as
            // a "normal" rule, it must provide a CP/ConfigActivity too.
            if (android.app.Flags.modesApi()) {
                boolean isImplicitRuleUpdateFromSystem = updateId != null
                        && ZenModeHelper.isImplicitRuleId(updateId)
                        && isCallerSystemOrSystemUi();
                if (!isImplicitRuleUpdateFromSystem
                        && rule.getOwner() == null
                        && rule.getConfigurationActivity() == null) {
                    throw new NullPointerException(
                        "Rule must have a conditionproviderservice and/or configuration activity");
                            "Rule must have a ConditionProviderService and/or configuration "
                                    + "activity");
                }
            } else {
                if (rule.getOwner() == null && rule.getConfigurationActivity() == null) {
                    throw new NullPointerException(
                            "Rule must have a ConditionProviderService and/or configuration "
                                    + "activity");
                }
            }
            Objects.requireNonNull(rule.getConditionId(), "ConditionId is null");
+7 −1
Original line number Diff line number Diff line
@@ -134,6 +134,8 @@ public class ZenModeHelper {
    private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
    static final int RULE_LIMIT_PER_PACKAGE = 100;

    private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name

    /**
     * Send new activation AutomaticZenRule statuses to apps with a min target SDK version
     */
@@ -653,7 +655,11 @@ public class ZenModeHelper {
    }

    private static String implicitRuleId(String forPackage) {
        return "implicit_" + forPackage;
        return IMPLICIT_RULE_ID_PREFIX + forPackage;
    }

    static boolean isImplicitRuleId(@NonNull String ruleId) {
        return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
    }

    boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason,
+72 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import static com.android.server.notification.NotificationRecordLogger.Notificat
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -9300,6 +9301,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyInt());
    }
    /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
    private ZenModeHelper setUpMockZenTest() {
        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
        mService.setZenHelper(zenModeHelper);
@@ -13870,6 +13872,76 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        }
    }
    @Test
    @EnableFlags(android.app.Flags.FLAG_MODES_API)
    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
    public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
        setUpRealZenTest();
        mService.setCallerIsNormalPackage();
        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
        // Create an implicit zen rule by calling setNotificationPolicy from an app.
        mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
                mBinderService.getAutomaticZenRules().entrySet());
        assertThat(rule.getValue().getOwner()).isNull();
        assertThat(rule.getValue().getConfigurationActivity()).isNull();
        // Now try to update said rule (e.g. disable it). Should fail.
        // We also validate the exception message because NPE could be thrown by all sorts of test
        // issues (e.g. misconfigured mocks).
        rule.getValue().setEnabled(false);
        NullPointerException e = assertThrows(NullPointerException.class,
                () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
        assertThat(e.getMessage()).isEqualTo(
                "Rule must have a ConditionProviderService and/or configuration activity");
    }
    @Test
    @EnableFlags(android.app.Flags.FLAG_MODES_API)
    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
    public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
        setUpRealZenTest();
        mService.setCallerIsNormalPackage();
        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
        // Create an implicit zen rule by calling setNotificationPolicy from an app.
        mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
                mBinderService.getAutomaticZenRules().entrySet());
        assertThat(rule.getValue().getOwner()).isNull();
        assertThat(rule.getValue().getConfigurationActivity()).isNull();
        // Now update said rule from Settings (e.g. disable it). Should work!
        mService.isSystemUid = true;
        rule.getValue().setEnabled(false);
        mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
        Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
                mBinderService.getAutomaticZenRules().entrySet());
        assertThat(updatedRule.getValue().isEnabled()).isFalse();
    }
    /** Prepares for a zen-related test that uses the real {@link ZenModeHelper}. */
    private void setUpRealZenTest() throws Exception {
        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
                .thenReturn(true);
        int iconResId = 79;
        String iconResName = "icon_79";
        String pkg = mContext.getPackageName();
        ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
        appInfoSpy.icon = iconResId;
        when(appInfoSpy.loadLabel(any())).thenReturn("Test App");
        when(mPackageManagerClient.getApplicationInfo(eq(pkg), anyInt())).thenReturn(appInfoSpy);
        when(mResources.getResourceName(eq(iconResId))).thenReturn(iconResName);
        when(mResources.getIdentifier(eq(iconResName), any(), any())).thenReturn(iconResId);
        when(mPackageManagerClient.getResourcesForApplication(eq(pkg))).thenReturn(mResources);
    }
    @Test
    public void testFixNotification_clearsLifetimeExtendedFlag() throws Exception {
        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);