From c003dda6a3dca56dadc1a537ef4ea83ff25f6a58 Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Fri, 16 Dec 2022 17:29:10 +0000 Subject: [PATCH] Clear out top activities not matching launched uid in task This change is a continuation of ag/20482146. If we have task1 with activities A1,B1, a task2 in the foreground could launch A2 with the NEWTASK flag, and affinity for A1 - causing an activity sandwich we wish to prevent. Hence, we clear out all activities not matching A's UID when A2 is launched with NEW_TASK. Test: atest ActivitySecurityModelTest Test: manually tested on device Bug: 248045984 Change-Id: Ia90b197aa7d41f91a7f8cc51b572cd344619c6ad --- .../android/server/wm/ActivityStarter.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d88f719907e32..7776e5c27a807 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2080,6 +2080,7 @@ class ActivityStarter { reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants); if (mAddingToTask) { + clearTopIfNeeded(targetTask, mCallingUid, mStartActivity.getUid(), mLaunchFlags); return START_SUCCESS; } @@ -2111,6 +2112,55 @@ class ActivityStarter { return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP; } + /** + * If the top activity uid does not match the launched activity, and the launch was not + * requested from the top uid, we want to clear out all non matching activities to prevent the + * top activity being sandwiched. + */ + private void clearTopIfNeeded(@NonNull Task targetTask, int callingUid, int startingUid, + int launchFlags) { + if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != FLAG_ACTIVITY_NEW_TASK) { + // Launch is from the same task, so must be a top or privileged UID + return; + } + + ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity(); + if (targetTaskTop != null && targetTaskTop.getUid() != startingUid) { + boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags + .shouldBlockActivityStart(callingUid); + int[] finishCount = new int[0]; + if (shouldBlockActivityStart) { + ActivityRecord activity = targetTask.getActivity( + ar -> !ar.finishing && ar.isUid(startingUid)); + + if (activity == null) { + // mStartActivity is not in task, so clear everything + activity = mStartActivity; + } + + finishCount = new int[1]; + if (activity != null) { + targetTask.performClearTop(activity, launchFlags, finishCount); + } + + if (finishCount[0] > 0) { + Slog.w(TAG, "Clearing top n: " + finishCount[0] + " activities from task t: " + + targetTask + " not matching top uid: " + callingUid); + } + } + + if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid) + && (!shouldBlockActivityStart || finishCount[0] > 0)) { + UiThread.getHandler().post(() -> Toast.makeText(mService.mContext, + (shouldBlockActivityStart + ? "Top activities cleared by " + : "Top activities would be cleared by ") + + ActivitySecurityModelFeatureFlags.DOC_LINK, + Toast.LENGTH_SHORT).show()); + } + } + } + /** * Check if the activity being launched is the same as the one currently at the top and it * should only be launched once.