Merge "Support for a Context to "renounce" permissions." into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index a5647cb..b1e8645 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10387,6 +10387,7 @@
     method public abstract android.content.pm.PackageManager getPackageManager();
     method public abstract String getPackageName();
     method public abstract String getPackageResourcePath();
+    method @Nullable public android.content.ContextParams getParams();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(String, int);
     method @NonNull public final String getString(@StringRes int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 53e1539..5d8eade 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -228,6 +228,7 @@
     field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
     field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+    field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
     field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -2184,6 +2185,14 @@
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
   }
 
+  public final class ContextParams {
+    method @Nullable @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
+  }
+
+  public static final class ContextParams.Builder {
+    method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.ContextParams.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
+  }
+
   public class ContextWrapper extends android.content.Context {
     method public android.content.Context createCredentialProtectedStorageContext();
     method @Nullable public java.io.File getPreloadsFileCache();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fd56c44..d040938 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContextParams;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,6 +31,7 @@
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ContextParams;
 import android.content.ContextWrapper;
 import android.content.IContentProvider;
 import android.content.IIntentReceiver;
@@ -221,8 +221,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String mOpPackageName;
 
-    /** Attribution tag of this context */
-    private final @Nullable String mAttributionTag;
+    private final @NonNull ContextParams mParams;
 
     private final @NonNull ResourcesManager mResourcesManager;
     @UnsupportedAppUsage
@@ -470,7 +469,12 @@
     /** @hide */
     @Override
     public @Nullable String getAttributionTag() {
-        return mAttributionTag;
+        return mParams.getAttributionTag();
+    }
+
+    @Override
+    public @Nullable ContextParams getParams() {
+        return mParams;
     }
 
     @Override
@@ -2047,6 +2051,11 @@
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
         }
+        if (mParams.isRenouncedPermission(permission)
+                && pid == android.os.Process.myPid() && uid == android.os.Process.myUid()) {
+            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
+            return PERMISSION_DENIED;
+        }
         return PermissionManager.checkPermission(permission, pid, uid);
     }
 
@@ -2056,6 +2065,11 @@
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
         }
+        if (mParams.isRenouncedPermission(permission)
+                && pid == android.os.Process.myPid() && uid == android.os.Process.myUid()) {
+            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
+            return PERMISSION_DENIED;
+        }
 
         try {
             return ActivityManager.getService().checkPermissionWithToken(
@@ -2093,6 +2107,10 @@
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
         }
+        if (mParams.isRenouncedPermission(permission)) {
+            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
+            return PERMISSION_DENIED;
+        }
 
         return checkPermission(permission, Process.myPid(), Process.myUid());
     }
@@ -2393,8 +2411,9 @@
         LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mToken,
-                    new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY, null,
+                    mToken, new UserHandle(UserHandle.getUserId(application.uid)),
+                    flags, null, null);
 
             final int displayId = getDisplayId();
             final Integer overrideDisplayId = mForceDisplayOverrideInResources
@@ -2423,14 +2442,14 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, null,
+            return new ContextImpl(this, mMainThread, mPackageInfo, mParams, null,
                     mToken, user, flags, null, null);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, mAttributionTag, null,
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams, null,
                     mToken, user, flags, null, null);
 
             final int displayId = getDisplayId();
@@ -2469,7 +2488,7 @@
         final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
         final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
-                mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null);
+                mParams, splitName, mToken, mUser, mFlags, classLoader, null);
 
         context.setResources(ResourcesManager.getInstance().getResources(
                 mToken,
@@ -2502,7 +2521,7 @@
             overrideConfiguration = displayAdjustedConfig;
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = getDisplayId();
@@ -2520,7 +2539,7 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = display.getDisplayId();
@@ -2578,7 +2597,7 @@
 
 
     ContextImpl createBaseWindowContext(IBinder token, Display display) {
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
                 mSplitName, token, mUser, mFlags, mClassLoader, null);
         // Window contexts receive configurations directly from the server and as such do not
         // need to override their display in ResourcesManager.
@@ -2609,21 +2628,21 @@
 
     @NonNull
     @Override
-    public Context createContext(@NonNull ContextParams contextParams) {
-        return this;
+    public Context createContext(@NonNull ContextParams params) {
+        return new ContextImpl(this, mMainThread, mPackageInfo, params, mSplitName,
+                mToken, mUser, mFlags, mClassLoader, null);
     }
 
     @Override
     public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
-        return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName,
-                mToken, mUser, mFlags, mClassLoader, null);
+        return createContext(new ContextParams.Builder().setAttributionTag(attributionTag).build());
     }
 
     @Override
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
                 mToken, mUser, flags, mClassLoader, null);
     }
 
@@ -2631,7 +2650,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
                 mToken, mUser, flags, mClassLoader, null);
     }
 
@@ -2805,8 +2824,8 @@
     @UnsupportedAppUsage
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
-                0, null, null);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
+                ContextParams.EMPTY, null, null, null, 0, null, null);
         context.setResources(packageInfo.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
@@ -2823,8 +2842,8 @@
      */
     static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
         final LoadedApk packageInfo = systemContext.mPackageInfo;
-        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
-                null, null, null, 0, null, null);
+        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
+                ContextParams.EMPTY, null, null, null, 0, null, null);
         context.setResources(createResources(null, packageInfo, null, displayId, null,
                 packageInfo.getCompatibilityInfo(), null));
         context.updateDisplay(displayId);
@@ -2848,8 +2867,8 @@
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
             String opPackageName) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
-                0, null, opPackageName);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
+                ContextParams.EMPTY, null, null, null, 0, null, opPackageName);
         context.setResources(packageInfo.getResources());
         context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
                 : CONTEXT_TYPE_NON_UI;
@@ -2878,7 +2897,7 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
                 activityInfo.splitName, activityToken, null, 0, classLoader, null);
         context.mContextType = CONTEXT_TYPE_ACTIVITY;
 
@@ -2911,7 +2930,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk packageInfo, @Nullable String attributionTag,
+            @NonNull LoadedApk packageInfo, @NonNull ContextParams params,
             @Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
             int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
         mOuterContext = this;
@@ -2966,7 +2985,7 @@
         }
 
         mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
-        mAttributionTag = attributionTag;
+        mParams = Objects.requireNonNull(params);
         mContentResolver = new ApplicationContentResolver(this, mainThread);
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 30b3d43..df5c58c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -888,6 +888,14 @@
         return getAttributionTag();
     }
 
+    /**
+     * Return the set of parameters which this Context was created with, if it
+     * was created via {@link #createContext(ContextParams)}.
+     */
+    public @Nullable ContextParams getParams() {
+        return null;
+    }
+
     /** Return the full application info for this context's package. */
     public abstract ApplicationInfo getApplicationInfo();
 
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index 16128a6..17ec2a8 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -18,6 +18,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * This class represents rules around how a context being created via
@@ -48,9 +55,19 @@
  * @see Context#createContext(ContextParams)
  */
 public final class ContextParams {
+    private final String mAttributionTag;
+    private final String mReceiverPackage;
+    private final String mReceiverAttributionTag;
+    private final Set<String> mRenouncedPermissions;
 
-    private ContextParams() {
-        /* hide ctor */
+    /** {@hide} */
+    public static final ContextParams EMPTY = new ContextParams.Builder().build();
+
+    private ContextParams(@NonNull ContextParams.Builder builder) {
+        mAttributionTag = builder.mAttributionTag;
+        mReceiverPackage = builder.mReceiverPackage;
+        mReceiverAttributionTag = builder.mReceiverAttributionTag;
+        mRenouncedPermissions = builder.mRenouncedPermissions;
     }
 
     /**
@@ -58,7 +75,7 @@
      */
     @Nullable
     public String getAttributionTag() {
-        return null;
+        return mAttributionTag;
     }
 
     /**
@@ -66,7 +83,7 @@
      */
     @Nullable
     public String getReceiverPackage() {
-        return null;
+        return mReceiverPackage;
     }
 
     /**
@@ -74,13 +91,33 @@
      */
     @Nullable
     public String getReceiverAttributionTag() {
-        return null;
+        return mReceiverAttributionTag;
+    }
+
+    /**
+     * @return The set of permissions to treat as renounced.
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("NullableCollection")
+    @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+    public @Nullable Set<String> getRenouncedPermissions() {
+        return mRenouncedPermissions;
+    }
+
+    /** @hide */
+    public boolean isRenouncedPermission(@NonNull String permission) {
+        return mRenouncedPermissions != null && mRenouncedPermissions.contains(permission);
     }
 
     /**
      * Builder for creating a {@link ContextParams}.
      */
     public static final class Builder {
+        private String mAttributionTag;
+        private String mReceiverPackage;
+        private String mReceiverAttributionTag;
+        private Set<String> mRenouncedPermissions;
 
         /**
          * Sets an attribution tag against which to track permission accesses.
@@ -90,6 +127,7 @@
          */
         @NonNull
         public Builder setAttributionTag(@NonNull String attributionTag) {
+            mAttributionTag = Objects.requireNonNull(attributionTag);
             return this;
         }
 
@@ -104,18 +142,46 @@
         @NonNull
         public Builder setReceiverPackage(@NonNull String packageName,
                 @Nullable String attributionTag) {
+            mReceiverPackage = Objects.requireNonNull(packageName);
+            mReceiverAttributionTag = attributionTag;
             return this;
         }
 
         /**
-         * Creates a new instance. You need to either specify an attribution tag
-         * or a receiver package or both.
+         * Sets permissions which have been voluntarily "renounced" by the
+         * calling app.
+         * <p>
+         * Interactions performed through the created Context will ideally be
+         * treated as if these "renounced" permissions have not actually been
+         * granted to the app, regardless of their actual grant status.
+         * <p>
+         * This is designed for use by separate logical components within an app
+         * which have no intention of interacting with data or services that are
+         * protected by the renounced permissions.
+         * <p>
+         * Note that only {@link PermissionInfo#PROTECTION_DANGEROUS}
+         * permissions are supported by this mechanism.
+         *
+         * @param renouncedPermissions The set of permissions to treat as
+         *            renounced.
+         * @return This builder.
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+        public @NonNull Builder setRenouncedPermissions(@NonNull Set<String> renouncedPermissions) {
+            mRenouncedPermissions = Collections.unmodifiableSet(renouncedPermissions);
+            return this;
+        }
+
+        /**
+         * Creates a new instance.
          *
          * @return The new instance.
          */
         @NonNull
         public ContextParams build() {
-            return new ContextParams();
+            return new ContextParams(this);
         }
     }
 }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index b71fb27..609f417 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -172,6 +172,11 @@
     }
 
     @Override
+    public @Nullable ContextParams getParams() {
+        return mBase.getParams();
+    }
+
+    @Override
     public ApplicationInfo getApplicationInfo() {
         return mBase.getApplicationInfo();
     }
@@ -1045,6 +1050,12 @@
     }
 
     @Override
+    @NonNull
+    public Context createContext(@NonNull ContextParams contextParams) {
+        return mBase.createContext(contextParams);
+    }
+
+    @Override
     public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
         return mBase.createAttributionContext(attributionTag);
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 072bb87..4bbb69f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5561,6 +5561,11 @@
     <permission android:name="android.permission.READ_PEOPLE_DATA"
                 android:protectionLevel="signature|appPredictor|recents"/>
 
+    <!-- @hide @SystemApi Allows a logical component within an application to
+         temporarily renounce a set of otherwise granted permissions. -->
+    <permission android:name="android.permission.RENOUNCE_PERMISSIONS"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->