Loading services/core/java/com/android/server/notification/NotificationManagerService.java +45 −34 Original line number Diff line number Diff line Loading @@ -380,8 +380,6 @@ public class NotificationManagerService extends SystemService { static final int INVALID_UID = -1; static final String ROOT_PKG = "root"; static final boolean ENABLE_BLOCKED_TOASTS = true; static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] { Adjustment.KEY_CONTEXTUAL_ACTIONS, Adjustment.KEY_TEXT_REPLIES, Loading Loading @@ -3165,34 +3163,11 @@ public class NotificationManagerService extends SystemService { } final int callingUid = Binder.getCallingUid(); final UserHandle callingUser = Binder.getCallingUserHandle(); checkCallerIsSameApp(pkg); final boolean isSystemToast = isCallerSystemOrPhone() || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg); final boolean isPackageSuspended = isPackagePaused(pkg); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); final boolean appIsForeground; final long callingIdentity = Binder.clearCallingIdentity(); try { appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; } finally { Binder.restoreCallingIdentity(callingIdentity); } if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended." : " by user request.")); return; } boolean isAppRenderedToast = (callback != null); if (blockToast(callingUid, isSystemToast, isAppRenderedToast)) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground at time the toast was posted"); if (!checkCanEnqueueToast(pkg, callingUid, isAppRenderedToast, isSystemToast)) { return; } Loading Loading @@ -3246,6 +3221,39 @@ public class NotificationManagerService extends SystemService { } } private boolean checkCanEnqueueToast(String pkg, int callingUid, boolean isAppRenderedToast, boolean isSystemToast) { final boolean isPackageSuspended = isPackagePaused(pkg); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); final boolean appIsForeground; final long callingIdentity = Binder.clearCallingIdentity(); try { appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; } finally { Binder.restoreCallingIdentity(callingIdentity); } if (!isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended." : " by user request.")); return false; } if (blockToast(callingUid, isSystemToast, isAppRenderedToast, isPackageInForegroundForToast(callingUid))) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground at time the toast was posted"); return false; } return true; } @Override public void cancelToast(String pkg, IBinder token) { Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token); Loading Loading @@ -7692,12 +7700,13 @@ public class NotificationManagerService extends SystemService { boolean isWithinQuota = mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG) || isExemptFromRateLimiting(record.pkg, userId); boolean isPackageInForeground = isPackageInForegroundForToast(record.uid); if (tryShowToast( record, rateLimitingEnabled, isWithinQuota)) { record, rateLimitingEnabled, isWithinQuota, isPackageInForeground)) { scheduleDurationReachedLocked(record, lastToastWasTextRecord); mIsCurrentToastShown = true; if (rateLimitingEnabled) { if (rateLimitingEnabled && !isPackageInForeground) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); } return; Loading @@ -7713,14 +7722,15 @@ public class NotificationManagerService extends SystemService { /** Returns true if it successfully showed the toast. */ private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled, boolean isWithinQuota) { if (rateLimitingEnabled && !isWithinQuota) { boolean isWithinQuota, boolean isPackageInForeground) { if (rateLimitingEnabled && !isWithinQuota && !isPackageInForeground) { reportCompatRateLimitingToastsChange(record.uid); Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); return false; } if (blockToast(record.uid, record.isSystemToast, record.isAppRendered())) { if (blockToast(record.uid, record.isSystemToast, record.isAppRendered(), isPackageInForeground)) { Slog.w(TAG, "Blocking custom toast from package " + record.pkg + " due to package not in the foreground at the time of showing the toast"); return false; Loading Loading @@ -7911,10 +7921,11 @@ public class NotificationManagerService extends SystemService { * with targetSdk < R. For apps with targetSdk R+, text toasts are not app-rendered, so * isAppRenderedToast == true means it's a custom toast. */ private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast) { private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast, boolean isPackageInForeground) { return isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(uid) && !isPackageInForeground && CompatChanges.isChangeEnabled(CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, uid); } Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +29 −26 Original line number Diff line number Diff line Loading @@ -5057,7 +5057,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test public void testToastRateLimiterCanPreventShowCallForCustomToast() throws Exception { public void testToastRateLimiterWontPreventShowCallForCustomToastWhenInForeground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; Loading @@ -5075,30 +5076,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token, callback, 2000, 0); verify(callback, times(0)).show(any()); } @Test public void testCustomToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached // Avoids rate limiting. setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); setAppInForegroundForToasts(mUid, true); Binder token = new Binder(); ITransientNotification callback = mock(ITransientNotification.class); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token, callback, 2000, 0); verify(callback).show(any()); verify(callback, times(1)).show(any()); } @Test Loading Loading @@ -5206,12 +5184,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test public void testToastRateLimiterCanPreventShowCallForTextToast() throws Exception { public void testToastRateLimiterCanPreventShowCallForTextToast_whenInBackground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false); setAppInForegroundForToasts(mUid, false); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) Loading @@ -5225,6 +5205,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); } @Test public void testToastRateLimiterWontPreventShowCallForTextToast_whenInForeground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false); setAppInForegroundForToasts(mUid, true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); Binder token = new Binder(); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); verify(mStatusBar, times(1)) .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); } @Test public void testTextToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception { final String testPackage = "testPackageName"; Loading @@ -5232,6 +5234,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true); setAppInForegroundForToasts(mUid, false); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) Loading Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +45 −34 Original line number Diff line number Diff line Loading @@ -380,8 +380,6 @@ public class NotificationManagerService extends SystemService { static final int INVALID_UID = -1; static final String ROOT_PKG = "root"; static final boolean ENABLE_BLOCKED_TOASTS = true; static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] { Adjustment.KEY_CONTEXTUAL_ACTIONS, Adjustment.KEY_TEXT_REPLIES, Loading Loading @@ -3165,34 +3163,11 @@ public class NotificationManagerService extends SystemService { } final int callingUid = Binder.getCallingUid(); final UserHandle callingUser = Binder.getCallingUserHandle(); checkCallerIsSameApp(pkg); final boolean isSystemToast = isCallerSystemOrPhone() || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg); final boolean isPackageSuspended = isPackagePaused(pkg); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); final boolean appIsForeground; final long callingIdentity = Binder.clearCallingIdentity(); try { appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; } finally { Binder.restoreCallingIdentity(callingIdentity); } if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended." : " by user request.")); return; } boolean isAppRenderedToast = (callback != null); if (blockToast(callingUid, isSystemToast, isAppRenderedToast)) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground at time the toast was posted"); if (!checkCanEnqueueToast(pkg, callingUid, isAppRenderedToast, isSystemToast)) { return; } Loading Loading @@ -3246,6 +3221,39 @@ public class NotificationManagerService extends SystemService { } } private boolean checkCanEnqueueToast(String pkg, int callingUid, boolean isAppRenderedToast, boolean isSystemToast) { final boolean isPackageSuspended = isPackagePaused(pkg); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); final boolean appIsForeground; final long callingIdentity = Binder.clearCallingIdentity(); try { appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; } finally { Binder.restoreCallingIdentity(callingIdentity); } if (!isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended." : " by user request.")); return false; } if (blockToast(callingUid, isSystemToast, isAppRenderedToast, isPackageInForegroundForToast(callingUid))) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground at time the toast was posted"); return false; } return true; } @Override public void cancelToast(String pkg, IBinder token) { Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token); Loading Loading @@ -7692,12 +7700,13 @@ public class NotificationManagerService extends SystemService { boolean isWithinQuota = mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG) || isExemptFromRateLimiting(record.pkg, userId); boolean isPackageInForeground = isPackageInForegroundForToast(record.uid); if (tryShowToast( record, rateLimitingEnabled, isWithinQuota)) { record, rateLimitingEnabled, isWithinQuota, isPackageInForeground)) { scheduleDurationReachedLocked(record, lastToastWasTextRecord); mIsCurrentToastShown = true; if (rateLimitingEnabled) { if (rateLimitingEnabled && !isPackageInForeground) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); } return; Loading @@ -7713,14 +7722,15 @@ public class NotificationManagerService extends SystemService { /** Returns true if it successfully showed the toast. */ private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled, boolean isWithinQuota) { if (rateLimitingEnabled && !isWithinQuota) { boolean isWithinQuota, boolean isPackageInForeground) { if (rateLimitingEnabled && !isWithinQuota && !isPackageInForeground) { reportCompatRateLimitingToastsChange(record.uid); Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); return false; } if (blockToast(record.uid, record.isSystemToast, record.isAppRendered())) { if (blockToast(record.uid, record.isSystemToast, record.isAppRendered(), isPackageInForeground)) { Slog.w(TAG, "Blocking custom toast from package " + record.pkg + " due to package not in the foreground at the time of showing the toast"); return false; Loading Loading @@ -7911,10 +7921,11 @@ public class NotificationManagerService extends SystemService { * with targetSdk < R. For apps with targetSdk R+, text toasts are not app-rendered, so * isAppRenderedToast == true means it's a custom toast. */ private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast) { private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast, boolean isPackageInForeground) { return isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(uid) && !isPackageInForeground && CompatChanges.isChangeEnabled(CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, uid); } Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +29 −26 Original line number Diff line number Diff line Loading @@ -5057,7 +5057,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test public void testToastRateLimiterCanPreventShowCallForCustomToast() throws Exception { public void testToastRateLimiterWontPreventShowCallForCustomToastWhenInForeground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; Loading @@ -5075,30 +5076,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token, callback, 2000, 0); verify(callback, times(0)).show(any()); } @Test public void testCustomToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached // Avoids rate limiting. setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); setAppInForegroundForToasts(mUid, true); Binder token = new Binder(); ITransientNotification callback = mock(ITransientNotification.class); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token, callback, 2000, 0); verify(callback).show(any()); verify(callback, times(1)).show(any()); } @Test Loading Loading @@ -5206,12 +5184,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test public void testToastRateLimiterCanPreventShowCallForTextToast() throws Exception { public void testToastRateLimiterCanPreventShowCallForTextToast_whenInBackground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false); setAppInForegroundForToasts(mUid, false); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) Loading @@ -5225,6 +5205,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); } @Test public void testToastRateLimiterWontPreventShowCallForTextToast_whenInForeground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false); setAppInForegroundForToasts(mUid, true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); Binder token = new Binder(); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); verify(mStatusBar, times(1)) .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); } @Test public void testTextToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception { final String testPackage = "testPackageName"; Loading @@ -5232,6 +5234,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.isSystemUid = false; setToastRateIsWithinQuota(false); // rate limit reached setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true); setAppInForegroundForToasts(mUid, false); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) Loading