summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2021-04-09 10:25:37 -0700
committerChris Tate <ctate@android.com>2021-04-20 18:34:56 +0000
commite9030d39ad88588c487d6f62eaf89063d901b4b8 (patch)
tree0c9e24fe730a238841b863e2032b30f08f92a381
parent01991ab3f919d924746bbd3a367f47d617f07b75 (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
-rw-r--r--core/java/android/app/ActivityManagerInternal.java8
-rw-r--r--core/java/android/app/PendingIntent.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java67
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java8
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(),