diff options
author | Christopher Tate <ctate@google.com> | 2021-04-09 10:25:37 -0700 |
---|---|---|
committer | Chris Tate <ctate@android.com> | 2021-04-20 18:34:56 +0000 |
commit | e9030d39ad88588c487d6f62eaf89063d901b4b8 (patch) | |
tree | 0c9e24fe730a238841b863e2032b30f08f92a381 | |
parent | 01991ab3f919d924746bbd3a367f47d617f07b75 (diff) |
Synthesize app PendingIntents rather than wrap them
Sometimes it's important that the system create PendingIntents that are
owned by their target app, rather than by the system itself. Make that
possible, and use them in the Notification auto-grouping mechanism.
This makes the auto group Notification's undering PendingIntent how has
the same capabilities as the app whose notifications it elides, rather
than having the system's own capabilities.
Along the way, build the PendingIntent mutability-check exception string
only when it's going to be used, not every time any PendingIntent is
created.
Bug: 174243774
Test: atest CtsAppTestCases:PendingIntentTest
Test: atest CtsAppTestCases:NotificationManagerTest
Test: atest NotificationManagerServiceTest
Change-Id: I5ec03706bc439d305b0093a1ee9c5d381024029c
4 files changed, 74 insertions, 21 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 080352408a67..605340061994 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -561,6 +561,14 @@ public abstract class ActivityManagerInternal { public abstract Intent getIntentForIntentSender(IIntentSender sender); /** + * Effectively PendingIntent.getActivityForUser(), but the PendingIntent is + * owned by the given uid rather than by the caller (i.e. the system). + */ + public abstract PendingIntent getPendingIntentActivityAsApp( + int requestCode, @NonNull Intent intent, int flags, Bundle options, + String ownerPkgName, int ownerUid); + + /** * @return mBootTimeTempAllowlistDuration of ActivityManagerConstants. */ public abstract long getBootTimeTempAllowListDuration(); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index ca0868310dee..0136a35e3975 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -358,12 +358,6 @@ public final class PendingIntent implements Parcelable { private static void checkFlags(int flags, String packageName) { final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0; final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0; - String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S - + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE" - + " be specified when creating a PendingIntent.\nStrongly consider" - + " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality" - + " depends on the PendingIntent being mutable, e.g. if it needs to" - + " be used with inline replies or bubbles."; if (flagImmutableSet && flagMutableSet) { throw new IllegalArgumentException( @@ -372,6 +366,12 @@ public final class PendingIntent implements Parcelable { if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED) && !flagImmutableSet && !flagMutableSet) { + String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S + + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE" + + " be specified when creating a PendingIntent.\nStrongly consider" + + " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality" + + " depends on the PendingIntent being mutable, e.g. if it needs to" + + " be used with inline replies or bubbles."; throw new IllegalArgumentException(msg); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4b640a61f1dd..ded66d5dc135 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -27,6 +27,7 @@ import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART; +import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.PROCESS_STATE_TOP; @@ -4814,10 +4815,27 @@ public class ActivityManagerService extends IActivityManager.Stub public IIntentSender getIntentSenderWithFeature(int type, String packageName, String featureId, IBinder token, String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions, int userId) { + enforceNotIsolatedCaller("getIntentSender"); + + return getIntentSenderWithFeatureAsApp(type, packageName, featureId, token, resultWho, + requestCode, intents, resolvedTypes, flags, bOptions, userId, + Binder.getCallingUid()); + } + + /** + * System-internal callers can invoke this with owningUid being the app's own identity + * rather than the public API's behavior of always assigning ownership to the actual + * caller identity. This will create an IntentSender as though the package/userid/uid app + * were the caller, so that the ultimate PendingIntent is triggered with only the app's + * capabilities and not the system's. Used in cases like notification groups where + * the OS must synthesize a PendingIntent on an app's behalf. + */ + public IIntentSender getIntentSenderWithFeatureAsApp(int type, String packageName, + String featureId, IBinder token, String resultWho, int requestCode, Intent[] intents, + String[] resolvedTypes, int flags, Bundle bOptions, int userId, int owningUid) { // NOTE: The service lock isn't held in this method because nothing in the method requires // the service lock to be held. - enforceNotIsolatedCaller("getIntentSender"); // Refuse possible leaked file descriptors if (intents != null) { if (intents.length < 1) { @@ -4848,9 +4866,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } - int callingUid = Binder.getCallingUid(); int origUserId = userId; - userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, + userId = mUserController.handleIncomingUser(Binder.getCallingPid(), owningUid, userId, type == ActivityManager.INTENT_SENDER_BROADCAST, ALLOW_NON_FULL, "getIntentSender", null); if (origUserId == UserHandle.USER_CURRENT) { @@ -4860,27 +4877,27 @@ public class ActivityManagerService extends IActivityManager.Stub userId = UserHandle.USER_CURRENT; } try { - if (callingUid != 0 && callingUid != SYSTEM_UID) { + if (owningUid != 0 && owningUid != SYSTEM_UID) { final int uid = AppGlobals.getPackageManager().getPackageUid(packageName, - MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); - if (!UserHandle.isSameApp(callingUid, uid)) { + MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(owningUid)); + if (!UserHandle.isSameApp(owningUid, uid)) { String msg = "Permission Denial: getIntentSender() from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + ", (need uid=" + uid + ")" - + " is not allowed to send as package " + packageName; + + Binder.getCallingPid() + + ", uid=" + owningUid + + ", (need uid=" + uid + ")" + + " is not allowed to send as package " + packageName; Slog.w(TAG, msg); throw new SecurityException(msg); } } if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { - return mAtmInternal.getIntentSender(type, packageName, featureId, callingUid, + return mAtmInternal.getIntentSender(type, packageName, featureId, owningUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions); } return mPendingIntentController.getIntentSender(type, packageName, featureId, - callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, + owningUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions); } catch (RemoteException e) { throw new SecurityException(e); @@ -16064,6 +16081,32 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public PendingIntent getPendingIntentActivityAsApp( + int requestCode, @NonNull Intent intent, int flags, Bundle options, + String ownerPkg, int ownerUid) { + // system callers must explicitly set mutability state + final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0; + final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0; + if (flagImmutableSet == flagMutableSet) { + throw new IllegalArgumentException( + "Must set exactly one of FLAG_IMMUTABLE or FLAG_MUTABLE"); + } + + final Context context = ActivityManagerService.this.mContext; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); + intent.migrateExtraStreamToClipData(context); + intent.prepareToLeaveProcess(context); + IIntentSender target = + ActivityManagerService.this.getIntentSenderWithFeatureAsApp( + INTENT_SENDER_ACTIVITY, ownerPkg, + context.getAttributionTag(), null, null, requestCode, + new Intent[] { intent }, + resolvedType != null ? new String[] { resolvedType } : null, + flags, options, UserHandle.getUserId(ownerUid), ownerUid); + return target != null ? new PendingIntent(target) : null; + } + + @Override public long getBootTimeTempAllowListDuration() { // Do not lock ActivityManagerService.this here, this API is called by // PackageManagerService. diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 52a5dc1e8982..ef6f11ae7dbf 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5680,9 +5680,11 @@ public class NotificationManagerService extends SystemService { summaryNotification.extras.putAll(extras); Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg); if (appIntent != null) { - summaryNotification.contentIntent = PendingIntent.getActivityAsUser( - getContext(), 0, appIntent, PendingIntent.FLAG_IMMUTABLE, null, - UserHandle.of(userId)); + final ActivityManagerInternal ami = LocalServices + .getService(ActivityManagerInternal.class); + summaryNotification.contentIntent = ami.getPendingIntentActivityAsApp( + 0, appIntent, PendingIntent.FLAG_IMMUTABLE, null, + pkg, appInfo.uid); } final StatusBarNotification summarySbn = new StatusBarNotification(adjustedSbn.getPackageName(), |