diff --git a/core/api/current.txt b/core/api/current.txt index 893616e059662..c6b4ea8e803ef 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9935,9 +9935,9 @@ package android.content { public static final class AttributionSource.Builder { ctor public AttributionSource.Builder(int); method @NonNull public android.content.AttributionSource build(); - method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@NonNull String); - method @NonNull public android.content.AttributionSource.Builder setNext(@NonNull android.content.AttributionSource); - method @NonNull public android.content.AttributionSource.Builder setPackageName(@NonNull String); + method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String); + method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource); + method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String); } public abstract class BroadcastReceiver { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 3f2aa79da3fcb..ea57e9863e6f5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2235,7 +2235,7 @@ package android.content { } public static final class AttributionSource.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@NonNull java.util.Set); + method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@Nullable java.util.Set); } public abstract class BroadcastReceiver { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 7b6f9b3f2b26e..90a3667016dea 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -666,6 +666,7 @@ package android.content { public final class AttributionSource implements android.os.Parcelable { ctor public AttributionSource(int, @Nullable String, @Nullable String); ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable android.content.AttributionSource); + ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set, @Nullable android.content.AttributionSource); } public final class AutofillOptions implements android.os.Parcelable { @@ -1985,6 +1986,7 @@ package android.permission { public final class PermissionManager { method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List getIndicatorAppOpUsageData(); + method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource); } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0f38b5fdb5c31..f0d5a893bb96c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5254,32 +5254,17 @@ public class Activity extends ContextThemeWrapper return; } - List filteredPermissions = null; - if (!getAttributionSource().getRenouncedPermissions().isEmpty()) { final int permissionCount = permissions.length; for (int i = 0; i < permissionCount; i++) { if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) { - if (filteredPermissions == null) { - filteredPermissions = new ArrayList<>(i); - for (int j = 0; j < i; j++) { - filteredPermissions.add(permissions[i]); - } - } - } else if (filteredPermissions != null) { - filteredPermissions.add(permissions[i]); + throw new IllegalArgumentException("Cannot request renounced permission: " + + permissions[i]); } } } - final Intent intent; - if (filteredPermissions == null) { - intent = getPackageManager().buildRequestPermissionsIntent(permissions); - } else { - intent = getPackageManager().buildRequestPermissionsIntent( - filteredPermissions.toArray(new String[0])); - } - + final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); mHasCurrentPermissionsRequest = true; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index eb31b5294a264..89312f4b3837f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -115,6 +115,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; class ReceiverRestrictedContext extends ContextWrapper { @@ -3074,14 +3075,16 @@ class ContextImpl extends Context { mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; mParams = Objects.requireNonNull(params); - mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource); + mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource, + params.getRenouncedPermissions()); mContentResolver = new ApplicationContentResolver(this, mainThread); } private @NonNull AttributionSource createAttributionSource(@Nullable String attributionTag, - @Nullable AttributionSource nextAttributionSource) { - AttributionSource attributionSource = new AttributionSource(Process.myUid(), mOpPackageName, - attributionTag, nextAttributionSource); + @Nullable AttributionSource nextAttributionSource, + @Nullable Set renouncedPermissions) { + AttributionSource attributionSource = new AttributionSource(Process.myUid(), + mOpPackageName, attributionTag, renouncedPermissions, nextAttributionSource); // If we want to access protected data on behalf of another app we need to // tell the OS that we opt in to participate in the attribution chain. if (nextAttributionSource != null) { diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 053bfc1a3253a..c851519e9b507 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -178,6 +178,15 @@ public final class AttributionSource implements Parcelable { /*renouncedPermissions*/ null, next); } + /** @hide */ + @TestApi + public AttributionSource(int uid, @Nullable String packageName, + @Nullable String attributionTag, @Nullable Set renouncedPermissions, + @Nullable AttributionSource next) { + this(uid, packageName, attributionTag, /*token*/ null, + renouncedPermissions, next); + } + /** @hide */ public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) { @@ -526,7 +535,7 @@ public final class AttributionSource implements Parcelable { /** * The package that is accessing the permission protected data. */ - public @NonNull Builder setPackageName(@NonNull String value) { + public @NonNull Builder setPackageName(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mPackageName = value; @@ -536,7 +545,7 @@ public final class AttributionSource implements Parcelable { /** * The attribution tag of the app accessing the permission protected data. */ - public @NonNull Builder setAttributionTag(@NonNull String value) { + public @NonNull Builder setAttributionTag(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; mAttributionTag = value; @@ -550,7 +559,7 @@ public final class AttributionSource implements Parcelable { */ @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) - public @NonNull Builder setRenouncedPermissions(@NonNull Set value) { + public @NonNull Builder setRenouncedPermissions(@Nullable Set value) { checkNotUsed(); mBuilderFieldsSet |= 0x10; mRenouncedPermissions = value; @@ -560,7 +569,7 @@ public final class AttributionSource implements Parcelable { /** * The next app to receive the permission protected data. */ - public @NonNull Builder setNext(@NonNull AttributionSource value) { + public @NonNull Builder setNext(@Nullable AttributionSource value) { checkNotUsed(); mBuilderFieldsSet |= 0x20; mNext = value; diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java index bd3eaea24126a..ace2ba721c108 100644 --- a/core/java/android/content/ContextParams.java +++ b/core/java/android/content/ContextParams.java @@ -16,10 +16,13 @@ package android.content; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityThread; +import android.content.pm.PackageManager; import java.util.Collections; import java.util.Objects; @@ -179,6 +182,13 @@ public final class ContextParams { @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public @NonNull Builder setRenouncedPermissions( @Nullable Set renouncedPermissions) { + // This is not a security check but a fail fast - the OS enforces the permission too + if (renouncedPermissions != null && !renouncedPermissions.isEmpty() + && ActivityThread.currentApplication().checkSelfPermission(Manifest.permission + .RENOUNCE_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Renouncing permissions requires: " + + Manifest.permission.RENOUNCE_PERMISSIONS); + } mRenouncedPermissions = renouncedPermissions; return this; } diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index fc963fef3eb4d..049bfe7f23fb2 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -979,7 +979,9 @@ public final class PermissionChecker { int uid, @NonNull Set renouncedPermissions) { final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED; - if (permissionGranted && renouncedPermissions.contains(permission)) { + if (permissionGranted && renouncedPermissions.contains(permission) + && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, + /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { return false; } return permissionGranted; diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 936cbfc70708b..751e9aafdf094 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -1114,6 +1114,7 @@ public final class PermissionManager { * * @hide */ + @TestApi public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) { try { return mPermissionManager.registerAttributionSource(source); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8312320a8d873..89a2614a1d928 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -501,6 +501,8 @@ applications that come with the platform + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b6b5ae5b6ce41..4ff3c55e3a701 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -449,6 +449,9 @@ + + + diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 7e3911ac9a6dd..4f74d5f6f8294 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -258,8 +258,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private final PermissionRegistry mRegistry = new PermissionRegistry(); @NonNull - private final AttributionSourceRegistry mAttributionSourceRegistry = - new AttributionSourceRegistry(); + private final AttributionSourceRegistry mAttributionSourceRegistry; @GuardedBy("mLock") @Nullable @@ -379,6 +378,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { mSystemPermissions = systemConfig.getSystemPermissions(); mGlobalGids = systemConfig.getGlobalGids(); mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper()); + mAttributionSourceRegistry = new AttributionSourceRegistry(context); // propagate permission configuration final ArrayMap permConfig = @@ -5294,6 +5294,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { private static final class AttributionSourceRegistry { private final Object mLock = new Object(); + private final Context mContext; + + AttributionSourceRegistry(@NonNull Context context) { + mContext = context; + } + private final WeakHashMap mAttributions = new WeakHashMap<>(); public @NonNull AttributionSource registerAttributionSource( @@ -5321,7 +5327,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { // app passing the source, in which case you must trust the other side; final int callingUid = Binder.getCallingUid(); - if (source.getUid() != callingUid) { + if (source.getUid() != callingUid && mContext.checkPermission( + Manifest.permission.UPDATE_APP_OPS_STATS, /*pid*/ -1, callingUid) + != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot register attribution source for uid:" + source.getUid() + " from uid:" + callingUid); }