API for granting/revoking runtime permissions.

Expose these as public API, since they're useful for devices where
the raw "pm grant" or "pm revoke" commands can be flaky.

Test: builds, boots
Bug: 75315597
Change-Id: I2de94587945f08fd09ebe729bb9872542a11ef26
diff --git a/api/current.txt b/api/current.txt
index 5e7fa591..9f9f7b8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6112,8 +6112,12 @@
     method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+    method public void grantRuntimePermission(java.lang.String, java.lang.String);
+    method public void grantRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
     method public boolean performGlobalAction(int);
+    method public void revokeRuntimePermission(java.lang.String, java.lang.String);
+    method public void revokeRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
     method public boolean setRotation(int);
     method public void setRunAsMonkey(boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index e84d204..be294e3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,8 +1,10 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
   }
 
 }
@@ -127,8 +129,8 @@
   public final class UiAutomation {
     method public void destroy();
     method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
-    method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
   }
 
   public class UiModeManager {
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..bd4933a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -47,6 +46,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -876,15 +876,35 @@
     }
 
     /**
+     * Grants a runtime permission to a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to grant the permission.
+     */
+    public void grantRuntimePermission(String packageName, String permission) {
+        grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public boolean grantRuntimePermission(String packageName, String permission,
+            UserHandle userHandle) {
+        grantRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
      * Grants a runtime permission to a package for a user.
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
-     * @return Whether granting succeeded.
-     *
-     * @hide
+     * @throws SecurityException if unable to grant the permission.
      */
-    @TestApi
-    public boolean grantRuntimePermission(String packageName, String permission,
+    public void grantRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
@@ -896,25 +916,42 @@
             // Calling out without a lock held.
             mUiAutomationConnection.grantRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error granting runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
-     * Revokes a runtime permission from a package for a user.
-     * @param packageName The package from which to revoke.
-     * @param permission The permission to revoke.
-     * @return Whether revoking succeeded.
-     *
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermission(String packageName, String permission) {
+        revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
      * @hide
      */
+    @Deprecated
     @TestApi
     public boolean revokeRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
+        revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermissionAsUser(String packageName, String permission,
+            UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
@@ -925,12 +962,9 @@
             // Calling out without a lock held.
             mUiAutomationConnection.revokeRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error revoking runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
@@ -949,6 +983,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source = null;
         ParcelFileDescriptor sink = null;
@@ -991,6 +1026,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source_read = null;
         ParcelFileDescriptor sink_read = null;
@@ -1056,6 +1092,16 @@
         }
     }
 
+    private void warnIfBetterCommand(String cmd) {
+        if (cmd.startsWith("pm grant ")) {
+            Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm grant'");
+        } else if (cmd.startsWith("pm revoke ")) {
+            Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm revoke'");
+        }
+    }
+
     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
 
         public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c4d3667..a35b5ff 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2003,11 +2003,11 @@
     <permission android:name="android.permission.REMOVE_TASKS"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+    <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
     <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
         android:protectionLevel="signature|privileged|development" />
 
-    <!-- @SystemApi @hide Allows an application to embed other activities -->
+    <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
     <permission android:name="android.permission.ACTIVITY_EMBEDDING"
                 android:protectionLevel="signature|privileged|development" />