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

Commit 67706813 authored by Yuri Lin's avatar Yuri Lin Committed by Matías Hernández
Browse files

Update areChannelsBypassingDnd from PreferencesHelper correctly

The previous implementation overwrites the channel permissions associated with manual DND. This change adds a new method to ZenModeHelper to explicitly only set the hasPriorityChannels (areChannelsBypassingDnd) bit.

Fixes: 372302344
Test: manual to confirm correct behavior, ZenModeHelperTest, PreferencesHelperTest
Flag: android.app.modes_ui
Change-Id: I38e8ff6187209ca2464c83490caae74847f4f3cd
parent e0dbf7e2
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -1986,6 +1986,7 @@ public class PreferencesHelper implements RankingConfig {
     * bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
     * when the current user (or its profiles) change.
     */
    // TODO: b/368247671 - remove fromSystemOrSystemUi argument when modes_ui is inlined.
    private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
            boolean fromSystemOrSystemUi) {
        ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
@@ -2016,7 +2017,12 @@ public class PreferencesHelper implements RankingConfig {
        boolean haveBypassingApps = candidatePkgs.size() > 0;
        if (mCurrentUserHasChannelsBypassingDnd != haveBypassingApps) {
            mCurrentUserHasChannelsBypassingDnd = haveBypassingApps;
            updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid, fromSystemOrSystemUi);
            if (android.app.Flags.modesUi()) {
                mZenModeHelper.updateHasPriorityChannels(mCurrentUserHasChannelsBypassingDnd);
            } else {
                updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid,
                        fromSystemOrSystemUi);
            }
        }
    }

@@ -2034,6 +2040,9 @@ public class PreferencesHelper implements RankingConfig {
        return true;
    }

    // TODO: b/368247671 - delete this method when modes_ui is inlined, as
    //                     updateCurrentUserHasChannelsBypassingDnd was the only caller and
    //                     PreferencesHelper should otherwise not need to modify actual policy
    public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
            boolean fromSystemOrSystemUi) {
        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+22 −0
Original line number Diff line number Diff line
@@ -1567,6 +1567,28 @@ public class ZenModeHelper {
        return azr;
    }

    // Update only the hasPriorityChannels state (aka areChannelsBypassingDnd) without modifying
    // any of the rest of the existing policy. This allows components that only want to modify
    // this bit (PreferencesHelper) to not have to adjust the rest of the policy.
    protected void updateHasPriorityChannels(boolean hasPriorityChannels) {
        if (!Flags.modesUi()) {
            Log.wtf(TAG, "updateHasPriorityChannels called without modes_ui");
        }
        synchronized (mConfigLock) {
            // If it already matches, do nothing
            if (mConfig.areChannelsBypassingDnd == hasPriorityChannels) {
                return;
            }

            ZenModeConfig newConfig = mConfig.copy();
            newConfig.areChannelsBypassingDnd = hasPriorityChannels;
            // The updated calculation of whether there are priority channels is always done by
            // the system, even if the event causing the calculation had a different origin.
            setConfigLocked(newConfig, null, ORIGIN_SYSTEM, "updateHasPriorityChannels",
                    Process.SYSTEM_UID);
        }
    }

    @SuppressLint("MissingPermission")
    void scheduleActivationBroadcast(String pkg, @UserIdInt int userId, String ruleId,
            boolean activated) {
+93 −19
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.NotificationChannel.ALLOW_BUBBLE_ON;
@@ -81,6 +82,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -248,7 +250,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
    @Parameters(name = "{0}")
    public static List<FlagsParameterization> getParams() {
        return FlagsParameterization.allCombinationsOf(
                FLAG_NOTIFICATION_CLASSIFICATION);
                FLAG_NOTIFICATION_CLASSIFICATION, FLAG_MODES_UI);
    }

    public PreferencesHelperTest(FlagsParameterization flags) {
@@ -2701,7 +2703,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // create notification channel that can bypass dnd
@@ -2711,18 +2717,30 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                uid, false);
        assertTrue(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // delete channels
        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2738,7 +2756,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // Recreate a channel & now the app has dnd access granted and can set the bypass dnd field
@@ -2748,7 +2770,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
                uid, false);

        assertTrue(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2764,7 +2790,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // create notification channel that can bypass dnd, using local app level settings
@@ -2774,18 +2804,30 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                uid, false);
        assertTrue(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // delete channels
        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2812,7 +2854,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2834,7 +2880,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2856,7 +2906,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2872,7 +2926,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                uid, false);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // update channel so it CAN bypass dnd:
@@ -2880,7 +2938,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        channel.setBypassDnd(true);
        mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
        assertTrue(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();

        // update channel so it can't bypass dnd:
@@ -2888,7 +2950,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        channel.setBypassDnd(false);
        mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2901,7 +2967,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
        mHelper.syncChannelsBypassingDnd();
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
        } else {
            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

@@ -2911,7 +2981,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
        assertFalse(mHelper.areChannelsBypassingDnd());
        if (android.app.Flags.modesUi()) {
            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
        } else {
            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
        }
        resetZenModeHelper();
    }

+23 −0
Original line number Diff line number Diff line
@@ -7027,6 +7027,29 @@ public class ZenModeHelperTest extends UiServiceTestCase {
                ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
    }

    @Test
    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
    public void updateHasPriorityChannels_keepsChannelSettings() {
        setupZenConfig();

        // Set priority channels setting on manual mode to confirm that it is unaffected by changes
        // to the state describing the existence of such channels.
        mZenModeHelper.mConfig.manualRule.zenPolicy =
                new ZenPolicy.Builder(mZenModeHelper.mConfig.manualRule.zenPolicy)
                        .allowPriorityChannels(false)
                        .build();

        mZenModeHelper.updateHasPriorityChannels(true);
        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isTrue();

        // getNotificationPolicy() gets its policy from the manual rule; channels not permitted
        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();

        mZenModeHelper.updateHasPriorityChannels(false);
        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isFalse();
        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();
    }

    private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
            @Nullable ZenPolicy zenPolicy) {
        ZenRule rule = new ZenRule();