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

Commit fc352711 authored by Yuri Lin's avatar Yuri Lin
Browse files

Optionally create package preferences in getNotificationChannels()

This behavior is to mimic existing behavior of getNotificationChannel(). In the (rare) case that an app requests a channel but the package preferences haven't been created yet, getNotificationChannel() would create it and the default channel if necessary. This change has getNotificationChannels() do so as well, but only when it's called via NotificationManager.getNotificationChannel.

Bug: 381131846
Test: NotificationManagerTest, PreferencesHelperTest
Flag: android.app.nm_binder_perf_cache_channels
Change-Id: I429ae8ac5e72878380399c81ebe6d5b73becd1ce
parent 3a0d362b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ interface INotificationManager
    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
    void deleteNotificationChannel(String pkg, String channelId);
    ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
    ParceledListSlice getOrCreateNotificationChannels(String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded);
    ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
    int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
    int getDeletedChannelCount(String pkg, int uid);
+12 −8
Original line number Diff line number Diff line
@@ -1211,7 +1211,8 @@ public class NotificationManager {
                    mNotificationChannelListCache.query(new NotificationChannelQuery(
                            mContext.getOpPackageName(),
                            mContext.getPackageName(),
                            mContext.getUserId())));
                            mContext.getUserId(),
                            true)));  // create (default channel) if needed
        } else {
            INotificationManager service = service();
            try {
@@ -1239,7 +1240,8 @@ public class NotificationManager {
                    mNotificationChannelListCache.query(new NotificationChannelQuery(
                            mContext.getOpPackageName(),
                            mContext.getPackageName(),
                            mContext.getUserId())));
                            mContext.getUserId(),
                            true)));  // create (default channel) if needed
        } else {
            INotificationManager service = service();
            try {
@@ -1265,7 +1267,8 @@ public class NotificationManager {
            return mNotificationChannelListCache.query(new NotificationChannelQuery(
                    mContext.getOpPackageName(),
                    mContext.getPackageName(),
               mContext.getUserId()));
                    mContext.getUserId(),
                    false));
        } else {
            INotificationManager service = service();
            try {
@@ -1405,8 +1408,8 @@ public class NotificationManager {
                public List<NotificationChannel> apply(NotificationChannelQuery query) {
                    INotificationManager service = service();
                    try {
                        return service.getNotificationChannels(query.callingPkg,
                                query.targetPkg, query.userId).getList();
                        return service.getOrCreateNotificationChannels(query.callingPkg,
                                query.targetPkg, query.userId, query.createIfNeeded).getList();
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
@@ -1434,7 +1437,8 @@ public class NotificationManager {
    private record NotificationChannelQuery(
            String callingPkg,
            String targetPkg,
            int userId) {}
            int userId,
            boolean createIfNeeded) {}

    /**
     * @hide
+24 −20
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.app;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -269,8 +270,9 @@ public class NotificationManagerTest {

        // It doesn't matter what the returned contents are, as long as we return a channel.
        // This setup must set up getNotificationChannels(), as that's the method called.
        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
                anyInt(), anyBoolean())).thenReturn(
                    new ParceledListSlice<>(List.of(exampleChannel())));

        // ask for the same channel 100 times without invalidating the cache
        for (int i = 0; i < 100; i++) {
@@ -282,7 +284,7 @@ public class NotificationManagerTest {
        NotificationChannel unused = mNotificationManager.getNotificationChannel("id");

        verify(mNotificationManager.mBackendService, times(2))
                .getNotificationChannels(any(), any(), anyInt());
                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
    }

    @Test
@@ -295,23 +297,24 @@ public class NotificationManagerTest {
        NotificationChannel c2 = new NotificationChannel("id2", "name2",
                NotificationManager.IMPORTANCE_NONE);

        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
                anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
                anyInt(), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));

        assertThat(mNotificationManager.getNotificationChannel("id1")).isEqualTo(c1);
        assertThat(mNotificationManager.getNotificationChannel("id2")).isEqualTo(c2);
        assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();

        verify(mNotificationManager.mBackendService, times(1))
                .getNotificationChannels(any(), any(), anyInt());
                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
    }

    @Test
    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
    public void getNotificationChannels_cachedUntilInvalidated() throws Exception {
        NotificationManager.invalidateNotificationChannelCache();
        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
                anyInt(), anyBoolean())).thenReturn(
                    new ParceledListSlice<>(List.of(exampleChannel())));

        // ask for channels 100 times without invalidating the cache
        for (int i = 0; i < 100; i++) {
@@ -323,7 +326,7 @@ public class NotificationManagerTest {
        List<NotificationChannel> res = mNotificationManager.getNotificationChannels();

        verify(mNotificationManager.mBackendService, times(2))
                .getNotificationChannels(any(), any(), anyInt());
                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
        assertThat(res).containsExactlyElementsIn(List.of(exampleChannel()));
    }

@@ -341,8 +344,9 @@ public class NotificationManagerTest {
        NotificationChannel c2 = new NotificationChannel("other", "name2",
                NotificationManager.IMPORTANCE_DEFAULT);

        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(), anyInt()))
                .thenReturn(new ParceledListSlice<>(List.of(c1, conv1, c2)));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
                anyInt(), anyBoolean())).thenReturn(
                    new ParceledListSlice<>(List.of(c1, conv1, c2)));

        // Lookup for channel c1 and c2: returned as expected
        assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(c1);
@@ -359,9 +363,9 @@ public class NotificationManagerTest {
        // Lookup of a nonexistent channel is null
        assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();

        // All of that should have been one call to getNotificationChannels()
        // All of that should have been one call to getOrCreateNotificationChannels()
        verify(mNotificationManager.mBackendService, times(1))
                .getNotificationChannels(any(), any(), anyInt());
                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
    }

    @Test
@@ -381,12 +385,12 @@ public class NotificationManagerTest {
        NotificationChannel channel3 = channel1.copy();
        channel3.setName("name3");

        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel1)));
        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg2),
                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel2)));
        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
                eq(userId1))).thenReturn(new ParceledListSlice<>(List.of(channel3)));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
                eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel1)));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg2),
                eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel2)));
        when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
                eq(userId1), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel3)));

        // set our context to pretend to be from package 1 and userId 0
        mContext.setParameters(pkg1, pkg1, userId);
@@ -402,7 +406,7 @@ public class NotificationManagerTest {

        // Those should have been three different calls
        verify(mNotificationManager.mBackendService, times(3))
                .getNotificationChannels(any(), any(), anyInt());
                .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
    }

    private Notification exampleNotification() {
+8 −1
Original line number Diff line number Diff line
@@ -4931,6 +4931,12 @@ public class NotificationManagerService extends SystemService {
        @Override
        public ParceledListSlice<NotificationChannel> getNotificationChannels(
                String callingPkg, String targetPkg, int userId) {
            return getOrCreateNotificationChannels(callingPkg, targetPkg, userId, false);
        }
        @Override
        public ParceledListSlice<NotificationChannel> getOrCreateNotificationChannels(
                String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded) {
            if (canNotifyAsPackage(callingPkg, targetPkg, userId)
                || isCallingUidSystem()) {
                int targetUid = -1;
@@ -4940,7 +4946,8 @@ public class NotificationManagerService extends SystemService {
                    /* ignore */
                }
                return mPreferencesHelper.getNotificationChannels(
                        targetPkg, targetUid, false /* includeDeleted */, true);
                        targetPkg, targetUid, false /* includeDeleted */, true,
                        createPrefsIfNeeded);
            }
            throw new SecurityException("Pkg " + callingPkg
                    + " cannot read channels for " + targetPkg + " in " + userId);
+16 −1
Original line number Diff line number Diff line
@@ -1961,10 +1961,25 @@ public class PreferencesHelper implements RankingConfig {
    @Override
    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
            boolean includeDeleted, boolean includeBundles) {
        return getNotificationChannels(pkg, uid, includeDeleted, includeBundles, false);
    }

    protected ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
            boolean includeDeleted, boolean includeBundles, boolean createPrefsIfNeeded) {
        if (createPrefsIfNeeded && !android.app.Flags.nmBinderPerfCacheChannels()) {
            Slog.wtf(TAG,
                    "getNotificationChannels called with createPrefsIfNeeded=true and flag off");
            createPrefsIfNeeded = false;
        }
        Objects.requireNonNull(pkg);
        List<NotificationChannel> channels = new ArrayList<>();
        synchronized (mLock) {
            PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
            PackagePreferences r;
            if (createPrefsIfNeeded) {
                r = getOrCreatePackagePreferencesLocked(pkg, uid);
            } else {
                r = getPackagePreferencesLocked(pkg, uid);
            }
            if (r == null) {
                return ParceledListSlice.emptyList();
            }
Loading