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);
             }