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

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

Fix saving Policy with STATE_PRIORITY_CHANNELS_BLOCKED

The state field was always dropped in NM.setNotificationPolicy(). This was okay when the only bit in the state was STATE_CHANNELS_BYPASSING_DND, which cannot be set from outside NMS. However, STATE_PRIORITY_CHANNELS_BLOCKED can be -- although, as a hidden API, only from some callers.

One subtlety is that we cannot just forward the state verbatim from the supplied Policy, because the caller likely doesn't know the current value of STATE_CHANNELS_BYPASSING_DND (in fact, with the NM.Policy <-> ZenPolicy conversion that Settings does, this value is outright dropped). Since ZenModeHelper will take the state (unless it's STATE_UNSET), NMS must be careful to merge the modifiable bits from the new Policy with the unmodifiable bits from the current Policy before passing it on.

Fixes: 369721344
Test: atest NotificationManagerServiceTest
Flag: android.app.modes_ui
Change-Id: I71d81b32e4dda482f2e738582baba36a79efc19a
parent 6f273d49
Loading
Loading
Loading
Loading
+26 −3
Original line number Diff line number Diff line
@@ -6243,6 +6243,7 @@ public class NotificationManagerService extends SystemService {
            int callingUid = Binder.getCallingUid();
            @ZenModeConfig.ConfigOrigin int origin = computeZenOrigin(fromUser);
            boolean isSystemCaller = isCallerSystemOrSystemUiOrShell();
            boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
                    && !canManageGlobalZenPolicy(pkg, callingUid);
@@ -6279,11 +6280,33 @@ public class NotificationManagerService extends SystemService {
                            policy.priorityCallSenders, policy.priorityMessageSenders,
                            policy.suppressedVisualEffects, currPolicy.priorityConversationSenders);
                }
                int newVisualEffects = calculateSuppressedVisualEffects(
                            policy, currPolicy, applicationInfo.targetSdkVersion);
                if (android.app.Flags.modesUi()) {
                    // 1. Callers should not modify STATE_CHANNELS_BYPASSING_DND, which is
                    // internally calculated and only indicates whether channels that want to bypass
                    // DND _exist_.
                    // 2. Only system callers should modify STATE_PRIORITY_CHANNELS_BLOCKED because
                    // it is @hide.
                    // 3. If the policy has been modified by the targetSdkVersion checks above then
                    // it has lost its state flags and that's fine (STATE_PRIORITY_CHANNELS_BLOCKED
                    // didn't exist until V).
                    int newState = Policy.STATE_UNSET;
                    if (isSystemCaller && policy.state != Policy.STATE_UNSET) {
                        newState = Policy.policyState(
                                currPolicy.hasPriorityChannels(),
                                policy.allowPriorityChannels());
                    }
                    policy = new Policy(policy.priorityCategories,
                            policy.priorityCallSenders, policy.priorityMessageSenders,
                            newVisualEffects, newState, policy.priorityConversationSenders);
                } else {
                    policy = new Policy(policy.priorityCategories,
                            policy.priorityCallSenders, policy.priorityMessageSenders,
                            newVisualEffects, policy.priorityConversationSenders);
                }
                if (shouldApplyAsImplicitRule) {
                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+54 −1
Original line number Diff line number Diff line
@@ -193,6 +193,7 @@ import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.Person;
import android.app.RemoteInput;
@@ -655,7 +656,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt()))
                .thenReturn(INVALID_TASK_ID);
        mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
        when(mUm.getProfileIds(eq(mUserId), eq(false))).thenReturn(new int[] { mUserId });
        when(mUm.getProfileIds(eq(mUserId), anyBoolean())).thenReturn(new int[]{mUserId});
        when(mUmInternal.getProfileIds(eq(mUserId), anyBoolean())).thenReturn(new int[]{mUserId});
        when(mAmi.getCurrentUserId()).thenReturn(mUserId);
        when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(true);
@@ -15936,6 +15938,57 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertThat(updatedRule.getValue().isEnabled()).isFalse();
    }
    @Test
    @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
    public void setNotificationPolicy_fromSystemApp_appliesPriorityChannelsAllowed()
            throws Exception {
        setUpRealZenTest();
        // Start with hasPriorityChannels=true, allowPriorityChannels=true ("default").
        mService.mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0, 0,
                        Policy.policyState(true, true), 0),
                ZenModeConfig.ORIGIN_SYSTEM, Process.SYSTEM_UID);
        // The caller will supply states with "wrong" hasPriorityChannels.
        int stateBlockingPriorityChannels = Policy.policyState(false, false);
        mBinderService.setNotificationPolicy(mPkg,
                new Policy(1, 0, 0, 0, stateBlockingPriorityChannels, 0), false);
        // hasPriorityChannels is untouched and allowPriorityChannels was updated.
        assertThat(mBinderService.getNotificationPolicy(mPkg).priorityCategories).isEqualTo(1);
        assertThat(mBinderService.getNotificationPolicy(mPkg).state).isEqualTo(
                Policy.policyState(true, false));
        // Same but setting allowPriorityChannels to true.
        int stateAllowingPriorityChannels = Policy.policyState(false, true);
        mBinderService.setNotificationPolicy(mPkg,
                new Policy(2, 0, 0, 0, stateAllowingPriorityChannels, 0), false);
        assertThat(mBinderService.getNotificationPolicy(mPkg).priorityCategories).isEqualTo(2);
        assertThat(mBinderService.getNotificationPolicy(mPkg).state).isEqualTo(
                Policy.policyState(true, true));
    }
    @Test
    @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
    @DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
    public void setNotificationPolicy_fromRegularAppThatCanModifyPolicy_ignoresState()
            throws Exception {
        setUpRealZenTest();
        // Start with hasPriorityChannels=true, allowPriorityChannels=true ("default").
        mService.mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0, 0,
                        Policy.policyState(true, true), 0),
                ZenModeConfig.ORIGIN_SYSTEM, Process.SYSTEM_UID);
        mService.setCallerIsNormalPackage();
        mBinderService.setNotificationPolicy(mPkg,
                new Policy(1, 0, 0, 0, Policy.policyState(false, false), 0), false);
        // Policy was updated but the attempt to change state was ignored (it's a @hide API).
        assertThat(mBinderService.getNotificationPolicy(mPkg).priorityCategories).isEqualTo(1);
        assertThat(mBinderService.getNotificationPolicy(mPkg).state).isEqualTo(
                Policy.policyState(true, true));
    }
    /** Prepares for a zen-related test that uses the real {@link ZenModeHelper}. */
    private void setUpRealZenTest() throws Exception {
        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))