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

Commit b2b91308 authored by Ricky Wai's avatar Ricky Wai Committed by Android (Google) Code Review
Browse files

Merge "Add hidden API to allow PendingIntent sender to allow/block BAL"

parents 1d29c3e8 f1f080fd
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -307,6 +308,7 @@ public class AlarmManagerService extends SystemService {
    BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
    BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
    BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic();
    ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();

    // TODO(b/172085676): Move inside alarm store.
    private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -1610,6 +1612,10 @@ public class AlarmManagerService extends SystemService {
    @Override
    public void onStart() {
        mInjector.init();
        mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
        mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
        mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false);
        mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
        mMetricsHelper = new MetricsHelper(getContext(), mLock);

        mListenerDeathRecipient = new IBinder.DeathRecipient() {
@@ -4339,7 +4345,16 @@ public class AlarmManagerService extends SystemService {
                    for (int i = 0; i < triggerList.size(); i++) {
                        Alarm alarm = triggerList.get(i);
                        try {
                            alarm.operation.send();
                            // Disallow AlarmManager to start random background activity.
                            final Bundle bundle;
                            if (alarm.operation.isActivity()) {
                                bundle = mActivityOptsRestrictBal.toBundle();
                            } else {
                                bundle = null;
                            }
                            alarm.operation.send(/* context */ null, /* code */0, /* intent */
                                    null, /* onFinished */null, /* handler */
                                    null, /* requiredPermission */ null, bundle);
                        } catch (PendingIntent.CanceledException e) {
                            if (alarm.repeatInterval > 0) {
                                // This IntentSender is no longer valid, but this
@@ -4901,9 +4916,19 @@ public class AlarmManagerService extends SystemService {
                    mSendCount++;

                    try {
                        final Bundle bundle;
                        if (alarm.mIdleOptions != null) {
                            bundle = alarm.mIdleOptions;
                        } else {
                            if (alarm.operation.isActivity()) {
                                bundle = mActivityOptsRestrictBal.toBundle();
                            } else {
                                bundle = null;
                            }
                        }
                        alarm.operation.send(getContext(), 0,
                                mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
                                mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
                                mDeliveryTracker, mHandler, null, bundle);
                    } catch (PendingIntent.CanceledException e) {
                        if (alarm.repeatInterval > 0) {
                            // This IntentSender is no longer valid, but this
+36 −0
Original line number Diff line number Diff line
@@ -166,6 +166,14 @@ public class ActivityOptions {
     */
    public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";

    /**
     * PendingIntent caller allows activity start even if PendingIntent creator is in background.
     * This only works if the PendingIntent caller is allowed to start background activities,
     * for example if it's in the foreground, or has BAL permission.
     * @hide
     */
    public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED =
            "android.pendingIntent.backgroundActivityAllowed";
    /**
     * Callback for when the last frame of the animation is played.
     * @hide
@@ -380,6 +388,12 @@ public class ActivityOptions {
    /** @hide */
    public static final int ANIM_REMOTE_ANIMATION = 13;

    /**
     * Default value for KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED.
     * @hide
     **/
    public static final boolean PENDING_INTENT_BAL_ALLOWED_DEFAULT = true;

    private String mPackageName;
    private Rect mLaunchBounds;
    private int mAnimationType = ANIM_UNDEFINED;
@@ -431,6 +445,7 @@ public class ActivityOptions {
    private String mSplashScreenThemeResName;
    @SplashScreen.SplashScreenStyle
    private int mSplashScreenStyle;
    private boolean mPendingIntentBalAllowed = PENDING_INTENT_BAL_ALLOWED_DEFAULT;
    private boolean mRemoveWithTaskOrganizer;
    private boolean mLaunchedFromBubble;
    private boolean mTransientLaunch;
@@ -1184,6 +1199,8 @@ public class ActivityOptions {
        mRemoteTransition = opts.getParcelable(KEY_REMOTE_TRANSITION);
        mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
        mSplashScreenThemeResName = opts.getString(KEY_SPLASH_SCREEN_THEME);
        mPendingIntentBalAllowed = opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
                PENDING_INTENT_BAL_ALLOWED_DEFAULT);
        mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
        mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
        mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
@@ -1399,6 +1416,24 @@ public class ActivityOptions {
        return mSplashScreenStyle;
    }

    /**
     * Set PendingIntent activity is allowed to be started in the background if the caller
     * can start background activities.
     * @hide
     */
    public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
        mPendingIntentBalAllowed = allowed;
    }

    /**
     * Get PendingIntent activity is allowed to be started in the background if the caller
     * can start background activities.
     * @hide
     */
    public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
        return mPendingIntentBalAllowed;
    }

    /**
     * Sets whether the activity is to be launched into LockTask mode.
     *
@@ -1972,6 +2007,7 @@ public class ActivityOptions {
        if (mSplashScreenThemeResName != null && !mSplashScreenThemeResName.isEmpty()) {
            b.putString(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResName);
        }
        b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
        if (mRemoveWithTaskOrganizer) {
            b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer);
        }
+35 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ public class BroadcastOptions {
    private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
    private boolean mDontSendToRestrictedApps = false;
    private boolean mAllowBackgroundActivityStarts;
    private boolean mPendingIntentBalAllowed = ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;

    /**
     * How long to temporarily put an app on the power allowlist when executing this broadcast
@@ -78,6 +79,16 @@ public class BroadcastOptions {
    private static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
            "android:broadcast.dontSendToRestrictedApps";

    /**
     * PendingIntent caller allows activity start even if PendingIntent creator is in background.
     * This only works if the PendingIntent caller is allowed to start background activities,
     * for example if it's in the foreground, or has BAL permission.
     * TODO: Merge it with ActivityOptions.
     * @hide
     */
    public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED =
            "android.pendingIntent.backgroundActivityAllowed";

    /**
     * Corresponds to {@link #setBackgroundActivityStartsAllowed}.
     */
@@ -130,6 +141,8 @@ public class BroadcastOptions {
        mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false);
        mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS,
                false);
        mPendingIntentBalAllowed = opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
                ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);
    }

    /**
@@ -300,6 +313,26 @@ public class BroadcastOptions {
        return mAllowBackgroundActivityStarts;
    }

    /**
     * Set PendingIntent activity is allowed to be started in the background if the caller
     * can start background activities.
     * TODO: Merge it with ActivityOptions.
     * @hide
     */
    public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
        mPendingIntentBalAllowed = allowed;
    }

    /**
     * Get PendingIntent activity is allowed to be started in the background if the caller
     * can start background activities.
     * TODO: Merge it with ActivityOptions.
     * @hide
     */
    public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
        return mPendingIntentBalAllowed;
    }

    /**
     * Returns the created options as a Bundle, which can be passed to
     * {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -328,6 +361,8 @@ public class BroadcastOptions {
        if (mAllowBackgroundActivityStarts) {
            b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true);
        }
        // TODO: Add API for BroadcastOptions and have a shared base class with ActivityOptions.
        b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
        return b.isEmpty() ? null : b;
    }
}
+54 −8
Original line number Diff line number Diff line
@@ -88,6 +88,9 @@ import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
@@ -101,6 +104,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
@@ -146,6 +150,13 @@ class ActivityStarter {
    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
    private static final int INVALID_LAUNCH_MODE = -1;

    /**
     * Feature flag to protect PendingIntent being abused to start background activity.
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    static final long ENABLE_PENDING_INTENT_BAL_OPTION = 192341120L;

    private final ActivityTaskManagerService mService;
    private final RootWindowContainer mRootWindowContainer;
    private final ActivityTaskSupervisor mSupervisor;
@@ -987,6 +998,10 @@ class ActivityStarter {
        abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
                callingPackage);

        // Merge the two options bundles, while realCallerOptions takes precedence.
        ActivityOptions checkedOptions = options != null
                ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;

        boolean restrictedBgActivity = false;
        if (!abort) {
            try {
@@ -995,15 +1010,12 @@ class ActivityStarter {
                restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                        callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                        request.originatingPendingIntent, request.allowBackgroundActivityStart,
                        intent);
                        intent, checkedOptions);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            }
        }

        // Merge the two options bundles, while realCallerOptions takes precedence.
        ActivityOptions checkedOptions = options != null
                ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
        if (request.allowPendingRemoteAnimationRegistryLookup) {
            checkedOptions = mService.getActivityStartController()
                    .getPendingRemoteAnimationRegistry()
@@ -1242,10 +1254,22 @@ class ActivityStarter {
        return activity != null && packageName.equals(activity.getPackageName());
    }

    private static boolean isPendingIntentBalAllowedByCaller(ActivityOptions activityOptions) {
        if (activityOptions == null) {
            return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
        }
        final Bundle options = activityOptions.toBundle();
        if (options == null) {
            return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;
        }
        return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
                ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);
    }

    boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
            final String callingPackage, int realCallingUid, int realCallingPid,
            WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
            boolean allowBackgroundActivityStart, Intent intent) {
            boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) {
        // don't abort for the most important UIDs
        final int callingAppId = UserHandle.getAppId(callingUid);
        if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
@@ -1314,7 +1338,29 @@ class ActivityStarter {
                ? isCallingUidPersistentSystemProcess
                : (realCallingAppId == Process.SYSTEM_UID)
                        || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
        if (realCallingUid != callingUid) {

        // If caller a legacy app, we won't check if caller has BAL permission.
        final boolean isPiBalOptionEnabled = CompatChanges.isChangeEnabled(
                ENABLE_PENDING_INTENT_BAL_OPTION, callingUid);

        // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
        final boolean balAllowedByPiSender =
                isPendingIntentBalAllowedByCaller(checkedOptions);

        if (balAllowedByPiSender && realCallingUid != callingUid) {
            if (isPiBalOptionEnabled) {
                if (ActivityManager.checkComponentPermission(
                        android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
                        realCallingUid, -1, true)
                        == PackageManager.PERMISSION_GRANTED) {
                    if (DEBUG_ACTIVITY_STARTS) {
                        Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
                                + ") has BAL permission.");
                    }
                    return false;
                }
            }

            // don't abort if the realCallingUid has a visible window
            // TODO(b/171459802): We should check appSwitchAllowed also
            if (realCallingUidHasAnyVisibleWindow) {
@@ -1389,9 +1435,9 @@ class ActivityStarter {
        // If we don't have callerApp at this point, no caller was provided to startActivity().
        // That's the case for PendingIntent-based starts, since the creator's process might not be
        // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
        // caller, so that we can make the decision based on its state.
        // caller if caller allows, so that we can make the decision based on its state.
        int callerAppUid = callingUid;
        if (callerApp == null) {
        if (callerApp == null && balAllowedByPiSender) {
            callerApp = mService.getProcessController(realCallingPid, realCallingUid);
            callerAppUid = realCallingUid;
        }
+1 −1
Original line number Diff line number Diff line
@@ -2077,7 +2077,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        final ActivityStarter starter = getActivityStartController().obtainStarter(
                null /* intent */, "moveTaskToFront");
        if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
                -1, callerApp, null, false, null)) {
                -1, callerApp, null, false, null, null)) {
            if (!isBackgroundActivityStartsEnabled()) {
                return;
            }
Loading