Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9030d39 authored by Christopher Tate's avatar Christopher Tate Committed by Chris Tate
Browse files

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
parent 01991ab3
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -560,6 +560,14 @@ public abstract class ActivityManagerInternal {
    @Nullable
    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.
     */
+6 −6
Original line number Diff line number Diff line
@@ -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);
        }
    }
+55 −12
Original line number Diff line number Diff line
@@ -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,13 +4877,13 @@ 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()
                            + ", uid=" + owningUid
                            + ", (need uid=" + uid + ")"
                            + " is not allowed to send as package " + packageName;
                    Slog.w(TAG, msg);
@@ -4875,12 +4892,12 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            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);
@@ -16063,6 +16080,32 @@ public class ActivityManagerService extends IActivityManager.Stub
            return ActivityManagerService.this.getIntentForIntentSender(sender);
        }
        @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
+5 −3
Original line number Diff line number Diff line
@@ -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(),