diff options
6 files changed, 80 insertions, 21 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 69a5b35a5b12..0ee7344e0e4d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -272,6 +272,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.io.IoUtils; @@ -403,6 +404,7 @@ public class NotificationManagerService extends SystemService { private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L; private IActivityManager mAm; + private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; private IPackageManager mPackageManager; private PackageManager mPackageManagerClient; @@ -1905,10 +1907,10 @@ public class NotificationManagerService extends SystemService { ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper, NotificationUsageStats usageStats, AtomicFile policyFile, ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am, - UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm, - IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, - UserManager userManager, NotificationHistoryManager historyManager, - StatsManager statsManager) { + ActivityTaskManagerInternal atm, UsageStatsManagerInternal appUsageStats, + DevicePolicyManagerInternal dpm, IUriGrantsManager ugm, + UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager, + NotificationHistoryManager historyManager, StatsManager statsManager) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -1918,6 +1920,7 @@ public class NotificationManagerService extends SystemService { mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); mAm = am; + mAtm = atm; mUgm = ugm; mUgmInternal = ugmInternal; mPackageManager = packageManager; @@ -2104,6 +2107,7 @@ public class NotificationManagerService extends SystemService { systemDir, "notification_policy.xml"), "notification-policy"), (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE), getGroupHelper(), ActivityManager.getService(), + LocalServices.getService(ActivityTaskManagerInternal.class), LocalServices.getService(UsageStatsManagerInternal.class), LocalServices.getService(DevicePolicyManagerInternal.class), UriGrantsManager.getService(), @@ -2835,7 +2839,9 @@ public class NotificationManagerService extends SystemService { return; } - if (callback != null && !appIsForeground && !isSystemToast && isCustom) { + boolean isAppRenderedToast = (callback != null); + if (isAppRenderedToast && isCustom && !isSystemToast + && !isPackageInForegroundForToast(pkg, callingUid)) { boolean block; long id = Binder.clearCallingIdentity(); try { @@ -2913,6 +2919,28 @@ public class NotificationManagerService extends SystemService { } } + /** + * Implementation note: Our definition of foreground for toasts is an implementation matter + * and should strike a balance between functionality and anti-abuse effectiveness. We + * currently worry about the following cases: + * <ol> + * <li>App with fullscreen activity: Allow toasts + * <li>App behind translucent activity from other app: Block toasts + * <li>App in multi-window: Allow toasts + * <li>App with expanded bubble: Allow toasts + * <li>App posting toasts on onCreate(), onStart(), onResume(): Allow toasts + * <li>App posting toasts on onPause(), onStop(), onDestroy(): Block toasts + * </ol> + * Checking if the UID has any resumed activities satisfy use-cases above. + * + * <p>Checking if {@code mActivityManager.getUidImportance(callingUid) == + * IMPORTANCE_FOREGROUND} does not work because it considers the app in foreground if it has + * any visible activities, failing case 2 in list above. + */ + private boolean isPackageInForegroundForToast(String pkg, int callingUid) { + return mAtm.hasResumedActivity(callingUid); + } + @Override public void cancelToast(String pkg, IBinder token) { Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index ca856ca7c1e6..edc87e5a4d88 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -169,6 +169,11 @@ public abstract class ActivityTaskManagerInternal { public abstract List<IBinder> getTopVisibleActivities(); /** + * Returns whether {@code uid} has any resumed activity. + */ + public abstract boolean hasResumedActivity(int uid); + + /** * Notify listeners that contents are drawn for the first time on a single task display. * * @param displayId An ID of the display on which contents are drawn. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7bacc427feb8..890b9454e6b6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -6172,6 +6172,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean hasResumedActivity(int uid) { + synchronized (mGlobalLock) { + final ArraySet<WindowProcessController> processes = mProcessMap.getProcesses(uid); + for (int i = 0, n = processes.size(); i < n; i++) { + final WindowProcessController process = processes.valueAt(i); + if (process.hasResumedActivity()) { + return true; + } + } + } + return false; + } + + @Override public int startActivitiesAsPackage(String packageName, @Nullable String featureId, int userId, Intent[] intents, Bundle bOptions) { Objects.requireNonNull(intents, "intents"); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 32eb932ea0ed..e5400d849e71 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -751,6 +751,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return false; } + boolean hasResumedActivity() { + for (int i = mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord activity = mActivities.get(i); + if (activity.isState(RESUMED)) { + return true; + } + } + return false; + } + void updateIntentForHeavyWeightActivity(Intent intent) { if (mActivities.isEmpty()) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 64d481a2e6a0..6315ba6b2ee2 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -17,7 +17,6 @@ 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.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.FLAG_AUTO_CANCEL; @@ -41,8 +40,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -167,6 +164,7 @@ import com.android.server.notification.NotificationManagerService.NotificationAs import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; @@ -253,6 +251,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock IActivityManager mAm; @Mock + ActivityTaskManagerInternal mAtm; + @Mock IUriGrantsManager mUgm; @Mock UriGrantsManagerInternal mUgmInternal; @@ -442,7 +442,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, - mGroupHelper, mAm, mAppUsageStats, + mGroupHelper, mAm, mAtm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); @@ -4632,8 +4632,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); - // this app is in the foreground - when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND); + setAppInForegroundForToasts(mUid, true); // enqueue toast -> toast should still enqueue ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), @@ -4651,8 +4650,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); - // this app is NOT in the foreground - when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE); + setAppInForegroundForToasts(mUid, false); // enqueue toast -> no toasts enqueued ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), @@ -4670,8 +4668,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); - // this app is in the foreground - when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND); + setAppInForegroundForToasts(mUid, true); // enqueue toast -> toast should still enqueue ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), @@ -4689,8 +4686,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); - // this app is NOT in the foreground - when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE); + setAppInForegroundForToasts(mUid, false); // enqueue toast -> toast should still enqueue ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), @@ -4748,8 +4744,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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); + setAppInForegroundForToasts(mUid, false); // enqueue toast -> no toasts enqueued ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), @@ -4771,8 +4766,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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); + setAppInForegroundForToasts(mUid, false); // enqueue toast -> system toast can still be enqueued ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), @@ -4780,6 +4774,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(1, mService.mToastQueue.size()); } + private void setAppInForegroundForToasts(int uid, boolean inForeground) { + int importance = (inForeground) ? IMPORTANCE_FOREGROUND : IMPORTANCE_NONE; + when(mActivityManager.getUidImportance(mUid)).thenReturn(importance); + when(mAtm.hasResumedActivity(uid)).thenReturn(inForeground); + } + @Test public void testOnPanelRevealedAndHidden() { int items = 5; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index e4d50c0e1786..19ff683a46a1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -65,6 +65,7 @@ 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.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; @@ -147,6 +148,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(SnoozeHelper.class), mock(NotificationUsageStats.class), mock(AtomicFile.class), mock(ActivityManager.class), mock(GroupHelper.class), mock(IActivityManager.class), + mock(ActivityTaskManagerInternal.class), mock(UsageStatsManagerInternal.class), mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class), mock(UriGrantsManagerInternal.class), |