Loading services/core/java/com/android/server/notification/NotificationManagerService.java +25 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; Loading Loading @@ -2001,7 +2002,8 @@ public class NotificationManagerService extends SystemService { return mInternalService; } private final IBinder mService = new INotificationManager.Stub() { @VisibleForTesting final IBinder mService = new INotificationManager.Stub() { // Toasts // ============================================================================ Loading @@ -2015,22 +2017,31 @@ public class NotificationManagerService extends SystemService { } if (pkg == null || callback == null) { Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback); return ; } final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg)); final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, Binder.getCallingUid()); if (ENABLE_BLOCKED_TOASTS && !isSystemToast && (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid()) || isPackageSuspended)) { final int callingUid = Binder.getCallingUid(); final boolean isSystemToast = isCallerSystemOrPhone() || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg); final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); long callingIdentity = Binder.clearCallingIdentity(); try { final boolean appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended by administrator." + (isPackageSuspended ? " due to package suspended." : " by user request.")); return; } } finally { Binder.restoreCallingIdentity(callingIdentity); } synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +109 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.EXTRA_BLOCKED_STATE; import static android.app.NotificationManager.IMPORTANCE_HIGH; Loading Loading @@ -74,6 +76,7 @@ import android.app.Notification.MessagingStyle.Message; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.ITransientNotification; import android.app.IUriGrantsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; Loading Loading @@ -118,12 +121,14 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.statusbar.NotificationVisibility; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiServiceTestCase; import com.android.server.lights.Light; import com.android.server.lights.LightsManager; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; Loading Loading @@ -160,6 +165,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private IPackageManager mPackageManager; @Mock private PackageManager mPackageManagerClient; @Mock private WindowManagerInternal mWindowManagerInternal; private TestableContext mContext = spy(getContext()); private final String PKG = mContext.getPackageName(); private TestableLooper mTestableLooper; Loading Loading @@ -238,6 +245,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { } @Override public void hide() { } } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading @@ -249,6 +266,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); mService = new TestableNotificationManagerService(mContext); Loading Loading @@ -302,6 +321,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; Loading Loading @@ -3548,4 +3568,93 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, captor.getValue().getNotification().flags); } @Test public void testAllowForegroundToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); // notifications from this package are blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND); // enqueue toast -> toast should still enqueue ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(1, mService.mToastQueue.size()); } @Test public void testDisallowToastsFromSuspendedPackages() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(true); // notifications from this package are NOT blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW); // enqueue toast -> no toasts enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(0, mService.mToastQueue.size()); } @Test public void testDisallowToastsFromBlockedApps() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); // notifications from this package are blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is NOT in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE); // enqueue toast -> no toasts enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(0, mService.mToastQueue.size()); } @Test public void testAlwaysAllowSystemToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(true); // notifications from this package ARE blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is NOT in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE); // enqueue toast -> system toast can still be enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(1, mService.mToastQueue.size()); } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +25 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; Loading Loading @@ -2001,7 +2002,8 @@ public class NotificationManagerService extends SystemService { return mInternalService; } private final IBinder mService = new INotificationManager.Stub() { @VisibleForTesting final IBinder mService = new INotificationManager.Stub() { // Toasts // ============================================================================ Loading @@ -2015,22 +2017,31 @@ public class NotificationManagerService extends SystemService { } if (pkg == null || callback == null) { Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback); return ; } final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg)); final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, Binder.getCallingUid()); if (ENABLE_BLOCKED_TOASTS && !isSystemToast && (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid()) || isPackageSuspended)) { final int callingUid = Binder.getCallingUid(); final boolean isSystemToast = isCallerSystemOrPhone() || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg); final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid); final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg, callingUid); long callingIdentity = Binder.clearCallingIdentity(); try { final boolean appIsForeground = mActivityManager.getUidImportance(callingUid) == IMPORTANCE_FOREGROUND; if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage && !appIsForeground) || isPackageSuspended)) { Slog.e(TAG, "Suppressing toast from package " + pkg + (isPackageSuspended ? " due to package suspended by administrator." + (isPackageSuspended ? " due to package suspended." : " by user request.")); return; } } finally { Binder.restoreCallingIdentity(callingIdentity); } synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +109 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.EXTRA_BLOCKED_STATE; import static android.app.NotificationManager.IMPORTANCE_HIGH; Loading Loading @@ -74,6 +76,7 @@ import android.app.Notification.MessagingStyle.Message; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.ITransientNotification; import android.app.IUriGrantsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; Loading Loading @@ -118,12 +121,14 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.statusbar.NotificationVisibility; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiServiceTestCase; import com.android.server.lights.Light; import com.android.server.lights.LightsManager; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; Loading Loading @@ -160,6 +165,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private IPackageManager mPackageManager; @Mock private PackageManager mPackageManagerClient; @Mock private WindowManagerInternal mWindowManagerInternal; private TestableContext mContext = spy(getContext()); private final String PKG = mContext.getPackageName(); private TestableLooper mTestableLooper; Loading Loading @@ -238,6 +245,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { } @Override public void hide() { } } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading @@ -249,6 +266,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); mService = new TestableNotificationManagerService(mContext); Loading Loading @@ -302,6 +321,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; Loading Loading @@ -3548,4 +3568,93 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, captor.getValue().getNotification().flags); } @Test public void testAllowForegroundToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); // notifications from this package are blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND); // enqueue toast -> toast should still enqueue ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(1, mService.mToastQueue.size()); } @Test public void testDisallowToastsFromSuspendedPackages() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(true); // notifications from this package are NOT blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW); // enqueue toast -> no toasts enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(0, mService.mToastQueue.size()); } @Test public void testDisallowToastsFromBlockedApps() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); // notifications from this package are blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is NOT in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE); // enqueue toast -> no toasts enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(0, mService.mToastQueue.size()); } @Test public void testAlwaysAllowSystemToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(true); // notifications from this package ARE blocked by the user mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); // this app is NOT in the foreground when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE); // enqueue toast -> system toast can still be enqueued ((INotificationManager)mService.mService).enqueueToast(testPackage, new TestableToastCallback(), 2000, 0); assertEquals(1, mService.mToastQueue.size()); } }