diff options
| author | 2024-01-15 16:45:02 +0000 | |
|---|---|---|
| committer | 2024-02-08 14:14:53 +0000 | |
| commit | 6adf3761979cff99ebb38468bd2eada82dfd13c5 (patch) | |
| tree | 4f277ebe14ae4dbf23da781529efe80f2e9bf752 | |
| parent | f12aa38bdb91f81c357854c67a0213c48e477f70 (diff) | |
Add ComponentCaller#checkContentUriPermission API for Activity
The new API checks if the app that launched the activity, i.e. activity
caller, had access to the content URI at launch time. It doesn't perform
a real time check to ensure no loss of grant information in the case of
caller's termination, hence only works for content URIs passed at
launch to mimic the lifetime of grant flags. Finally, for security
reasons, the method requires the caller of the API to the same access to
the content URI, otherwise it throws.
The code for this check is located in a new class ActivityCallerState.
Bug: 293467489
Test: atest CtsAndroidAppTestCases:android.app.cts.ComponentCallerTest
Change-Id: Ide1ea3470e8cc48f4d59e431ef19681050273af2
8 files changed, 372 insertions, 1 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index bfa486b4eda4..48887df60d70 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5425,6 +5425,7 @@ package android.app { @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 b8bd030872c1..a59f04bf4f3a 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 @@ public class ActivityClient { } } + /** 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 583408ed8db9..a440dbc48db6 100644 --- a/core/java/android/app/ComponentCaller.java +++ b/core/java/android/app/ComponentCaller.java @@ -18,6 +18,9 @@ package android.app; 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 @@ public final class ComponentCaller { 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 5b044f616487..05fee72b7e61 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -23,6 +23,7 @@ import android.app.PictureInPictureParams; 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 @@ interface IActivityClientController { 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 000000000000..4416605d9f04 --- /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 2e0546eee8e7..173e139e124c 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_FREEFORM; 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.Intent; 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.LocalServices; 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 @@ class ActivityClientController extends IActivityClientController.Stub { 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 c2117eaa72c4..09c329be7d09 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.am.PendingIntentRecord; 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } return appContext; }); + mCallerState = new ActivityCallerState(mAtmService); } /** @@ -10113,6 +10129,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A .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 d99000efeeb4..07afa5fc21be 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 @@ class ActivityStarter { 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()) { |