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

Commit b147fa12 authored by Ricky Wai's avatar Ricky Wai
Browse files

Restrict background activity start by task

We now also allow apps to start activity in its own background task, while
that task won't move to front.

Background activity is not allowed to be started in new task or app
is not in that task stack.
Also, task moving / reparent task is not allowed.

Bug: 128772406
Test: atest android.server.wm.BackgroundActivityLaunchTest
Change-Id: I20f501e755f30f9ef581f7f8c39a2875f959e5b8
parent ec4c269f
Loading
Loading
Loading
Loading
+83 −19
Original line number Original line Diff line number Diff line
@@ -160,6 +160,10 @@ class ActivityStarter {
    private int mCallingUid;
    private int mCallingUid;
    private ActivityOptions mOptions;
    private ActivityOptions mOptions;


    // If it is true, background activity can only be started in an existing task that contains
    // an activity with same uid.
    private boolean mRestrictedBgActivity;

    private int mLaunchMode;
    private int mLaunchMode;
    private boolean mLaunchTaskBehind;
    private boolean mLaunchTaskBehind;
    private int mLaunchFlags;
    private int mLaunchFlags;
@@ -455,6 +459,7 @@ class ActivityStarter {
        mIntent = starter.mIntent;
        mIntent = starter.mIntent;
        mCallingUid = starter.mCallingUid;
        mCallingUid = starter.mCallingUid;
        mOptions = starter.mOptions;
        mOptions = starter.mOptions;
        mRestrictedBgActivity = starter.mRestrictedBgActivity;


        mLaunchTaskBehind = starter.mLaunchTaskBehind;
        mLaunchTaskBehind = starter.mLaunchTaskBehind;
        mLaunchFlags = starter.mLaunchFlags;
        mLaunchFlags = starter.mLaunchFlags;
@@ -551,7 +556,8 @@ class ActivityStarter {
            mLastStartActivityTimeMs = System.currentTimeMillis();
            mLastStartActivityTimeMs = System.currentTimeMillis();
            mLastStartActivityRecord[0] = r;
            mLastStartActivityRecord[0] = r;
            mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
            mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, mLastStartActivityRecord);
                    startFlags, doResume, options, inTask, mLastStartActivityRecord,
                    false /* restrictedBgActivity */);
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
                    mLastStartActivityRecord[0]);
                    mLastStartActivityRecord[0]);
            return mLastStartActivityResult;
            return mLastStartActivityResult;
@@ -760,22 +766,17 @@ class ActivityStarter {
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);
                callingPid, resolvedType, aInfo.applicationInfo);


        boolean abortBackgroundStart = false;
        boolean restrictedBgActivity = false;
        if (!abort) {
        if (!abort) {
            try {
            try {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "shouldAbortBackgroundActivityStart");
                        "shouldAbortBackgroundActivityStart");
                abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid,
                restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                        callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                        callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                        originatingPendingIntent, allowBackgroundActivityStart, intent);
                        originatingPendingIntent, allowBackgroundActivityStart, intent);
            } finally {
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            }
            abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
            // TODO: remove this toast after feature development is done
            if (abortBackgroundStart) {
                showBackgroundActivityBlockedToast(abort, callingPackage);
            }
        }
        }


        // Merge the two options bundles, while realCallerOptions takes precedence.
        // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -918,8 +919,10 @@ class ActivityStarter {
                || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
                || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                    realCallingPid, realCallingUid, "Activity start")) {
                    realCallingPid, realCallingUid, "Activity start")) {
                if (!restrictedBgActivity) {
                    mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
                    mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
                            sourceRecord, startFlags, stack, callerApp));
                            sourceRecord, startFlags, stack, callerApp));
                }
                ActivityOptions.abort(checkedOptions);
                ActivityOptions.abort(checkedOptions);
                return ActivityManager.START_SWITCHES_CANCELED;
                return ActivityManager.START_SWITCHES_CANCELED;
            }
            }
@@ -929,7 +932,7 @@ class ActivityStarter {
        mController.doPendingActivityLaunches(false);
        mController.doPendingActivityLaunches(false);


        final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
        final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity);
                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
        mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
        mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
        return res;
        return res;
    }
    }
@@ -1395,13 +1398,13 @@ class ActivityStarter {
    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {
                ActivityRecord[] outActivity, boolean restrictedBgActivity) {
        int result = START_CANCELED;
        int result = START_CANCELED;
        final ActivityStack startedActivityStack;
        final ActivityStack startedActivityStack;
        try {
        try {
            mService.mWindowManager.deferSurfaceLayout();
            mService.mWindowManager.deferSurfaceLayout();
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity);
                    startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
        } finally {
        } finally {
            final ActivityStack currentStack = r.getActivityStack();
            final ActivityStack currentStack = r.getActivityStack();
            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
@@ -1437,14 +1440,40 @@ class ActivityStarter {
        return result;
        return result;
    }
    }


    /**
     * Return true if background activity is really aborted.
     *
     * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
     */
    private boolean handleBackgroundActivityAbort(ActivityRecord r) {
        // TODO(b/131747138): Remove toast and refactor related code in Q release.
        boolean abort = !mService.isBackgroundActivityStartsEnabled();
        showBackgroundActivityBlockedToast(abort, r.launchedFromPackage);
        if (!abort) {
            return false;
        }
        ActivityRecord resultRecord = r.resultTo;
        String resultWho = r.resultWho;
        int requestCode = r.requestCode;
        if (resultRecord != null) {
            ActivityStack resultStack = resultRecord.getActivityStack();
            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
                    RESULT_CANCELED, null);
        }
        // We pretend to the caller that it was really started to make it backward compatible, but
        // they will just get a cancel result.
        ActivityOptions.abort(r.pendingOptions);
        return true;
    }

    // Note: This method should only be called from {@link startActivity}.
    // Note: This method should only be called from {@link startActivity}.
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
            ActivityRecord[] outActivity, boolean restrictedBgActivity) {

        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);
                voiceInteractor, restrictedBgActivity);

        final int preferredWindowingMode = mLaunchParams.mWindowingMode;
        final int preferredWindowingMode = mLaunchParams.mWindowingMode;


        computeLaunchingTaskFlags();
        computeLaunchingTaskFlags();
@@ -1652,7 +1681,7 @@ class ActivityStarter {
        } else {
        } else {
            // This not being started from an existing activity, and not part of a new task...
            // This not being started from an existing activity, and not part of a new task...
            // just put it in the top task, though these days this case should never happen.
            // just put it in the top task, though these days this case should never happen.
            setTaskToCurrentTopOrCreateNewTask();
            result = setTaskToCurrentTopOrCreateNewTask();
        }
        }
        if (result != START_SUCCESS) {
        if (result != START_SUCCESS) {
            return result;
            return result;
@@ -1725,6 +1754,7 @@ class ActivityStarter {
        mIntent = null;
        mIntent = null;
        mCallingUid = -1;
        mCallingUid = -1;
        mOptions = null;
        mOptions = null;
        mRestrictedBgActivity = false;


        mLaunchTaskBehind = false;
        mLaunchTaskBehind = false;
        mLaunchFlags = 0;
        mLaunchFlags = 0;
@@ -1764,7 +1794,8 @@ class ActivityStarter {


    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            boolean restrictedBgActivity) {
        reset(false /* clearRequest */);
        reset(false /* clearRequest */);


        mStartActivity = r;
        mStartActivity = r;
@@ -1774,6 +1805,7 @@ class ActivityStarter {
        mSourceRecord = sourceRecord;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;
        mVoiceInteractor = voiceInteractor;
        mRestrictedBgActivity = restrictedBgActivity;


        mLaunchParams.reset();
        mLaunchParams.reset();


@@ -1874,6 +1906,11 @@ class ActivityStarter {
        }
        }


        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;

        if (restrictedBgActivity) {
            mAvoidMoveToFront = true;
            mDoResume = false;
        }
    }
    }


    private void sendNewTaskResultRequestIfNeeded() {
    private void sendNewTaskResultRequestIfNeeded() {
@@ -2271,6 +2308,9 @@ class ActivityStarter {
        // isLockTaskModeViolation fails below.
        // isLockTaskModeViolation fails below.


        if (mReuseTask == null) {
        if (mReuseTask == null) {
            if (mRestrictedBgActivity && handleBackgroundActivityAbort(mStartActivity)) {
                return START_ABORTED;
            }
            final TaskRecord task = mTargetStack.createTaskRecord(
            final TaskRecord task = mTargetStack.createTaskRecord(
                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
@@ -2283,6 +2323,11 @@ class ActivityStarter {
            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                    + " in new task " + mStartActivity.getTaskRecord());
                    + " in new task " + mStartActivity.getTaskRecord());
        } else {
        } else {
            if (mRestrictedBgActivity && !mReuseTask.containsAppUid(mCallingUid)) {
                if (handleBackgroundActivityAbort(mStartActivity)) {
                    return START_ABORTED;
                }
            }
            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
        }
        }


@@ -2322,6 +2367,12 @@ class ActivityStarter {


        final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
        final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
        final ActivityStack sourceStack = mSourceRecord.getActivityStack();
        final ActivityStack sourceStack = mSourceRecord.getActivityStack();
        if (mRestrictedBgActivity && !sourceTask.containsAppUid(mCallingUid)) {
            if (handleBackgroundActivityAbort(mStartActivity)) {
                return START_ABORTED;
            }
            return START_ABORTED;
        }
        // We only want to allow changing stack in two cases:
        // We only want to allow changing stack in two cases:
        // 1. If the target task is not the top one. Otherwise we would move the launching task to
        // 1. If the target task is not the top one. Otherwise we would move the launching task to
        //    the other side, rather than show two side by side.
        //    the other side, rather than show two side by side.
@@ -2483,20 +2534,33 @@ class ActivityStarter {
        }
        }
    }
    }


    private void setTaskToCurrentTopOrCreateNewTask() {
    private int setTaskToCurrentTopOrCreateNewTask() {
        mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
        mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
        if (mDoResume) {
        if (mDoResume) {
            mTargetStack.moveToFront("addingToTopTask");
            mTargetStack.moveToFront("addingToTopTask");
        }
        }
        final ActivityRecord prev = mTargetStack.getTopActivity();
        final ActivityRecord prev = mTargetStack.getTopActivity();
        if (mRestrictedBgActivity && prev == null) {
            if (handleBackgroundActivityAbort(mStartActivity)) {
                return START_ABORTED;
            }
            return START_ABORTED;
        }
        final TaskRecord task = (prev != null)
        final TaskRecord task = (prev != null)
                ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
                ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
                mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
                mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
        if (mRestrictedBgActivity && !task.containsAppUid(mCallingUid)) {
            if (handleBackgroundActivityAbort(mStartActivity)) {
                return START_ABORTED;
            }
            return START_ABORTED;
        }
        addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
        addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
        mTargetStack.positionChildWindowContainerAtTop(task);
        mTargetStack.positionChildWindowContainerAtTop(task);
        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                + " in new guessed " + mStartActivity.getTaskRecord());
                + " in new guessed " + mStartActivity.getTaskRecord());
        return START_SUCCESS;
    }
    }


    private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
    private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
+13 −0
Original line number Original line Diff line number Diff line
@@ -1161,6 +1161,19 @@ class TaskRecord extends ConfigurationContainer {
        return false;
        return false;
    }
    }


    /**
     * Return true if any activities in this task belongs to input uid.
     */
    boolean containsAppUid(int uid) {
        for (int i = mActivities.size() - 1; i >= 0; --i) {
            final ActivityRecord r = mActivities.get(i);
            if (r.getUid() == uid) {
                return true;
            }
        }
        return false;
    }

    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
        if (mStack != null) {
        if (mStack != null) {
            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {