Collect NeededUriGrants without holding locks.

In preparation for a future security fix, we need to determine Uri
permission grants before acquiring the WM lock.  This CL is mostly
mechanical refactoring to split the calculation and granting logic
into two phases, and pass around the calculated NeededUriGrants.

There is no other logic changes included in this CL, so it should
have no effects.

Bug: 115619667
Test: atest WmTests:ActivityStarterTests
Test: atest FrameworksServicesTests:com.android.server.uri
Test: atest CtsAppSecurityHostTestCases:android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert
Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests
Test: atest CtsWindowManagerDeviceTestCases:ActivityStarterTests
Change-Id: I07f331a53e27ae5c9d4bade50d70544d02bcd7b1
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c3760a0..ff523fa 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2436,7 +2436,8 @@
      * Sets the result for activity that started this one, clears the references to activities
      * started for result from this one, and clears new intents.
      */
-    private void finishActivityResults(int resultCode, Intent resultData) {
+    private void finishActivityResults(int resultCode, Intent resultData,
+            NeededUriGrants resultGrants) {
         // Send the result if needed
         if (resultTo != null) {
             if (DEBUG_RESULTS) {
@@ -2450,10 +2451,7 @@
                 }
             }
             if (info.applicationInfo.uid > 0) {
-                final NeededUriGrants needed = mAtmService.mUgmInternal
-                        .checkGrantUriPermissionFromIntent(resultData, info.applicationInfo.uid,
-                                resultTo.packageName, resultTo.mUserId);
-                mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(needed,
+                mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
                         resultTo.getUriPermissionsLocked());
             }
             resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
@@ -2490,7 +2488,8 @@
      * See {@link #finishIfPossible(int, Intent, String, boolean)}
      */
     @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
-        return finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason, oomAdj);
+        return finishIfPossible(Activity.RESULT_CANCELED,
+                null /* resultData */, null /* resultGrants */, reason, oomAdj);
     }
 
     /**
@@ -2504,8 +2503,8 @@
      * {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the
      * request to finish it was not ignored.
      */
-    @FinishRequest int finishIfPossible(int resultCode, Intent resultData, String reason,
-            boolean oomAdj) {
+    @FinishRequest int finishIfPossible(int resultCode, Intent resultData,
+            NeededUriGrants resultGrants, String reason, boolean oomAdj) {
         if (DEBUG_RESULTS || DEBUG_STATES) {
             Slog.v(TAG_STATES, "Finishing activity r=" + this + ", result=" + resultCode
                     + ", data=" + resultData + ", reason=" + reason);
@@ -2557,7 +2556,7 @@
                         shouldAdjustGlobalFocus);
             }
 
-            finishActivityResults(resultCode, resultData);
+            finishActivityResults(resultCode, resultData, resultGrants);
 
             final boolean endTask = task.getActivityBelow(this) == null
                     && !task.isClearingToReuseTask();
@@ -2915,7 +2914,8 @@
 
     /** Note: call {@link #cleanUp(boolean, boolean)} before this method. */
     void removeFromHistory(String reason) {
-        finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
+        finishActivityResults(Activity.RESULT_CANCELED,
+                null /* resultData */, null /* resultGrants */);
         makeFinishingLocked();
         if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
             Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
@@ -3664,11 +3664,9 @@
     }
 
     void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
-            Intent data) {
+            Intent data, NeededUriGrants dataGrants) {
         if (callingUid > 0) {
-            final NeededUriGrants needed = mAtmService.mUgmInternal
-                    .checkGrantUriPermissionFromIntent(data, callingUid, packageName, mUserId);
-            mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(needed,
+            mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants,
                     getUriPermissionsLocked());
         }
 
@@ -3708,11 +3706,10 @@
      * Deliver a new Intent to an existing activity, so that its onNewIntent()
      * method will be called at the proper time.
      */
-    final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
+    final void deliverNewIntentLocked(int callingUid, Intent intent, NeededUriGrants intentGrants,
+            String referrer) {
         // The activity now gets access to the data associated with this Intent.
-        final NeededUriGrants needed = mAtmService.mUgmInternal.checkGrantUriPermissionFromIntent(
-                intent, callingUid, packageName, mUserId);
-        mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(needed,
+        mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
                 getUriPermissionsLocked());
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 9b9b613..5c91b36 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -156,6 +156,7 @@
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.AppTimeTracker;
+import com.android.server.uri.NeededUriGrants;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -2365,8 +2366,8 @@
         return false;
     }
 
-    boolean navigateUpTo(ActivityRecord srec, Intent destIntent, int resultCode,
-            Intent resultData) {
+    boolean navigateUpTo(ActivityRecord srec, Intent destIntent, NeededUriGrants destGrants,
+            int resultCode, Intent resultData, NeededUriGrants resultGrants) {
         if (!srec.attachedToProcess()) {
             // Nothing to do if the caller is not attached, because this method should be called
             // from an alive activity.
@@ -2417,12 +2418,14 @@
         resultCodeHolder[0] = resultCode;
         final Intent[] resultDataHolder = new Intent[1];
         resultDataHolder[0] = resultData;
+        final NeededUriGrants[] resultGrantsHolder = new NeededUriGrants[1];
+        resultGrantsHolder[0] = resultGrants;
         final ActivityRecord finalParent = parent;
         task.forAllActivities((ar) -> {
             if (ar == finalParent) return true;
 
-            ar.finishIfPossible(
-                    resultCodeHolder[0], resultDataHolder[0], "navigate-up", true /* oomAdj */);
+            ar.finishIfPossible(resultCodeHolder[0], resultDataHolder[0], resultGrantsHolder[0],
+                    "navigate-up", true /* oomAdj */);
             // Only return the supplied result for the first activity finished
             resultCodeHolder[0] = Activity.RESULT_CANCELED;
             resultDataHolder[0] = null;
@@ -2439,7 +2442,7 @@
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
                     (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                parent.deliverNewIntentLocked(callingUid, destIntent, srec.packageName);
+                parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
             } else {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
@@ -2463,7 +2466,8 @@
                 } catch (RemoteException e) {
                     foundParentInTask = false;
                 }
-                parent.finishIfPossible(resultCode, resultData, "navigate-top", true /* oomAdj */);
+                parent.finishIfPossible(resultCode, resultData, resultGrants,
+                        "navigate-top", true /* oomAdj */);
             }
         }
         Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 62979ff..48b4811 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -139,6 +139,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.UserState;
+import com.android.server.uri.NeededUriGrants;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 
 import java.io.FileDescriptor;
@@ -379,14 +380,17 @@
         final int startFlags;
         final ActivityStack stack;
         final WindowProcessController callerApp;
+        final NeededUriGrants intentGrants;
 
-        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack, WindowProcessController app) {
-            r = _r;
-            sourceRecord = _sourceRecord;
-            startFlags = _startFlags;
-            stack = _stack;
-            callerApp = app;
+        PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
+                int startFlags, ActivityStack stack, WindowProcessController callerApp,
+                NeededUriGrants intentGrants) {
+            this.r = r;
+            this.sourceRecord = sourceRecord;
+            this.startFlags = startFlags;
+            this.stack = stack;
+            this.callerApp = callerApp;
+            this.intentGrants = intentGrants;
         }
 
         void sendErrorResult(String message) {
@@ -1003,7 +1007,7 @@
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
                 resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null /* data */);
+                        Activity.RESULT_CANCELED, null /* data */, null /* dataGrants */);
             }
             final String msg;
             if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index c28d47c..23974d7 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -512,7 +512,7 @@
                     "pendingActivityLaunch");
             try {
                 starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        resume, pal.r.pendingOptions, null);
+                        resume, pal.r.pendingOptions, null, pal.intentGrants);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5b9b126..daa97b5 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -311,6 +311,7 @@
 
         IApplicationThread caller;
         Intent intent;
+        NeededUriGrants intentGrants;
         // A copy of the original requested intent, in case for ephemeral app launch.
         Intent ephemeralIntent;
         String resolvedType;
@@ -362,6 +363,7 @@
         void reset() {
             caller = null;
             intent = null;
+            intentGrants = null;
             ephemeralIntent = null;
             resolvedType = null;
             activityInfo = null;
@@ -401,6 +403,7 @@
         void set(Request request) {
             caller = request.caller;
             intent = request.intent;
+            intentGrants = request.intentGrants;
             ephemeralIntent = request.ephemeralIntent;
             resolvedType = request.resolvedType;
             activityInfo = request.activityInfo;
@@ -455,6 +458,20 @@
                 callingPid = callingUid = -1;
             }
 
+            // To determine the set of needed Uri permission grants, we need the
+            // "resolved" calling UID, where we try our best to identify the
+            // actual caller that is starting this activity
+            int resolvedCallingUid = callingUid;
+            if (caller != null) {
+                synchronized (supervisor.mService.mGlobalLock) {
+                    final WindowProcessController callerApp = supervisor.mService
+                            .getProcessController(caller);
+                    if (callerApp != null) {
+                        resolvedCallingUid = callerApp.mInfo.uid;
+                    }
+                }
+            }
+
             // Save a copy in case ephemeral needs it
             ephemeralIntent = new Intent(intent);
             // Don't modify the client's object!
@@ -504,6 +521,13 @@
             // Collect information about the target of the Intent.
             activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
                     profilerInfo);
+
+            // Carefully collect grants without holding lock
+            if (activityInfo != null) {
+                intentGrants = supervisor.mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+                        intent, resolvedCallingUid, activityInfo.applicationInfo.packageName,
+                        UserHandle.getUserId(activityInfo.applicationInfo.uid));
+            }
         }
     }
 
@@ -578,7 +602,8 @@
      */
     void startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, Task inTask) {
+            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
+            NeededUriGrants intentGrants) {
         try {
             final LaunchingState launchingState = mSupervisor.getActivityMetricsLogger()
                     .notifyActivityLaunching(r.intent, r.resultTo);
@@ -587,7 +612,7 @@
             mLastStartActivityRecord = r;
             mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                     voiceInteractor, startFlags, doResume, options, inTask,
-                    false /* restrictedBgActivity */);
+                    false /* restrictedBgActivity */, intentGrants);
             mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
                     mLastStartActivityResult, mLastStartActivityRecord);
         } finally {
@@ -814,6 +839,7 @@
 
         final IApplicationThread caller = request.caller;
         Intent intent = request.intent;
+        NeededUriGrants intentGrants = request.intentGrants;
         String resolvedType = request.resolvedType;
         ActivityInfo aInfo = request.activityInfo;
         ResolveInfo rInfo = request.resolveInfo;
@@ -961,7 +987,7 @@
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
                 resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
-                        null /* data */);
+                        null /* data */, null /* dataGrants */);
             }
             SafeActivityOptions.abort(options);
             return err;
@@ -1023,12 +1049,16 @@
             callingPid = mInterceptor.mCallingPid;
             callingUid = mInterceptor.mCallingUid;
             checkedOptions = mInterceptor.mActivityOptions;
+
+            // The interception target shouldn't get any permission grants
+            // intended for the original destination
+            intentGrants = null;
         }
 
         if (abort) {
             if (resultRecord != null) {
                 resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
-                        null /* data */);
+                        null /* data */, null /* dataGrants */);
             }
             // We pretend to the caller that it was really started, but they will just get a
             // cancel result.
@@ -1074,6 +1104,10 @@
                 }
                 intent = newIntent;
 
+                // The permissions review target shouldn't get any permission
+                // grants intended for the original destination
+                intentGrants = null;
+
                 resolvedType = null;
                 callingUid = realCallingUid;
                 callingPid = realCallingPid;
@@ -1106,6 +1140,10 @@
             callingUid = realCallingUid;
             callingPid = realCallingPid;
 
+            // The ephemeral installer shouldn't get any permission grants
+            // intended for the original destination
+            intentGrants = null;
+
             aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
         }
 
@@ -1132,7 +1170,7 @@
                     realCallingPid, realCallingUid, "Activity start")) {
                 if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {
                     mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
-                            sourceRecord, startFlags, stack, callerApp));
+                            sourceRecord, startFlags, stack, callerApp, intentGrants));
                 }
                 ActivityOptions.abort(checkedOptions);
                 return ActivityManager.START_SWITCHES_CANCELED;
@@ -1144,7 +1182,7 @@
 
         mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                 request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
-                restrictedBgActivity);
+                restrictedBgActivity, intentGrants);
 
         if (request.outActivity != null) {
             request.outActivity[0] = mLastStartActivityRecord;
@@ -1169,7 +1207,7 @@
         int requestCode = r.requestCode;
         if (resultRecord != null) {
             resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
-                    null /* data */);
+                    null /* data */, null /* dataGrants */);
         }
         // We pretend to the caller that it was really started to make it backward compatible, but
         // they will just get a cancel result.
@@ -1471,14 +1509,14 @@
     private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                 IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                 int startFlags, boolean doResume, ActivityOptions options, Task inTask,
-                boolean restrictedBgActivity) {
+                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
         int result = START_CANCELED;
         final ActivityStack startedActivityStack;
         try {
             mService.deferWindowLayout();
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
             result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, restrictedBgActivity);
+                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             startedActivityStack = handleStartResult(r, result);
@@ -1546,7 +1584,7 @@
     int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, Task inTask,
-            boolean restrictedBgActivity) {
+            boolean restrictedBgActivity, NeededUriGrants intentGrants) {
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                 voiceInteractor, restrictedBgActivity);
 
@@ -1583,7 +1621,7 @@
                 ? null : targetTask.getTopNonFinishingActivity();
         if (targetTaskTop != null) {
             // Recycle the target task for this launch.
-            startResult = recycleTask(targetTask, targetTaskTop, reusedTask);
+            startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
             if (startResult != START_SUCCESS) {
                 return startResult;
             }
@@ -1595,7 +1633,7 @@
         // we need to check if it should only be launched once.
         final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
         if (topStack != null) {
-            startResult = deliverToCurrentTopIfNeeded(topStack);
+            startResult = deliverToCurrentTopIfNeeded(topStack, intentGrants);
             if (startResult != START_SUCCESS) {
                 return startResult;
             }
@@ -1626,9 +1664,7 @@
             }
         }
 
-        final NeededUriGrants needed = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
-                mIntent, mCallingUid, mStartActivity.packageName, mStartActivity.mUserId);
-        mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(needed,
+        mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
                 mStartActivity.getUriPermissionsLocked());
         if (mStartActivity.resultTo != null && mStartActivity.resultTo.info != null) {
             // we need to resolve resultTo to a uid as grantImplicitAccess deals explicitly in UIDs
@@ -1755,7 +1791,8 @@
         if (mStartActivity.packageName == null) {
             if (mStartActivity.resultTo != null) {
                 mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
-                        mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
+                        mStartActivity.requestCode, RESULT_CANCELED,
+                        null /* data */, null /* dataGrants */);
             }
             ActivityOptions.abort(mOptions);
             return START_CLASS_NOT_FOUND;
@@ -1800,7 +1837,8 @@
      * - Determine whether need to add a new activity on top or just brought the task to front.
      */
     @VisibleForTesting
-    int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask) {
+    int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask,
+            NeededUriGrants intentGrants) {
         // Should not recycle task which is from a different user, just adding the starting
         // activity to the task.
         if (targetTask.mUserId != mStartActivity.mUserId) {
@@ -1860,7 +1898,7 @@
         }
 
         complyActivityFlags(targetTask,
-                reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null);
+                reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
 
         if (clearTaskForReuse) {
             // Clear task for re-use so later code to methods
@@ -1896,7 +1934,7 @@
      * Check if the activity being launched is the same as the one currently at the top and it
      * should only be launched once.
      */
-    private int deliverToCurrentTopIfNeeded(ActivityStack topStack) {
+    private int deliverToCurrentTopIfNeeded(ActivityStack topStack, NeededUriGrants intentGrants) {
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
                 && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
@@ -1924,7 +1962,7 @@
             return START_RETURN_INTENT_TO_CALLER;
         }
 
-        deliverNewIntent(top);
+        deliverNewIntent(top, intentGrants);
 
         // Don't use mStartActivity.task to show the toast. We're not starting a new activity but
         // reusing 'top'. Fields in mStartActivity may not be fully initialized.
@@ -1938,7 +1976,8 @@
      * Applying the launching flags to the task, which might clear few or all the activities in the
      * task.
      */
-    private void complyActivityFlags(Task targetTask, ActivityRecord reusedActivity) {
+    private void complyActivityFlags(Task targetTask, ActivityRecord reusedActivity,
+            NeededUriGrants intentGrants) {
         ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
         final boolean resetTask =
                 reusedActivity != null && (mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0;
@@ -1982,7 +2021,7 @@
                     // so make sure the task now has the identity of the new intent.
                     top.getTask().setIntent(mStartActivity);
                 }
-                deliverNewIntent(top);
+                deliverNewIntent(top, intentGrants);
             } else {
                 // A special case: we need to start the activity because it is not currently
                 // running, and the caller has asked to clear the current task to have this
@@ -2008,7 +2047,7 @@
                 final Task task = act.getTask();
                 task.moveActivityToFrontLocked(act);
                 act.updateOptionsLocked(mOptions);
-                deliverNewIntent(act);
+                deliverNewIntent(act, intentGrants);
                 mTargetStack.mLastPausedActivity = null;
             } else {
                 mAddingToTask = true;
@@ -2028,7 +2067,7 @@
                 if (targetTaskTop.isRootOfTask()) {
                     targetTaskTop.getTask().setIntent(mStartActivity);
                 }
-                deliverNewIntent(targetTaskTop);
+                deliverNewIntent(targetTaskTop, intentGrants);
             } else if (!targetTask.isSameIntentFilter(mStartActivity)) {
                 // In this case we are launching the root activity of the task, but with a
                 // different intent. We should start a new instance on top.
@@ -2236,7 +2275,8 @@
             // as normal without a dependency on its originator.
             Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
             mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
-                    mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
+                    mStartActivity.requestCode, RESULT_CANCELED,
+                    null /* data */, null /* dataGrants */);
             mStartActivity.resultTo = null;
         }
     }
@@ -2515,13 +2555,13 @@
         }
     }
 
-    private void deliverNewIntent(ActivityRecord activity) {
+    private void deliverNewIntent(ActivityRecord activity, NeededUriGrants intentGrants) {
         if (mIntentDelivered) {
             return;
         }
 
         activity.logStartActivity(EventLogTags.WM_NEW_INTENT, activity.getTask());
-        activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+        activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, intentGrants,
                 mStartActivity.launchedFromPackage);
         mIntentDelivered = true;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index fdbb2b2..5f591b5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -270,6 +270,7 @@
 import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.vr.VrManagerInternal;
 
@@ -1672,11 +1673,18 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        final ActivityRecord r;
         synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            r = ActivityRecord.isInStackLocked(token);
             if (r == null) {
                 return true;
             }
+        }
+
+        // Carefully collect grants without holding lock
+        final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
+
+        synchronized (mGlobalLock) {
             // Keep track of the root activity of the task before we finish it
             final Task tr = r.getTask();
             final ActivityRecord rootR = tr.getRootActivity();
@@ -1737,7 +1745,8 @@
                     // Explicitly dismissing the activity so reset its relaunch flag.
                     r.mRelaunchReason = RELAUNCH_REASON_NONE;
                 } else {
-                    r.finishIfPossible(resultCode, resultData, "app-request", true /* oomAdj */);
+                    r.finishIfPossible(resultCode, resultData, resultGrants,
+                            "app-request", true /* oomAdj */);
                     res = r.finishing;
                     if (!res) {
                         Slog.i(TAG, "Failed to finish by app-request");
@@ -2258,14 +2267,21 @@
     @Override
     public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
             Intent resultData) {
+        final ActivityRecord r;
+        synchronized (mGlobalLock) {
+            r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+        }
+
+        // Carefully collect grants without holding lock
+        final NeededUriGrants destGrants = collectGrants(destIntent, r);
+        final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
 
         synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r != null) {
-                return r.getRootTask().navigateUpTo(
-                        r, destIntent, resultCode, resultData);
-            }
-            return false;
+            return r.getRootTask().navigateUpTo(
+                    r, destIntent, destGrants, resultCode, resultData, resultGrants);
         }
     }
 
@@ -2417,6 +2433,15 @@
         return r.resultTo;
     }
 
+    private NeededUriGrants collectGrants(Intent intent, ActivityRecord target) {
+        if (target != null) {
+            return mUgmInternal.checkGrantUriPermissionFromIntent(intent,
+                    Binder.getCallingUid(), target.packageName, target.mUserId);
+        } else {
+            return null;
+        }
+    }
+
     @Override
     public void unhandledBack() {
         mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
@@ -6573,12 +6598,20 @@
         @Override
         public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
                 int requestCode, int resultCode, Intent data) {
+            final ActivityRecord r;
             synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r != null && r.getRootTask() != null) {
-                    r.sendResult(callingUid, resultWho, requestCode, resultCode, data);
+                r = ActivityRecord.isInStackLocked(activityToken);
+                if (r == null || r.getRootTask() == null) {
+                    return;
                 }
             }
+
+            // Carefully collect grants without holding lock
+            final NeededUriGrants dataGrants = collectGrants(data, r);
+
+            synchronized (mGlobalLock) {
+                r.sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f8ad6f2..0691822 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1517,8 +1517,8 @@
             forAllActivities((r) -> {
                 if (r.finishing) return;
                 // TODO: figure-out how to avoid object creation due to capture of reason variable.
-                r.finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                        false /* oomAdj */);
+                r.finishIfPossible(Activity.RESULT_CANCELED,
+                        null /* resultData */, null /* resultGrants */, reason, false /* oomAdj */);
             });
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 063568d..fbf906c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -762,8 +762,8 @@
         assertTrue(mStack.isTopStackInDisplayArea());
 
         mActivity.setState(RESUMED, "test");
-        mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, "test",
-                false /* oomAdj */);
+        mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
+                null /* resultGrants */, "test", false /* oomAdj */);
 
         assertTrue(stack1.isTopStackInDisplayArea());
     }
@@ -788,8 +788,8 @@
 
         // Finish top activity and verify the next focusable rootable task has adjusted to top.
         topActivity.setState(RESUMED, "test");
-        topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, "test",
-                false /* oomAdj */);
+        topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
+                null /* resultGrants */, "test", false /* oomAdj */);
         assertEquals(mTask, mStack.getTopMostTask());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 64e08c6..37882bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1281,11 +1281,13 @@
         secondActivity.app.setThread(null);
         // This should do nothing from a non-attached caller.
         assertFalse(mStack.navigateUpTo(secondActivity /* source record */,
-                firstActivity.intent /* destIntent */, 0 /* resultCode */, null /* resultData */));
+                firstActivity.intent /* destIntent */, null /* destGrants */,
+                0 /* resultCode */, null /* resultData */, null /* resultGrants */));
 
         secondActivity.app.setThread(thread);
         assertTrue(mStack.navigateUpTo(secondActivity /* source record */,
-                firstActivity.intent /* destIntent */, 0 /* resultCode */, null /* resultData */));
+                firstActivity.intent /* destIntent */, null /* destGrants */,
+                0 /* resultCode */, null /* resultData */, null /* resultGrants */));
         // The firstActivity uses default launch mode, so the activities between it and itself will
         // be finished.
         assertTrue(secondActivity.finishing);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 27782f5..ca4456b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -86,12 +86,12 @@
         wpc.setThread(mock(IApplicationThread.class));
 
         mController.addPendingActivityLaunch(
-                new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
+                new PendingActivityLaunch(activity, source, startFlags, stack, wpc, null));
         final boolean resume = random.nextBoolean();
         mController.doPendingActivityLaunches(resume);
 
         verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
-                eq(null), eq(startFlags), eq(resume), eq(null), eq(null));
+                eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
     }
 
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 02d1f9b..4a19684 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -989,7 +989,7 @@
                 .setUserId(10)
                 .build();
 
-        final int result = starter.recycleTask(task, null, null);
+        final int result = starter.recycleTask(task, null, null, null);
         assertThat(result == START_SUCCESS).isTrue();
         assertThat(starter.mAddingToTask).isTrue();
     }
@@ -1068,7 +1068,8 @@
                 /* doResume */true,
                 /* options */null,
                 /* inTask */null,
-                /* restrictedBgActivity */false);
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
 
         // Then
         verify(stack).ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);