Merge "Add self revocation public API"
diff --git a/core/api/current.txt b/core/api/current.txt
index ab51690..e702dad 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10876,6 +10876,8 @@
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void revokeUriPermission(String, android.net.Uri, int);
+    method public void selfRevokePermission(@NonNull String);
+    method public void selfRevokePermissions(@NonNull java.util.Collection<java.lang.String>);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent, @Nullable String);
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ac56488..e1c05e2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9604,6 +9604,7 @@
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
+    method @BinderThread public void onSelfRevokePermissions(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f3e9f10..fe512ff 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -113,6 +113,7 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -2170,6 +2171,11 @@
     }
 
     @Override
+    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+        getSystemService(PermissionManager.class).selfRevokePermissions(permissions);
+    }
+
+    @Override
     public int checkCallingPermission(String permission) {
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bccfacf..2df6f9a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -94,8 +94,11 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Interface to global information about an application environment.  This is
@@ -6407,6 +6410,43 @@
             @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags,
             @Nullable String message);
 
+
+    /**
+     * Triggers the asynchronous revocation of a permission.
+     *
+     * @param permName The name of the permission to be revoked.
+     * @see #selfRevokePermissions(Collection)
+     */
+    public void selfRevokePermission(@NonNull String permName) {
+        selfRevokePermissions(Collections.singletonList(permName));
+    }
+
+    /**
+     * Triggers the revocation of one or more permissions for the calling package. A package is only
+     * able to revoke a permission under the following conditions:
+     * <ul>
+     * <li>Each permission in {@code permissions} must be granted to the calling package.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. The revocation happens asynchronously and kills all processes running in the
+     * calling UID. It will be triggered once it is safe to do so. In particular, it will not be
+     * triggered as long as the package remains in the foreground, or has any active manifest
+     * components (e.g. when another app is accessing a content provider in the package).
+     * <p>
+     * If you want to revoke the permissions right away, you could call {@code System.exit()}, but
+     * this could affect other apps that are accessing your app at the moment. For example, apps
+     * accessing a content provider in your app will all crash.
+     *
+     * @param permissions Collection of permissions to be revoked.
+     * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
+     * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)
+     */
+    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     /** @hide */
     @IntDef(flag = true, prefix = { "CONTEXT_" }, value = {
             CONTEXT_INCLUDE_CODE,
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a02004..805e499 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -53,6 +53,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -1015,6 +1016,11 @@
     }
 
     @Override
+    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+        mBase.selfRevokePermissions(permissions);
+    }
+
+    @Override
     public Context createPackageContext(String packageName, int flags)
         throws PackageManager.NameNotFoundException {
         return mBase.createPackageContext(packageName, flags);
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0e32a78..5814bac 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -56,4 +56,6 @@
             in AndroidFuture<String> callback);
     void getUnusedAppCount(
             in AndroidFuture callback);
+    void selfRevokePermissions(in String packageName, in List<String> permissions,
+            in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 90b5e51..8e5581b 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -76,6 +76,8 @@
 
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
+    void selfRevokePermissions(String packageName, in List<String> permissions);
+
     void startOneTimePermissionSession(String packageName, int userId, long timeout,
             int importanceToResetTimer, int importanceToKeepSessionAlive);
 
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index a0788e7..47cd107 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -817,4 +817,40 @@
             }
         });
     }
+
+    /**
+     * Triggers the revocation of one or more permissions for a package, under the following
+     * conditions:
+     * <ul>
+     * <li>The package {@code packageName} must be under the same UID as the calling process
+     * (typically, the target package is the calling package).
+     * <li>Each permission in {@code permissions} must be granted to the package
+     * {@code packageName}.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     *
+     * @see Context#selfRevokePermissions(Collection)
+     *
+     * @hide
+     */
+    public void selfRevokePermissions(@NonNull String packageName,
+            @NonNull List<String> permissions) {
+        mRemoteService.postAsync(service -> {
+            AndroidFuture<Void> future = new AndroidFuture<>();
+            service.selfRevokePermissions(packageName, permissions, future);
+            return future;
+        }).whenComplete((result, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
+                        + " for package " + packageName, err);
+            }
+        });
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index c979303..dcbab62 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -324,6 +324,27 @@
             @NonNull Consumer<String> callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
+
+    /**
+     * Triggers the revocation of one or more permissions for a package. This should only be called
+     * at the request of {@code packageName}.
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     * @param callback Callback waiting for operation to be complete.
+     *
+     * @see PermissionManager#selfRevokePermissions(java.util.Collection)
+     */
+    @BinderThread
+    public void onSelfRevokePermissions(@NonNull String packageName,
+            @NonNull List<String> permissions, @NonNull Runnable callback) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     /**
      * Get a user-readable sentence, describing the set of privileges that are to be granted to a
      * companion app managing a device of the given profile.
@@ -646,6 +667,20 @@
                     callback.completeExceptionally(t);
                 }
             }
+
+            @Override
+            public void selfRevokePermissions(@NonNull String packageName,
+                    @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
+                try {
+                    enforceSomePermissionsGrantedToCaller(
+                            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+                    Objects.requireNonNull(callback);
+                    onSelfRevokePermissions(packageName, permissions,
+                            () -> callback.complete(null));
+                } catch (Throwable t) {
+                    callback.completeExceptionally(t);
+                }
+            }
         };
     }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 4f22876..13941dc 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -68,6 +68,7 @@
 import com.android.internal.util.CollectionUtils;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -561,6 +562,19 @@
     }
 
     /**
+     * @see Context#selfRevokePermissions(Collection)
+     * @hide
+     */
+    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+        try {
+            mPermissionManager.selfRevokePermissions(mContext.getPackageName(),
+                    new ArrayList<String>(permissions));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the state flags associated with a permission.
      *
      * @param packageName the package name for which to get the flags
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 85e54f3..1cfcdf51 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -386,7 +386,8 @@
     @Override
     public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
             long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) {
-        mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
                 "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
                         + " to register permissions as one time.");
         Objects.requireNonNull(packageName);
@@ -557,6 +558,12 @@
     }
 
     @Override
+    public void selfRevokePermissions(@NonNull String packageName,
+            @NonNull List<String> permissions) {
+        mPermissionManagerServiceImpl.selfRevokePermissions(packageName, permissions);
+    }
+
+    @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId) {
         return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 21cb2c9..7833c43 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1591,6 +1591,25 @@
         }
     }
 
+    @Override
+    public void selfRevokePermissions(String packageName, List<String> permissions) {
+        final int callingUid = Binder.getCallingUid();
+        int callingUserId = UserHandle.getUserId(callingUid);
+        int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
+        if (targetPackageUid != callingUid) {
+            throw new SecurityException("uid " + callingUid
+                    + " cannot revoke permissions for package " + packageName + " with uid "
+                    + targetPackageUid);
+        }
+        for (String permName : permissions) {
+            if (!checkCallingOrSelfPermission(permName)) {
+                throw new SecurityException("uid " + callingUid + " cannot revoke permission "
+                        + permName + " because it does not hold that permission");
+            }
+        }
+        mPermissionControllerManager.selfRevokePermissions(packageName, permissions);
+    }
+
     private boolean mayManageRolePermission(int uid) {
         final PackageManager packageManager = mContext.getPackageManager();
         final String[] packageNames = packageManager.getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 3771f03..c582f9e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -327,6 +327,26 @@
     void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
 
     /**
+     * Triggers the revocation of one or more permissions for a package, under the following
+     * conditions:
+     * <ul>
+     * <li>The package {@code packageName} must be under the same UID as the calling process
+     *   (typically, the target package is the calling package).
+     * <li>Each permission in {@code permissions} must be granted to the package
+     * {@code packageName}.
+     * <li>Each permission in {@code permissions} must be a runtime permission.
+     * </ul>
+     * <p>
+     * For every permission in {@code permissions}, the entire permission group it belongs to will
+     * be revoked. This revocation happens asynchronously and kills all processes running in the
+     * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+     *
+     * @param packageName The name of the package for which the permissions will be revoked.
+     * @param permissions List of permissions to be revoked.
+     */
+    void selfRevokePermissions(String packageName, List<String> permissions);
+
+    /**
      * Get whether you should show UI with rationale for requesting a permission. You should do this
      * only if you do not have the permission and the context in which the permission is requested
      * does not clearly communicate to the user what would be the benefit from grating this