Merge "Add ComponentCaller#checkContentUriPermission API for Activity" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 7dedbd8..321d35d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5425,6 +5425,7 @@
 
   @FlaggedApi("android.security.content_uri_permission_apis") public final class ComponentCaller {
     ctor public ComponentCaller(@NonNull android.os.IBinder, @Nullable android.os.IBinder);
+    method public int checkContentUriPermission(@NonNull android.net.Uri, int);
     method @Nullable public String getPackage();
     method public int getUid();
   }
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index b8bd030..a59f04b 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -17,13 +17,16 @@
 package android.app;
 
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.os.UserHandle.getCallingUserId;
 
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
@@ -296,6 +299,18 @@
         }
     }
 
+    /** Checks if the app that launched the activity has access to the URI. */
+    public int checkActivityCallerContentUriPermission(IBinder activityToken, IBinder callerToken,
+            Uri uri, int modeFlags) {
+        try {
+            return getActivityClientController().checkActivityCallerContentUriPermission(
+                    activityToken, callerToken, ContentProvider.getUriWithoutUserId(uri), modeFlags,
+                    ContentProvider.getUserIdFromUri(uri, getCallingUserId()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     public void setRequestedOrientation(IBinder token, int requestedOrientation) {
         try {
             getActivityClientController().setRequestedOrientation(token, requestedOrientation);
diff --git a/core/java/android/app/ComponentCaller.java b/core/java/android/app/ComponentCaller.java
index 583408e..a440dbc 100644
--- a/core/java/android/app/ComponentCaller.java
+++ b/core/java/android/app/ComponentCaller.java
@@ -18,6 +18,9 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.IBinder;
 import android.os.Process;
 
@@ -118,6 +121,40 @@
         return ActivityClient.getInstance().getLaunchedFromPackage(mActivityToken);
     }
 
+    /**
+     * Determines whether this component caller had access to a specific content URI at launch time.
+     * Apps can use this API to validate content URIs coming from other apps.
+     *
+     * <p><b>Note</b>, in {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} only
+     * {@link Activity} has access to {@link ComponentCaller} instances.
+     *
+     * <p>Before using this method, note the following:
+     * <ul>
+     *     <li>You must have access to the supplied URI, otherwise it will throw a
+     *     {@link SecurityException}.
+     *     <li>This is not a real time check, i.e. the permissions have been computed at launch
+     *     time.
+     *     <li>This method will return the correct result for content URIs passed at launch time,
+     *     specifically the ones from {@link Intent#getData()}, and {@link Intent#getClipData()} in
+     *     the intent of {@code startActivity(intent)}. For others, it will throw an
+     *     {@link IllegalArgumentException}.
+     * </ul>
+     *
+     * @param uri The content uri that is being checked
+     * @param modeFlags The access modes to check
+     * @return {@link PackageManager#PERMISSION_GRANTED} if this activity caller is allowed to
+     *         access that uri, or {@link PackageManager#PERMISSION_DENIED} if it is not
+     * @throws IllegalArgumentException if uri is a non-content URI or it wasn't passed at launch
+     * @throws SecurityException if you don't have access to uri
+     *
+     * @see android.content.Context#checkContentUriPermissionFull(Uri, int, int, int)
+     */
+    @PackageManager.PermissionResult
+    public int checkContentUriPermission(@NonNull Uri uri, @Intent.AccessUriMode int modeFlags) {
+        return ActivityClient.getInstance().checkActivityCallerContentUriPermission(mActivityToken,
+                mCallerToken, uri, modeFlags);
+    }
+
     @Override
     public boolean equals(@Nullable Object obj) {
         if (obj == null || !(obj instanceof ComponentCaller other)) {
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 5b044f6..05fee72 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -23,6 +23,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.PersistableBundle;
@@ -91,6 +92,9 @@
     int getLaunchedFromUid(in IBinder token);
     String getLaunchedFromPackage(in IBinder token);
 
+    int checkActivityCallerContentUriPermission(in IBinder activityToken, in IBinder callerToken,
+            in Uri uri, int modeFlags, int userId);
+
     void setRequestedOrientation(in IBinder token, int requestedOrientation);
     int getRequestedOrientation(in IBinder token);
 
diff --git a/services/core/java/com/android/server/wm/ActivityCallerState.java b/services/core/java/com/android/server/wm/ActivityCallerState.java
new file mode 100644
index 0000000..4416605
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityCallerState.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.content.ClipData;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.uri.GrantUri;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.WeakHashMap;
+
+/**
+ * Represents the state of activity callers. Used by {@link ActivityRecord}.
+ * @hide
+ */
+final class ActivityCallerState {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityCallerState" : TAG_ATM;
+
+    // XML tags for CallerInfo
+    private static final String TAG_READABLE_CONTENT_URI = "readable_content_uri";
+    private static final String TAG_WRITABLE_CONTENT_URI = "writable_content_uri";
+    private static final String TAG_INACCESSIBLE_CONTENT_URI = "inaccessible_content_uri";
+    private static final String ATTR_SOURCE_USER_ID = "source_user_id";
+    private static final String ATTR_URI = "uri";
+    private static final String ATTR_PREFIX = "prefix";
+
+    // Map for storing CallerInfo instances
+    private final WeakHashMap<IBinder, CallerInfo> mCallerTokenInfoMap = new WeakHashMap<>();
+
+    final ActivityTaskManagerService mAtmService;
+
+    ActivityCallerState(ActivityTaskManagerService service) {
+        mAtmService = service;
+    }
+
+    CallerInfo getCallerInfoOrNull(IBinder callerToken) {
+        return mCallerTokenInfoMap.getOrDefault(callerToken, null);
+    }
+
+    void add(IBinder callerToken, CallerInfo callerInfo) {
+        mCallerTokenInfoMap.put(callerToken, callerInfo);
+    }
+
+    void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) {
+        final CallerInfo callerInfo = new CallerInfo();
+        mCallerTokenInfoMap.put(callerToken, callerInfo);
+
+        final ArraySet<Uri> contentUris = getContentUrisFromIntent(intent);
+        for (int i = contentUris.size() - 1; i >= 0; i--) {
+            final Uri contentUri = contentUris.valueAt(i);
+
+            final boolean hasRead = addContentUriIfUidHasPermission(contentUri, callerUid,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION, callerInfo.mReadableContentUris);
+
+            final boolean hasWrite = addContentUriIfUidHasPermission(contentUri, callerUid,
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION, callerInfo.mWritableContentUris);
+
+            if (!hasRead && !hasWrite) {
+                callerInfo.mInaccessibleContentUris.add(convertToGrantUri(contentUri,
+                        /* modeFlags */ 0));
+            }
+        }
+    }
+
+    boolean checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags) {
+        if (!Intent.isAccessUriMode(modeFlags)) {
+            throw new IllegalArgumentException("Mode flags are not access URI mode flags: "
+                    + modeFlags);
+        }
+
+        final CallerInfo callerInfo = mCallerTokenInfoMap.getOrDefault(callerToken, null);
+        if (callerInfo == null) {
+            Slog.e(TAG, "Caller not found for checkContentUriPermission of: "
+                    + grantUri.uri.toSafeString());
+            return false;
+        }
+
+        if (callerInfo.mInaccessibleContentUris.contains(grantUri)) {
+            return false;
+        }
+
+        final boolean readMet = callerInfo.mReadableContentUris.contains(grantUri);
+        final boolean writeMet = callerInfo.mWritableContentUris.contains(grantUri);
+
+        if (!readMet && !writeMet) {
+            throw new IllegalArgumentException("The supplied URI wasn't passed at launch: "
+                    + grantUri.uri.toSafeString());
+        }
+
+        final boolean checkRead = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+        if (checkRead && !readMet) {
+            return false;
+        }
+
+        final boolean checkWrite = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+        if (checkWrite && !writeMet) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean addContentUriIfUidHasPermission(Uri contentUri, int uid, int modeFlags,
+            ArraySet<GrantUri> grantUris) {
+        final GrantUri grantUri = convertToGrantUri(contentUri, modeFlags);
+        if (mAtmService.mUgmInternal.checkUriPermission(grantUri, uid,
+                modeFlags, /* isFullAccessForContentUri */ true)) {
+            grantUris.add(grantUri);
+            return true;
+        }
+        return false;
+    }
+
+    private static GrantUri convertToGrantUri(Uri contentUri, int modeFlags) {
+        return new GrantUri(ContentProvider.getUserIdFromUri(contentUri,
+                UserHandle.getCallingUserId()), ContentProvider.getUriWithoutUserId(contentUri),
+                modeFlags);
+    }
+
+    private static ArraySet<Uri> getContentUrisFromIntent(Intent intent) {
+        final ArraySet<Uri> uris = new ArraySet<>();
+        if (intent == null) return uris;
+
+        // getData
+        addUriIfContentUri(intent.getData(), uris);
+
+        final ClipData clipData = intent.getClipData();
+        if (clipData == null) return uris;
+
+        for (int i = 0; i < clipData.getItemCount(); i++) {
+            final ClipData.Item item = clipData.getItemAt(i);
+
+            // getUri
+            addUriIfContentUri(item.getUri(), uris);
+
+            // getIntent
+            uris.addAll(getContentUrisFromIntent(item.getIntent()));
+        }
+        return uris;
+    }
+
+    private static void addUriIfContentUri(Uri uri, ArraySet<Uri> uris) {
+        if (uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            uris.add(uri);
+        }
+    }
+
+    public static final class CallerInfo {
+        final ArraySet<GrantUri> mReadableContentUris = new ArraySet<>();
+        final ArraySet<GrantUri> mWritableContentUris = new ArraySet<>();
+        final ArraySet<GrantUri> mInaccessibleContentUris = new ArraySet<>();
+
+        public void saveToXml(TypedXmlSerializer out)
+                throws IOException, XmlPullParserException {
+            for (int i = mReadableContentUris.size() - 1; i >= 0; i--) {
+                saveGrantUriToXml(out, mReadableContentUris.valueAt(i), TAG_READABLE_CONTENT_URI);
+            }
+
+            for (int i = mWritableContentUris.size() - 1; i >= 0; i--) {
+                saveGrantUriToXml(out, mWritableContentUris.valueAt(i), TAG_WRITABLE_CONTENT_URI);
+            }
+
+            for (int i = mInaccessibleContentUris.size() - 1; i >= 0; i--) {
+                saveGrantUriToXml(out, mInaccessibleContentUris.valueAt(i),
+                        TAG_INACCESSIBLE_CONTENT_URI);
+            }
+        }
+
+        public static CallerInfo restoreFromXml(TypedXmlPullParser in)
+                throws IOException, XmlPullParserException {
+            CallerInfo callerInfo = new CallerInfo();
+            final int outerDepth = in.getDepth();
+            int event;
+            while (((event = in.next()) != END_DOCUMENT)
+                    && (event != END_TAG || in.getDepth() >= outerDepth)) {
+                if (event == START_TAG) {
+                    final String name = in.getName();
+                    if (TAG_READABLE_CONTENT_URI.equals(name)) {
+                        callerInfo.mReadableContentUris.add(restoreGrantUriFromXml(in));
+                    } else if (TAG_WRITABLE_CONTENT_URI.equals(name)) {
+                        callerInfo.mWritableContentUris.add(restoreGrantUriFromXml(in));
+                    } else if (TAG_INACCESSIBLE_CONTENT_URI.equals(name)) {
+                        callerInfo.mInaccessibleContentUris.add(restoreGrantUriFromXml(in));
+                    } else {
+                        Slog.w(TAG, "restoreActivity: unexpected name=" + name);
+                        XmlUtils.skipCurrentTag(in);
+                    }
+                }
+            }
+            return callerInfo;
+        }
+
+        private void saveGrantUriToXml(TypedXmlSerializer out, GrantUri grantUri, String tag)
+                throws IOException, XmlPullParserException {
+            out.startTag(null, tag);
+            out.attributeInt(null, ATTR_SOURCE_USER_ID, grantUri.sourceUserId);
+            out.attribute(null, ATTR_URI, String.valueOf(grantUri.uri));
+            out.attributeBoolean(null, ATTR_PREFIX, grantUri.prefix);
+            out.endTag(null, tag);
+        }
+
+        private static GrantUri restoreGrantUriFromXml(TypedXmlPullParser in)
+                throws IOException, XmlPullParserException {
+            int sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID, 0);
+            Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
+            boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false);
+            return new GrantUri(sourceUserId, uri,
+                    prefix ? Intent.FLAG_GRANT_PREFIX_URI_PERMISSION : 0);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 2e0546e..173e139 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -30,6 +30,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -80,6 +82,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -103,6 +106,7 @@
 import com.android.server.Watchdog;
 import com.android.server.pm.KnownPackages;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.uri.GrantUri;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.vr.VrManagerInternal;
 
@@ -715,6 +719,32 @@
         return null;
     }
 
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    @Override
+    public int checkActivityCallerContentUriPermission(IBinder activityToken, IBinder callerToken,
+            Uri uri, int modeFlags, int userId) {
+        // 1. Check if we have access to the URI - > throw if we don't
+        GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
+        if (!mService.mUgmInternal.checkUriPermission(grantUri, Binder.getCallingUid(), modeFlags,
+                /* isFullAccessForContentUri */ true)) {
+            throw new SecurityException("You don't have access to the content URI, hence can't"
+                    + " check if the caller has access to it: " + uri);
+        }
+
+        // 2. Get the permission result for the caller
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+            if (r != null) {
+                boolean granted = r.checkContentUriPermission(callerToken, grantUri, modeFlags);
+                return granted ? PERMISSION_GRANTED : PERMISSION_DENIED;
+            }
+        }
+        return PERMISSION_DENIED;
+    }
+
     /** Whether the call to one of the getLaunchedFrom APIs is performed by an internal caller. */
     private boolean isInternalCallerGetLaunchedFrom(int uid) {
         if (UserHandle.getAppId(uid) == SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c2117ea..09c329b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -381,6 +381,7 @@
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.uri.GrantUri;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
@@ -436,6 +437,7 @@
     private static final String ATTR_LAUNCHEDFROMFEATURE = "launched_from_feature";
     private static final String ATTR_RESOLVEDTYPE = "resolved_type";
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
+    private static final String TAG_INITIAL_CALLER_INFO = "initial_caller_info";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
     // How many activities have to be scheduled to stop to force a stop pass.
@@ -472,6 +474,7 @@
     private static final float ASPECT_RATIO_ROUNDING_TOLERANCE = 0.005f;
 
     final ActivityTaskManagerService mAtmService;
+    final ActivityCallerState mCallerState;
     @NonNull
     final ActivityInfo info; // activity info provided by developer in AndroidManifest
     // Which user is this running for?
@@ -2021,6 +2024,18 @@
         }
     }
 
+    void computeInitialCallerInfo() {
+        computeCallerInfo(initialCallerInfoAccessToken, intent, launchedFromUid);
+    }
+
+    void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) {
+        mCallerState.computeCallerInfo(callerToken, intent, callerUid);
+    }
+
+    boolean checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags) {
+        return mCallerState.checkContentUriPermission(callerToken, grantUri, modeFlags);
+    }
+
     private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
             int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
             @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
@@ -2246,6 +2261,7 @@
                             }
                             return appContext;
                         });
+        mCallerState = new ActivityCallerState(mAtmService);
     }
 
     /**
@@ -10113,6 +10129,16 @@
             mPersistentState.saveToXml(out);
             out.endTag(null, TAG_PERSISTABLEBUNDLE);
         }
+
+        if (android.security.Flags.contentUriPermissionApis()) {
+            ActivityCallerState.CallerInfo initialCallerInfo = mCallerState.getCallerInfoOrNull(
+                    initialCallerInfoAccessToken);
+            if (initialCallerInfo != null) {
+                out.startTag(null, TAG_INITIAL_CALLER_INFO);
+                initialCallerInfo.saveToXml(out);
+                out.endTag(null, TAG_INITIAL_CALLER_INFO);
+            }
+        }
     }
 
     static ActivityRecord restoreFromXml(TypedXmlPullParser in,
@@ -10127,6 +10153,7 @@
         int userId = in.getAttributeInt(null, ATTR_USERID, 0);
         long createTime = in.getAttributeLong(null, ATTR_ID, -1);
         final int outerDepth = in.getDepth();
+        ActivityCallerState.CallerInfo initialCallerInfo = null;
 
         TaskDescription taskDescription = new TaskDescription();
         taskDescription.restoreFromXml(in);
@@ -10146,6 +10173,9 @@
                     persistentState = PersistableBundle.restoreFromXml(in);
                     if (DEBUG) Slog.d(TaskPersister.TAG,
                             "ActivityRecord: persistentState=" + persistentState);
+                } else if (android.security.Flags.contentUriPermissionApis()
+                        && TAG_INITIAL_CALLER_INFO.equals(name)) {
+                    initialCallerInfo = ActivityCallerState.CallerInfo.restoreFromXml(in);
                 } else {
                     Slog.w(TAG, "restoreActivity: unexpected name=" + name);
                     XmlUtils.skipCurrentTag(in);
@@ -10164,7 +10194,7 @@
             throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
                     " resolvedType=" + resolvedType);
         }
-        return new ActivityRecord.Builder(service)
+        final ActivityRecord r = new ActivityRecord.Builder(service)
                 .setLaunchedFromUid(launchedFromUid)
                 .setLaunchedFromPackage(launchedFromPackage)
                 .setLaunchedFromFeature(launchedFromFeature)
@@ -10176,6 +10206,11 @@
                 .setTaskDescription(taskDescription)
                 .setCreateTime(createTime)
                 .build();
+
+        if (android.security.Flags.contentUriPermissionApis() && initialCallerInfo != null) {
+            r.mCallerState.add(r.initialCallerInfoAccessToken, initialCallerInfo);
+        }
+        return r;
     }
 
     private static boolean isInVrUiMode(Configuration config) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d99000e..07afa5f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1586,6 +1586,10 @@
             return null;
         }
 
+        if (android.security.Flags.contentUriPermissionApis() && started.isAttached()) {
+            started.computeInitialCallerInfo();
+        }
+
         // Apply setAlwaysOnTop when starting an activity is successful regardless of creating
         // a new Activity or reusing the existing activity.
         if (options != null && options.getTaskAlwaysOnTop()) {