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

Commit 480453dd authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Lazily create system reserved channels

To reduce memory consumption (permanent) and boot time (first time after
the flag is turned on), only create channels when they are immediately
needed.

Test: NotificationManagerServiceTest
Test: PreferencesHelperTest
Test: NotificationAssisistantServiceTest
Test: NotificationManagerTest
Flag: android.service.notification.notification_classification
Fixes: 374186404
Change-Id: I63e9e93283d78c4e98906cccb6638e95d9206a0a
parent 53c8e353
Loading
Loading
Loading
Loading
+10 −18
Original line number Diff line number Diff line
@@ -49,10 +49,6 @@ import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
@@ -108,9 +104,7 @@ import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
@@ -6924,21 +6918,19 @@ public class NotificationManagerService extends SystemService {
    @GuardedBy("mNotificationLock")
    @Nullable
    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
    private NotificationChannel getClassificationChannelLocked(NotificationRecord r,
            Bundle adjustments) {
        int type = adjustments.getInt(KEY_TYPE);
        if (TYPE_NEWS == type) {
            return mPreferencesHelper.getNotificationChannel(
                    r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false);
        } else if (TYPE_PROMOTION == type) {
            return mPreferencesHelper.getNotificationChannel(
                    r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false);
        } else if (TYPE_SOCIAL_MEDIA == type) {
            return mPreferencesHelper.getNotificationChannel(
                    r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false);
        } else if (TYPE_CONTENT_RECOMMENDATION == type) {
            return mPreferencesHelper.getNotificationChannel(
                    r.getSbn().getPackageName(), r.getUid(), RECS_ID, false);
        if (type >= TYPE_PROMOTION && type <= TYPE_CONTENT_RECOMMENDATION) {
            NotificationChannel channel = mPreferencesHelper.getReservedChannel(
                    r.getSbn().getPackageName(), r.getUid(), type);
            if (channel == null) {
                channel = mPreferencesHelper.createReservedChannel(
                        r.getSbn().getPackageName(), r.getUid(), type);
                handleSavePolicyFile();
            }
            return channel;
        }
        return null;
    }
+100 −52
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.notification;

import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
@@ -32,6 +33,10 @@ import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.notificationClassification;

import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -66,6 +71,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
@@ -549,10 +555,6 @@ public class PreferencesHelper implements RankingConfig {
                Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
            }

            if (notificationClassification()) {
                addReservedChannelsLocked(r);
            }

            if (r.uid == UNKNOWN_UID) {
                if (Flags.persistIncompleteRestoreData()) {
                    r.userId = userId;
@@ -587,7 +589,7 @@ public class PreferencesHelper implements RankingConfig {

    private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
            PackageManager.NameNotFoundException {
        if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
        if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
            // Not present
            return false;
        }
@@ -598,7 +600,7 @@ public class PreferencesHelper implements RankingConfig {
        }

        // Remove Default Channel.
        r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
        r.channels.remove(DEFAULT_CHANNEL_ID);

        return true;
    }
@@ -609,8 +611,8 @@ public class PreferencesHelper implements RankingConfig {
            return false;
        }

        if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
            r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
        if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
            r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString(
                    com.android.internal.R.string.default_notification_channel_label));
            return false;
        }
@@ -623,7 +625,7 @@ public class PreferencesHelper implements RankingConfig {
        // Create Default Channel
        NotificationChannel channel;
        channel = new NotificationChannel(
                NotificationChannel.DEFAULT_CHANNEL_ID,
                DEFAULT_CHANNEL_ID,
                mContext.getString(R.string.default_notification_channel_label),
                r.importance);
        channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
@@ -642,38 +644,25 @@ public class PreferencesHelper implements RankingConfig {
        return true;
    }

    private void addReservedChannelsLocked(PackagePreferences p) {
        if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) {
            NotificationChannel channel = new NotificationChannel(
                    NotificationChannel.PROMOTIONS_ID,
                    mContext.getString(R.string.promotional_notification_channel_label),
                    IMPORTANCE_LOW);
            p.channels.put(channel.getId(), channel);
        }

        if (!p.channels.containsKey(NotificationChannel.RECS_ID)) {
            NotificationChannel channel = new NotificationChannel(
                    NotificationChannel.RECS_ID,
                    mContext.getString(R.string.recs_notification_channel_label),
                    IMPORTANCE_LOW);
            p.channels.put(channel.getId(), channel);
        }

        if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) {
            NotificationChannel channel = new NotificationChannel(
                    NotificationChannel.NEWS_ID,
                    mContext.getString(R.string.news_notification_channel_label),
                    IMPORTANCE_LOW);
            p.channels.put(channel.getId(), channel);
        }

        if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) {
            NotificationChannel channel = new NotificationChannel(
                    NotificationChannel.SOCIAL_MEDIA_ID,
                    mContext.getString(R.string.social_notification_channel_label),
                    IMPORTANCE_LOW);
            p.channels.put(channel.getId(), channel);
    private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) {
        String label = "";
        switch (channelId) {
            case PROMOTIONS_ID:
                label = mContext.getString(R.string.promotional_notification_channel_label);
                break;
            case RECS_ID:
                label = mContext.getString(R.string.recs_notification_channel_label);
                break;
            case NEWS_ID:
                label = mContext.getString(R.string.news_notification_channel_label);
                break;
            case SOCIAL_MEDIA_ID:
                label = mContext.getString(R.string.social_notification_channel_label);
                break;
        }
        NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW);
        p.channels.put(channelId, channel);
        return channel;
    }

    public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
@@ -1078,7 +1067,7 @@ public class PreferencesHelper implements RankingConfig {
            if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
                throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
            }
            if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
            if (DEFAULT_CHANNEL_ID.equals(channel.getId())) {
                throw new IllegalArgumentException("Reserved id");
            }
            // Only the user can update bundle channel settings
@@ -1411,6 +1400,54 @@ public class PreferencesHelper implements RankingConfig {
        }
    }

    private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
        switch (type) {
            case TYPE_CONTENT_RECOMMENDATION:
                return RECS_ID;
            case TYPE_NEWS:
                return NEWS_ID;
            case TYPE_PROMOTION:
                return PROMOTIONS_ID;
            case TYPE_SOCIAL_MEDIA:
                return SOCIAL_MEDIA_ID;
        }
        return null;
    }

    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
    public NotificationChannel getReservedChannel(String pkg, int uid,
            @Adjustment.Types int type) {
        if (!notificationClassification()) {
            return null;
        }
        Objects.requireNonNull(pkg);
        String channelId = getChannelIdForBundleType(type);
        if (channelId == null) {
            return null;
        }
        NotificationChannel channel =
                getConversationNotificationChannel(pkg, uid, channelId, null, true, false);
        return channel;
    }

    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
    public NotificationChannel createReservedChannel(String pkg, int uid,
            @Adjustment.Types int type) {
        if (!notificationClassification()) {
            return null;
        }
        Objects.requireNonNull(pkg);
        PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
        if (r == null) {
            return null;
        }
        String channelId = getChannelIdForBundleType(type);
        if (channelId == null) {
            return null;
        }
        return addReservedChannelLocked(r, channelId);
    }

    @Override
    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
            boolean includeDeleted) {
@@ -1429,7 +1466,7 @@ public class PreferencesHelper implements RankingConfig {
                return null;
            }
            if (channelId == null) {
                channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
                channelId = DEFAULT_CHANNEL_ID;
            }
            NotificationChannel channel = null;
            if (conversationId != null) {
@@ -1540,7 +1577,7 @@ public class PreferencesHelper implements RankingConfig {
            int N = r.channels.size() - 1;
            for (int i = N; i >= 0; i--) {
                String key = r.channels.keyAt(i);
                if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
                if (!DEFAULT_CHANNEL_ID.equals(key)) {
                    r.channels.remove(key);
                }
            }
@@ -1658,10 +1695,7 @@ public class PreferencesHelper implements RankingConfig {
                        && (activeChannelFilter == null
                                || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
                                || activeChannelFilter.contains(nc.getId()))
                        && !PROMOTIONS_ID.equals(nc.getId())
                        && !NEWS_ID.equals(nc.getId())
                        && !SOCIAL_MEDIA_ID.equals(nc.getId())
                        && !RECS_ID.equals(nc.getId());
                        && !SYSTEM_RESERVED_IDS.contains(nc.getId());
                if (includeChannel) {
                    if (nc.getGroup() != null) {
                        if (r.groups.get(nc.getGroup()) != null) {
@@ -1924,10 +1958,24 @@ public class PreferencesHelper implements RankingConfig {
    public boolean onlyHasDefaultChannel(String pkg, int uid) {
        synchronized (mLock) {
            PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
            if (r.channels.size() == (notificationClassification() ? 5 : 1)
                    && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
            if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
                if (r.channels.size() == 1) {
                    return true;
                }
                if (notificationClassification()) {
                    if (r.channels.size() <= 5) {
                        for (NotificationChannel c : r.channels.values()) {
                            if (!SYSTEM_RESERVED_IDS.contains(c.getId()) &&
                                    !DEFAULT_CHANNEL_ID.equals(c.getId())) {
                                return false;
                            }
                            return true;
                        }
                    } else {
                        return false;
                    }
                }
            }
            return false;
        }
    }
@@ -2744,9 +2792,9 @@ public class PreferencesHelper implements RankingConfig {
                PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
                if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
                    if (PackagePreferences.channels.containsKey(
                            NotificationChannel.DEFAULT_CHANNEL_ID)) {
                            DEFAULT_CHANNEL_ID)) {
                        PackagePreferences.channels.get(
                                NotificationChannel.DEFAULT_CHANNEL_ID).setName(
                                DEFAULT_CHANNEL_ID).setName(
                                context.getResources().getString(
                                        R.string.default_notification_channel_label));
                    }
+25 −1
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -98,7 +101,10 @@ import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
@@ -16767,6 +16773,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        r.applyAdjustments();
        assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
        signals.putInt(KEY_TYPE, TYPE_PROMOTION);
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r.applyAdjustments();
        assertThat(r.getChannel().getId()).isEqualTo(PROMOTIONS_ID);
        signals.putInt(KEY_TYPE, TYPE_SOCIAL_MEDIA);
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r.applyAdjustments();
        assertThat(r.getChannel().getId()).isEqualTo(SOCIAL_MEDIA_ID);
        signals.putInt(KEY_TYPE, TYPE_CONTENT_RECOMMENDATION);
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r.applyAdjustments();
        assertThat(r.getChannel().getId()).isEqualTo(RECS_ID);
    }
    @Test
@@ -17066,7 +17090,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
    public void testAppCannotUseReservedBundleChannels() throws Exception {
        mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
        mService.mPreferencesHelper.createReservedChannel(mPkg, mUid, TYPE_NEWS);
        NotificationChannel news = mBinderService.getNotificationChannel(
                mPkg, mContext.getUserId(), mPkg, NEWS_ID);
        assertThat(news).isNotNull();
+60 −132

File changed.

Preview size limit exceeded, changes collapsed.