Hookup renounced permissions
Propagate renounced permissions from context params
to the context attribution source. Throw if one
tries to request at runtime a renounced permission.
Also make the AttributionSource take null for the
setters to ease usage, otherwise folks should always
check for null before calling a builder method.
Additionally, we allow apps that have UPDATE_APP_OPS_STATS
to register arbitrary trusted AttributionSource for
testing. Note that this permission allows abritrary app
op operations, thus we are not relaxing the security
model.
bug: 158792096
Test: atest CtsPermission5TestCases
Change-Id: I4330684bb8695fb998cf31e9363b94ad981ba2cc
diff --git a/core/api/current.txt b/core/api/current.txt
index 1299ce5..7d1ba9f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9935,9 +9935,9 @@
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 3f2aa79..ea57e98 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2235,7 +2235,7 @@
}
public static final class AttributionSource.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@Nullable java.util.Set<java.lang.String>);
}
public abstract class BroadcastReceiver {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7b6f9b3..90a3667 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -666,6 +666,7 @@
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<java.lang.String>, @Nullable android.content.AttributionSource);
}
public final class AutofillOptions implements android.os.Parcelable {
@@ -1985,6 +1986,7 @@
public final class PermissionManager {
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> 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 0f38b5f..f0d5a89 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5254,32 +5254,17 @@
return;
}
- List<String> 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 eb31b52..89312f4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -115,6 +115,7 @@
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 @@
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<String> 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 053bfc1..c851519 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -179,6 +179,15 @@
}
/** @hide */
+ @TestApi
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
+ @Nullable AttributionSource next) {
+ this(uid, packageName, attributionTag, /*token*/ null,
+ renouncedPermissions, next);
+ }
+
+ /** @hide */
public AttributionSource(@NonNull AttributionSource current,
@Nullable AttributionSource next) {
this(current.getUid(), current.getPackageName(), current.getAttributionTag(),
@@ -526,7 +535,7 @@
/**
* 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 @@
/**
* 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 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
- public @NonNull Builder setRenouncedPermissions(@NonNull Set<String> value) {
+ public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mRenouncedPermissions = value;
@@ -560,7 +569,7 @@
/**
* 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 bd3eaea..ace2ba7 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 @@
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
public @NonNull Builder setRenouncedPermissions(
@Nullable Set<String> 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 fc963fe..049bfe7 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -979,7 +979,9 @@
int uid, @NonNull Set<String> 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 936cbfc..751e9aa 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1114,6 +1114,7 @@
*
* @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 8312320..89a2614 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -501,6 +501,8 @@
<permission name="android.permission.SCHEDULE_PRIORITIZED_ALARM" />
<!-- Permission required for CTS test - SystemMediaRouter2Test -->
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+ <!-- Permission required for CTS test - CtsPermission5TestCases -->
+ <permission name="android.permission.RENOUNCE_PERMISSIONS" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b6b5ae5..4ff3c55 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -449,6 +449,9 @@
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
+ <!-- Permission required for CTS test - CtsPermission5TestCases -->
+ <uses-permission android:name="android.permission.RENOUNCE_PERMISSIONS" />
+
<!-- Permission required for CTS test - android.widget.cts.ToastTest -->
<uses-permission android:name="android.permission.UNLIMITED_TOASTS" />
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 7e3911a..4f74d5f 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 @@
private final PermissionRegistry mRegistry = new PermissionRegistry();
@NonNull
- private final AttributionSourceRegistry mAttributionSourceRegistry =
- new AttributionSourceRegistry();
+ private final AttributionSourceRegistry mAttributionSourceRegistry;
@GuardedBy("mLock")
@Nullable
@@ -379,6 +378,7 @@
mSystemPermissions = systemConfig.getSystemPermissions();
mGlobalGids = systemConfig.getGlobalGids();
mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());
+ mAttributionSourceRegistry = new AttributionSourceRegistry(context);
// propagate permission configuration
final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
@@ -5294,6 +5294,12 @@
private static final class AttributionSourceRegistry {
private final Object mLock = new Object();
+ private final Context mContext;
+
+ AttributionSourceRegistry(@NonNull Context context) {
+ mContext = context;
+ }
+
private final WeakHashMap<IBinder, AttributionSource> mAttributions = new WeakHashMap<>();
public @NonNull AttributionSource registerAttributionSource(
@@ -5321,7 +5327,9 @@
// 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);
}