summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bernardo Rufino <brufino@google.com> 2020-03-06 20:33:11 +0000
committer Bernardo Rufino <brufino@google.com> 2020-03-19 18:12:24 +0000
commite6cb3104ea432d13f167fb6cc882ee7e76ba19d9 (patch)
tree1921d6aac8599e1512ff8038f884940d866de38f
parentf0e7d48fec203c37d60b9bf6d654e7c1317303ee (diff)
Allow custom toasts only if app has resumed activity
Bug linked is a vulnerability where an app starts an external activity that it wants the user to interact with along with repeatedly posting custom toasts with the intent of redressing the UI of the activity just launched. The activity that is being started is translucent, so the check for IMPORTANCE_FOREGROUND wasn't working because activity manager was still considering the process foreground since it had a visible activity. Thus, changing our logic to only enable custom toasts in case the app has a resumed activity. This has the following implications in the cases below: * Translucent activity on top: Block toasts * Multi-window: Allow toasts * Bubble: Allows when bubble is expanded (it's a resumed activity) * SAW: Probably block, but fine since app can already do what they want * onCreate(), onStart(), onResume(): Allow toasts * onPause(), onStop(), onDestroy(): Block toasts Note that custom toasts are deprecated and we haven't specified what exactly "foreground" or "background" meant in this context, so we have some flexibility in the implementation. Bug: 115385786 Test: From an app start a translucent activity from another package and then try to post a toast, verify the toast is blocked. Test: atest ToastUITest android.widget.cts.ToastTest android.widget.cts29.ToastTest android.server.wm.ToastTest Change-Id: Ia434332e066f1ef2cd01e150b087f8e5117f1e63
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java38
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java10
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java32
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java2
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),