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

Commit 48a9e264 authored by Julia Reynolds's avatar Julia Reynolds Committed by Android (Google) Code Review
Browse files

Merge "Check disqualifying notifications synchronously."

parents 986c32da d94054f9
Loading
Loading
Loading
Loading
+92 −77
Original line number Diff line number Diff line
@@ -2732,10 +2732,8 @@ public class NotificationManagerService extends SystemService {
                summaries.put(pkg, summarySbn.getKey());
            }
        }
        if (summaryRecord != null) {
            synchronized (mNotificationLock) {
                mEnqueuedNotifications.add(summaryRecord);
            }
        if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
                summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
            mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
        }
    }
@@ -2966,13 +2964,15 @@ public class NotificationManagerService extends SystemService {
                    + " notification=" + notification);
        }
        checkCallerIsSystemOrSameApp(pkg);
        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);

        final int userId = ActivityManager.handleIncomingUser(callingPid,
                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
        final UserHandle user = new UserHandle(userId);

        if (pkg == null || notification == null) {
            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
                    + " id=" + id + " notification=" + notification);
        }
        // Fix the notification as best we can.
        try {
            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
@@ -2986,11 +2986,7 @@ public class NotificationManagerService extends SystemService {

        mUsageStats.registerEnqueuedByApp(pkg);


        if (pkg == null || notification == null) {
            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
                    + " id=" + id + " notification=" + notification);
        }
        // setup local book-keeping
        String channelId = notification.getChannel();
        if (mIsTelevision && (new Notification.TvExtender(notification)).getChannel() != null) {
            channelId = (new Notification.TvExtender(notification)).getChannel();
@@ -3000,12 +2996,50 @@ public class NotificationManagerService extends SystemService {
        final StatusBarNotification n = new StatusBarNotification(
                pkg, opPkg, id, tag, callingUid, callingPid, notification,
                user, null, System.currentTimeMillis());
        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);

        if (!checkDisqualifyingFeatures(userId, callingUid, id,tag, r)) {
            return;
        }

        // Whitelist pending intents.
        if (notification.allPendingIntents != null) {
            final int intentCount = notification.allPendingIntents.size();
            if (intentCount > 0) {
                final ActivityManagerInternal am = LocalServices
                        .getService(ActivityManagerInternal.class);
                final long duration = LocalServices.getService(
                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
                for (int i = 0; i < intentCount; i++) {
                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                    if (pendingIntent != null) {
                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
                    }
                }
            }
        }

        mHandler.post(new EnqueueNotificationRunnable(userId, r));

        idOut[0] = id;
    }

    /**
     * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
     *
     * Has side effects.
     */
    private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
            NotificationRecord r) {
        final String pkg = r.sbn.getPackageName();
        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);

        // Limit the number of notifications that any given package except the android
        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
        if (!isSystemNotification && !isNotificationFromListener) {
            synchronized (mNotificationLock) {
                if (mNotificationsByKey.get(n.getKey()) != null) {
                if (mNotificationsByKey.get(r.sbn.getKey()) != null) {
                    // this is an update, rate limit updates only
                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
@@ -3016,16 +3050,18 @@ public class NotificationManagerService extends SystemService {
                                    + ". Shedding events. package=" + pkg);
                            mLastOverRateLogTime = now;
                        }
                        return;
                        return false;
                    }
                }

                int count = 0;
                final int N = mNotificationList.size();
                for (int i=0; i<N; i++) {
                    final NotificationRecord r = mNotificationList.get(i);
                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
                        if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
                    final NotificationRecord existing = mNotificationList.get(i);
                    if (existing.sbn.getPackageName().equals(pkg)
                            && existing.sbn.getUserId() == userId) {
                        if (existing.sbn.getId() == id
                                && TextUtils.equals(existing.sbn.getTag(), tag)) {
                            break;  // Allow updating existing notification
                        }
                        count++;
@@ -3033,35 +3069,53 @@ public class NotificationManagerService extends SystemService {
                            mUsageStats.registerOverCountQuota(pkg);
                            Slog.e(TAG, "Package has already posted " + count
                                    + " notifications.  Not showing more.  package=" + pkg);
                            return;
                            return false;
                        }
                    }
                }
            }
        }

        // Whitelist pending intents.
        if (notification.allPendingIntents != null) {
            final int intentCount = notification.allPendingIntents.size();
            if (intentCount > 0) {
                final ActivityManagerInternal am = LocalServices
                        .getService(ActivityManagerInternal.class);
                final long duration = LocalServices.getService(
                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
                for (int i = 0; i < intentCount; i++) {
                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                    if (pendingIntent != null) {
                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
        // snoozed apps
        if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
            // TODO: log to event log
            if (DBG) {
                Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
            }
            mSnoozeHelper.update(userId, r);
            savePolicyFile();
            return false;
        }


        // blocked apps
        if (isBlocked(r, mUsageStats)) {
            return false;
        }

        return true;
    }

        // setup local book-keeping
        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
        mHandler.post(new EnqueueNotificationRunnable(userId, r));
    protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
        final String pkg = r.sbn.getPackageName();
        final int callingUid = r.sbn.getUid();

        idOut[0] = id;
        final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
        if (isPackageSuspended) {
            Slog.e(TAG, "Suppressing notification from package due to package "
                    + "suspended by administrator.");
            usageStats.registerSuspendedByAdmin(r);
            return isPackageSuspended;
        }

        final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE
                || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
                || !noteNotificationOp(pkg, callingUid);
        if (isBlocked) {
            Slog.e(TAG, "Suppressing notification from package by user request.");
            usageStats.registerBlocked(r);
        }
        return isBlocked;
    }

    protected class EnqueueNotificationRunnable implements Runnable {
@@ -3079,16 +3133,6 @@ public class NotificationManagerService extends SystemService {
                mEnqueuedNotifications.add(r);
                scheduleTimeoutLocked(r);

                if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
                    // TODO: log to event log
                    if (DBG) {
                        Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
                    }
                    mSnoozeHelper.update(userId, r);
                    savePolicyFile();
                    return;
                }

                final StatusBarNotification n = r.sbn;
                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
@@ -3123,51 +3167,22 @@ public class NotificationManagerService extends SystemService {

                mRankingHelper.extractSignals(r);

                // blocked apps
                if (isBlocked(r, mUsageStats)) {
                    return;
                }

                // tell the assistant service about the notification
                if (mNotificationAssistants.isEnabled()) {
                    mNotificationAssistants.onNotificationEnqueued(r);
                    mHandler.postDelayed(new PostNotificationRunnable(userId, r.getKey()),
                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                            DELAY_FOR_ASSISTANT_TIME);
                } else {
                    mHandler.post(new PostNotificationRunnable(userId, r.getKey()));
                }
            }
        }

        protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
            final String pkg = r.sbn.getPackageName();
            final int callingUid = r.sbn.getUid();

            final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
            if (isPackageSuspended) {
                Slog.e(TAG, "Suppressing notification from package due to package "
                        + "suspended by administrator.");
                usageStats.registerSuspendedByAdmin(r);
                return isPackageSuspended;
                    mHandler.post(new PostNotificationRunnable(r.getKey()));
                }

            final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE
                    || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
                    || !noteNotificationOp(pkg, callingUid);
            if (isBlocked) {
                Slog.e(TAG, "Suppressing notification from package by user request.");
                    usageStats.registerBlocked(r);
            }
            return isBlocked;
        }
    }

    protected class PostNotificationRunnable implements Runnable {
        private final String key;
        private final int userId;

        PostNotificationRunnable(int userId, String key) {
            this.userId = userId;
        PostNotificationRunnable(String key) {
            this.key = key;
        }

+3 −12
Original line number Diff line number Diff line
@@ -223,10 +223,7 @@ public class NotificationManagerServiceTest {
        NotificationChannel channel = new NotificationChannel("id", "name",
                NotificationManager.IMPORTANCE_HIGH);
        NotificationRecord r = generateNotificationRecord(channel);
        NotificationManagerService.EnqueueNotificationRunnable enqueue =
                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
                        r);
        assertTrue(enqueue.isBlocked(r, usageStats));
        assertTrue(mNotificationManagerService.isBlocked(r, usageStats));
        verify(usageStats, times(1)).registerSuspendedByAdmin(eq(r));
    }

@@ -240,10 +237,7 @@ public class NotificationManagerServiceTest {
                NotificationManager.IMPORTANCE_HIGH);
        channel.setImportance(NotificationManager.IMPORTANCE_NONE);
        NotificationRecord r = generateNotificationRecord(channel);
        NotificationManagerService.EnqueueNotificationRunnable enqueue =
                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
                        r);
        assertTrue(enqueue.isBlocked(r, usageStats));
        assertTrue(mNotificationManagerService.isBlocked(r, usageStats));
        verify(usageStats, times(1)).registerBlocked(eq(r));
    }

@@ -257,10 +251,7 @@ public class NotificationManagerServiceTest {
                NotificationManager.IMPORTANCE_HIGH);
        NotificationRecord r = generateNotificationRecord(channel);
        r.setUserImportance(NotificationManager.IMPORTANCE_NONE);
        NotificationManagerService.EnqueueNotificationRunnable enqueue =
                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
                        r);
        assertTrue(enqueue.isBlocked(r, usageStats));
        assertTrue(mNotificationManagerService.isBlocked(r, usageStats));
        verify(usageStats, times(1)).registerBlocked(eq(r));
    }