Loading services/core/java/com/android/server/notification/NotificationManagerService.java +18 −46 Original line number Diff line number Diff line Loading @@ -6746,6 +6746,8 @@ public class NotificationManagerService extends SystemService { return false; } mUsageStats.registerEnqueuedByAppAndAccepted(pkg); if (info != null) { // Cache the shortcut synchronously after the associated notification is posted in case // the app unpublishes this shortcut immediately after posting the notification. If the Loading Loading @@ -7184,11 +7186,12 @@ public class NotificationManagerService extends SystemService { + " cannot create notifications"); } // rate limit updates that aren't completed progress notifications if (mNotificationsByKey.get(r.getSbn().getKey()) != null && !r.getNotification().hasCompletedProgress() && !isAutogroup) { // Rate limit updates that aren't completed progress notifications // Search for the original one in the posted and not-yet-posted (enqueued) lists. boolean isUpdate = mNotificationsByKey.get(r.getSbn().getKey()) != null || findNotificationByListLocked(mEnqueuedNotifications, r.getSbn().getKey()) != null; if (isUpdate && !r.getNotification().hasCompletedProgress() && !isAutogroup) { final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg); if (appEnqueueRate > mMaxPackageEnqueueRate) { mUsageStats.registerOverRateQuota(pkg); Loading Loading @@ -7813,15 +7816,8 @@ public class NotificationManagerService extends SystemService { boolean posted = false; synchronized (mNotificationLock) { try { NotificationRecord r = null; int N = mEnqueuedNotifications.size(); for (int i = 0; i < N; i++) { final NotificationRecord enqueued = mEnqueuedNotifications.get(i); if (Objects.equals(key, enqueued.getKey())) { r = enqueued; break; } } NotificationRecord r = findNotificationByListLocked(mEnqueuedNotifications, key); if (r == null) { Slog.i(TAG, "Cannot find enqueued record for key: " + key); return false; Loading Loading @@ -9486,7 +9482,7 @@ public class NotificationManagerService extends SystemService { * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). */ private boolean notificationMatchesUserId(NotificationRecord r, int userId) { private static boolean notificationMatchesUserId(NotificationRecord r, int userId) { return // looking for USER_ALL notifications? match everything userId == UserHandle.USER_ALL Loading Loading @@ -9845,9 +9841,9 @@ public class NotificationManagerService extends SystemService { return null; } @GuardedBy("mNotificationLock") private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { @Nullable private static NotificationRecord findNotificationByListLocked( ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); Loading @@ -9860,8 +9856,7 @@ public class NotificationManagerService extends SystemService { return null; } @GuardedBy("mNotificationLock") private List<NotificationRecord> findNotificationsByListLocked( private static List<NotificationRecord> findNotificationsByListLocked( ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { List<NotificationRecord> matching = new ArrayList<>(); final int len = list.size(); Loading @@ -9876,9 +9871,9 @@ public class NotificationManagerService extends SystemService { return matching; } @GuardedBy("mNotificationLock") private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, String key) { @Nullable private static NotificationRecord findNotificationByListLocked( ArrayList<NotificationRecord> list, String key) { final int N = list.size(); for (int i = 0; i < N; i++) { if (key.equals(list.get(i).getKey())) { Loading @@ -9888,29 +9883,6 @@ public class NotificationManagerService extends SystemService { return null; } /** * There may be multiple records that match your criteria. For instance if there have been * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This * method will find all of them in the given list * @return */ @GuardedBy("mNotificationLock") private List<NotificationRecord> findEnqueuedNotificationsForCriteria( String pkg, String tag, int id, int userId) { final ArrayList<NotificationRecord> records = new ArrayList<>(); final int n = mEnqueuedNotifications.size(); for (int i = 0; i < n; i++) { NotificationRecord r = mEnqueuedNotifications.get(i); if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id && TextUtils.equals(r.getSbn().getTag(), tag) && r.getSbn().getPackageName().equals(pkg)) { records.add(r); } } return records; } @GuardedBy("mNotificationLock") int indexOfNotificationLocked(String key) { final int N = mNotificationList.size(); Loading services/core/java/com/android/server/notification/NotificationUsageStats.java +30 −30 Original line number Diff line number Diff line Loading @@ -16,23 +16,16 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_HIGH; import android.app.Notification; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteFullException; import android.database.sqlite.SQLiteOpenHelper; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.SystemClock; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.server.notification.NotificationManagerService.DumpFilter; Loading @@ -42,8 +35,6 @@ import org.json.JSONObject; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.Set; Loading @@ -63,7 +54,6 @@ public class NotificationUsageStats { private static final String TAG = "NotificationUsageStats"; private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = true; private static final boolean ENABLE_SQLITE_LOG = true; private static final AggregatedStats[] EMPTY_AGGREGATED_STATS = new AggregatedStats[0]; private static final String DEVICE_GLOBAL_STATS = "__global"; // packages start with letters private static final int MSG_EMIT = 1; Loading @@ -73,12 +63,15 @@ public class NotificationUsageStats { public static final int FOUR_HOURS = 1000 * 60 * 60 * 4; private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : FOUR_HOURS; // Guarded by synchronized(this). @GuardedBy("this") private final Map<String, AggregatedStats> mStats = new HashMap<>(); @GuardedBy("this") private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>(); @GuardedBy("this") private ArraySet<String> mStatExpiredkeys = new ArraySet<>(); private final Context mContext; private final Handler mHandler; @GuardedBy("this") private long mLastEmitTime; public NotificationUsageStats(Context context) { Loading @@ -105,11 +98,7 @@ public class NotificationUsageStats { */ public synchronized float getAppEnqueueRate(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); if (stats != null) { return stats.getEnqueueRate(SystemClock.elapsedRealtime()); } else { return 0f; } } /** Loading @@ -117,11 +106,7 @@ public class NotificationUsageStats { */ public synchronized boolean isAlertRateLimited(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); if (stats != null) { return stats.isAlertRateLimited(); } else { return false; } } /** Loading @@ -135,17 +120,33 @@ public class NotificationUsageStats { releaseAggregatedStatsLocked(aggregatedStatsArray); } /** * Called when a notification that was enqueued by an app is effectively enqueued to be * posted. This is after rate checking, to update the rate. * * <p>Note that if we updated the arrival estimate <em>before</em> checking it, then an app * enqueueing at slightly above the acceptable rate would never get their notifications * accepted; updating afterwards allows the rate to dip below the threshold and thus lets * through some of them. */ public synchronized void registerEnqueuedByAppAndAccepted(String packageName) { final long now = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(packageName); for (AggregatedStats stats : aggregatedStatsArray) { stats.updateInterarrivalEstimate(now); } releaseAggregatedStatsLocked(aggregatedStatsArray); } /** * Called when a notification has been posted. */ public synchronized void registerPostedByApp(NotificationRecord notification) { final long now = SystemClock.elapsedRealtime(); notification.stats.posttimeElapsedMs = now; notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numPostedByApp++; stats.updateInterarrivalEstimate(now); stats.countApiUse(notification); stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0); } Loading @@ -161,7 +162,6 @@ public class NotificationUsageStats { AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; stats.updateInterarrivalEstimate(SystemClock.elapsedRealtime()); stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); Loading Loading @@ -257,12 +257,12 @@ public class NotificationUsageStats { } } // Locked by this. @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) { return getAggregatedStatsLocked(record.getSbn().getPackageName()); } // Locked by this. @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(String packageName) { if (!ENABLE_AGGREGATED_IN_MEMORY_STATS) { return EMPTY_AGGREGATED_STATS; Loading @@ -277,7 +277,7 @@ public class NotificationUsageStats { return array; } // Locked by this. @GuardedBy("this") private void releaseAggregatedStatsLocked(AggregatedStats[] array) { for(int i = 0; i < array.length; i++) { array[i] = null; Loading @@ -285,7 +285,7 @@ public class NotificationUsageStats { mStatsArrays.offer(array); } // Locked by this. @GuardedBy("this") private AggregatedStats getOrCreateAggregatedStatsLocked(String key) { AggregatedStats result = mStats.get(key); if (result == null) { Loading services/core/java/com/android/server/notification/RateEstimator.java +5 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,10 @@ package com.android.server.notification; * * {@hide} */ public class RateEstimator { private static final double RATE_ALPHA = 0.8; class RateEstimator { private static final double RATE_ALPHA = 0.5; private static final double MINIMUM_DT = 0.0005; private Long mLastEventTime; private double mInterarrivalTime; Loading @@ -34,18 +35,12 @@ public class RateEstimator { } /** Update the estimate to account for an event that just happened. */ public float update(long now) { float rate; if (mLastEventTime == null) { // No last event time, rate is zero. rate = 0f; } else { public void update(long now) { if (mLastEventTime != null) { // Calculate the new inter-arrival time based on last event time. mInterarrivalTime = getInterarrivalEstimate(now); rate = (float) (1.0 / mInterarrivalTime); } mLastEventTime = now; return rate; } /** @return the estimated rate if there were a new event right now. */ Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +99 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.No import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; Loading Loading @@ -678,6 +679,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @After public void assertAllTrackersFinishedOrCancelled() { waitForIdle(); // Finish async work. // Verify that no trackers were left dangling. for (PostNotificationTracker tracker : mPostNotificationTrackerFactory.mCreatedTrackers) { assertThat(tracker.isOngoing()).isFalse(); Loading Loading @@ -11768,6 +11770,103 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(n.isUserInitiatedJob()); } @Test public void enqueue_updatesEnqueueRate() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); // Don't waitForIdle() here. We want to verify the "intermediate" state. verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats, never()).registerPostedByApp(any()); waitForIdle(); } @Test public void enqueue_withPost_updatesEnqueueRateAndPost() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats).registerPostedByApp(any()); } @Test public void enqueueNew_whenOverEnqueueRate_accepts() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); waitForIdle(); assertThat(mService.mNotificationsByKey).hasSize(1); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats).registerPostedByApp(any()); } @Test public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception { // Post the first version. Notification original = generateNotificationRecord(null).getNotification(); original.when = 111; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); waitForIdle(); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); reset(mUsageStats); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE - 1f); // Post the update. Notification update = generateNotificationRecord(null).getNotification(); update.when = 222; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats, never()).registerPostedByApp(any()); verify(mUsageStats).registerUpdatedByApp(any(), any()); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222); } @Test public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception { // Post the first version. Notification original = generateNotificationRecord(null).getNotification(); original.when = 111; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); waitForIdle(); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); reset(mUsageStats); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); // Post the update. Notification update = generateNotificationRecord(null).getNotification(); update.when = 222; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats, never()).registerEnqueuedByAppAndAccepted(any()); verify(mUsageStats, never()).registerPostedByApp(any()); verify(mUsageStats, never()).registerUpdatedByApp(any(), any()); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old } private void setDpmAppOppsExemptFromDismissal(boolean isOn) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java +52 −42 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +18 −46 Original line number Diff line number Diff line Loading @@ -6746,6 +6746,8 @@ public class NotificationManagerService extends SystemService { return false; } mUsageStats.registerEnqueuedByAppAndAccepted(pkg); if (info != null) { // Cache the shortcut synchronously after the associated notification is posted in case // the app unpublishes this shortcut immediately after posting the notification. If the Loading Loading @@ -7184,11 +7186,12 @@ public class NotificationManagerService extends SystemService { + " cannot create notifications"); } // rate limit updates that aren't completed progress notifications if (mNotificationsByKey.get(r.getSbn().getKey()) != null && !r.getNotification().hasCompletedProgress() && !isAutogroup) { // Rate limit updates that aren't completed progress notifications // Search for the original one in the posted and not-yet-posted (enqueued) lists. boolean isUpdate = mNotificationsByKey.get(r.getSbn().getKey()) != null || findNotificationByListLocked(mEnqueuedNotifications, r.getSbn().getKey()) != null; if (isUpdate && !r.getNotification().hasCompletedProgress() && !isAutogroup) { final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg); if (appEnqueueRate > mMaxPackageEnqueueRate) { mUsageStats.registerOverRateQuota(pkg); Loading Loading @@ -7813,15 +7816,8 @@ public class NotificationManagerService extends SystemService { boolean posted = false; synchronized (mNotificationLock) { try { NotificationRecord r = null; int N = mEnqueuedNotifications.size(); for (int i = 0; i < N; i++) { final NotificationRecord enqueued = mEnqueuedNotifications.get(i); if (Objects.equals(key, enqueued.getKey())) { r = enqueued; break; } } NotificationRecord r = findNotificationByListLocked(mEnqueuedNotifications, key); if (r == null) { Slog.i(TAG, "Cannot find enqueued record for key: " + key); return false; Loading Loading @@ -9486,7 +9482,7 @@ public class NotificationManagerService extends SystemService { * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). */ private boolean notificationMatchesUserId(NotificationRecord r, int userId) { private static boolean notificationMatchesUserId(NotificationRecord r, int userId) { return // looking for USER_ALL notifications? match everything userId == UserHandle.USER_ALL Loading Loading @@ -9845,9 +9841,9 @@ public class NotificationManagerService extends SystemService { return null; } @GuardedBy("mNotificationLock") private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { @Nullable private static NotificationRecord findNotificationByListLocked( ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); Loading @@ -9860,8 +9856,7 @@ public class NotificationManagerService extends SystemService { return null; } @GuardedBy("mNotificationLock") private List<NotificationRecord> findNotificationsByListLocked( private static List<NotificationRecord> findNotificationsByListLocked( ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { List<NotificationRecord> matching = new ArrayList<>(); final int len = list.size(); Loading @@ -9876,9 +9871,9 @@ public class NotificationManagerService extends SystemService { return matching; } @GuardedBy("mNotificationLock") private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, String key) { @Nullable private static NotificationRecord findNotificationByListLocked( ArrayList<NotificationRecord> list, String key) { final int N = list.size(); for (int i = 0; i < N; i++) { if (key.equals(list.get(i).getKey())) { Loading @@ -9888,29 +9883,6 @@ public class NotificationManagerService extends SystemService { return null; } /** * There may be multiple records that match your criteria. For instance if there have been * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This * method will find all of them in the given list * @return */ @GuardedBy("mNotificationLock") private List<NotificationRecord> findEnqueuedNotificationsForCriteria( String pkg, String tag, int id, int userId) { final ArrayList<NotificationRecord> records = new ArrayList<>(); final int n = mEnqueuedNotifications.size(); for (int i = 0; i < n; i++) { NotificationRecord r = mEnqueuedNotifications.get(i); if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id && TextUtils.equals(r.getSbn().getTag(), tag) && r.getSbn().getPackageName().equals(pkg)) { records.add(r); } } return records; } @GuardedBy("mNotificationLock") int indexOfNotificationLocked(String key) { final int N = mNotificationList.size(); Loading
services/core/java/com/android/server/notification/NotificationUsageStats.java +30 −30 Original line number Diff line number Diff line Loading @@ -16,23 +16,16 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_HIGH; import android.app.Notification; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteFullException; import android.database.sqlite.SQLiteOpenHelper; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.SystemClock; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.server.notification.NotificationManagerService.DumpFilter; Loading @@ -42,8 +35,6 @@ import org.json.JSONObject; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.Set; Loading @@ -63,7 +54,6 @@ public class NotificationUsageStats { private static final String TAG = "NotificationUsageStats"; private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = true; private static final boolean ENABLE_SQLITE_LOG = true; private static final AggregatedStats[] EMPTY_AGGREGATED_STATS = new AggregatedStats[0]; private static final String DEVICE_GLOBAL_STATS = "__global"; // packages start with letters private static final int MSG_EMIT = 1; Loading @@ -73,12 +63,15 @@ public class NotificationUsageStats { public static final int FOUR_HOURS = 1000 * 60 * 60 * 4; private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : FOUR_HOURS; // Guarded by synchronized(this). @GuardedBy("this") private final Map<String, AggregatedStats> mStats = new HashMap<>(); @GuardedBy("this") private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>(); @GuardedBy("this") private ArraySet<String> mStatExpiredkeys = new ArraySet<>(); private final Context mContext; private final Handler mHandler; @GuardedBy("this") private long mLastEmitTime; public NotificationUsageStats(Context context) { Loading @@ -105,11 +98,7 @@ public class NotificationUsageStats { */ public synchronized float getAppEnqueueRate(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); if (stats != null) { return stats.getEnqueueRate(SystemClock.elapsedRealtime()); } else { return 0f; } } /** Loading @@ -117,11 +106,7 @@ public class NotificationUsageStats { */ public synchronized boolean isAlertRateLimited(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); if (stats != null) { return stats.isAlertRateLimited(); } else { return false; } } /** Loading @@ -135,17 +120,33 @@ public class NotificationUsageStats { releaseAggregatedStatsLocked(aggregatedStatsArray); } /** * Called when a notification that was enqueued by an app is effectively enqueued to be * posted. This is after rate checking, to update the rate. * * <p>Note that if we updated the arrival estimate <em>before</em> checking it, then an app * enqueueing at slightly above the acceptable rate would never get their notifications * accepted; updating afterwards allows the rate to dip below the threshold and thus lets * through some of them. */ public synchronized void registerEnqueuedByAppAndAccepted(String packageName) { final long now = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(packageName); for (AggregatedStats stats : aggregatedStatsArray) { stats.updateInterarrivalEstimate(now); } releaseAggregatedStatsLocked(aggregatedStatsArray); } /** * Called when a notification has been posted. */ public synchronized void registerPostedByApp(NotificationRecord notification) { final long now = SystemClock.elapsedRealtime(); notification.stats.posttimeElapsedMs = now; notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numPostedByApp++; stats.updateInterarrivalEstimate(now); stats.countApiUse(notification); stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0); } Loading @@ -161,7 +162,6 @@ public class NotificationUsageStats { AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; stats.updateInterarrivalEstimate(SystemClock.elapsedRealtime()); stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); Loading Loading @@ -257,12 +257,12 @@ public class NotificationUsageStats { } } // Locked by this. @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) { return getAggregatedStatsLocked(record.getSbn().getPackageName()); } // Locked by this. @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(String packageName) { if (!ENABLE_AGGREGATED_IN_MEMORY_STATS) { return EMPTY_AGGREGATED_STATS; Loading @@ -277,7 +277,7 @@ public class NotificationUsageStats { return array; } // Locked by this. @GuardedBy("this") private void releaseAggregatedStatsLocked(AggregatedStats[] array) { for(int i = 0; i < array.length; i++) { array[i] = null; Loading @@ -285,7 +285,7 @@ public class NotificationUsageStats { mStatsArrays.offer(array); } // Locked by this. @GuardedBy("this") private AggregatedStats getOrCreateAggregatedStatsLocked(String key) { AggregatedStats result = mStats.get(key); if (result == null) { Loading
services/core/java/com/android/server/notification/RateEstimator.java +5 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,10 @@ package com.android.server.notification; * * {@hide} */ public class RateEstimator { private static final double RATE_ALPHA = 0.8; class RateEstimator { private static final double RATE_ALPHA = 0.5; private static final double MINIMUM_DT = 0.0005; private Long mLastEventTime; private double mInterarrivalTime; Loading @@ -34,18 +35,12 @@ public class RateEstimator { } /** Update the estimate to account for an event that just happened. */ public float update(long now) { float rate; if (mLastEventTime == null) { // No last event time, rate is zero. rate = 0f; } else { public void update(long now) { if (mLastEventTime != null) { // Calculate the new inter-arrival time based on last event time. mInterarrivalTime = getInterarrivalEstimate(now); rate = (float) (1.0 / mInterarrivalTime); } mLastEventTime = now; return rate; } /** @return the estimated rate if there were a new event right now. */ Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +99 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.No import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; Loading Loading @@ -678,6 +679,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @After public void assertAllTrackersFinishedOrCancelled() { waitForIdle(); // Finish async work. // Verify that no trackers were left dangling. for (PostNotificationTracker tracker : mPostNotificationTrackerFactory.mCreatedTrackers) { assertThat(tracker.isOngoing()).isFalse(); Loading Loading @@ -11768,6 +11770,103 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(n.isUserInitiatedJob()); } @Test public void enqueue_updatesEnqueueRate() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); // Don't waitForIdle() here. We want to verify the "intermediate" state. verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats, never()).registerPostedByApp(any()); waitForIdle(); } @Test public void enqueue_withPost_updatesEnqueueRateAndPost() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats).registerPostedByApp(any()); } @Test public void enqueueNew_whenOverEnqueueRate_accepts() throws Exception { Notification n = generateNotificationRecord(null).getNotification(); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); waitForIdle(); assertThat(mService.mNotificationsByKey).hasSize(1); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats).registerPostedByApp(any()); } @Test public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception { // Post the first version. Notification original = generateNotificationRecord(null).getNotification(); original.when = 111; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); waitForIdle(); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); reset(mUsageStats); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE - 1f); // Post the update. Notification update = generateNotificationRecord(null).getNotification(); update.when = 222; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); verify(mUsageStats, never()).registerPostedByApp(any()); verify(mUsageStats).registerUpdatedByApp(any(), any()); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222); } @Test public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception { // Post the first version. Notification original = generateNotificationRecord(null).getNotification(); original.when = 111; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); waitForIdle(); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); reset(mUsageStats); when(mUsageStats.getAppEnqueueRate(eq(PKG))) .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); // Post the update. Notification update = generateNotificationRecord(null).getNotification(); update.when = 222; mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); waitForIdle(); verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); verify(mUsageStats, never()).registerEnqueuedByAppAndAccepted(any()); verify(mUsageStats, never()).registerPostedByApp(any()); verify(mUsageStats, never()).registerUpdatedByApp(any(), any()); assertThat(mService.mNotificationList).hasSize(1); assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old } private void setDpmAppOppsExemptFromDismissal(boolean isOn) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java +52 −42 File changed.Preview size limit exceeded, changes collapsed. Show changes