am 7c97a3dd: Merge "Permissions control via profile/device owner admin" into mnc-dev

* commit '7c97a3ddea62a3bd8a95ca51e7b172b209388815':
  Permissions control via profile/device owner admin
diff --git a/api/current.txt b/api/current.txt
index e886227..73829e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5730,6 +5730,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public int getPermissionPolicy(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
@@ -5782,6 +5783,8 @@
     method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
     method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
     method public void setPasswordQuality(android.content.ComponentName, int);
+    method public boolean setPermissionGranted(android.content.ComponentName, java.lang.String, java.lang.String, boolean);
+    method public void setPermissionPolicy(android.content.ComponentName, int);
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
@@ -5870,6 +5873,9 @@
     field public static final int PASSWORD_QUALITY_NUMERIC_COMPLEX = 196608; // 0x30000
     field public static final int PASSWORD_QUALITY_SOMETHING = 65536; // 0x10000
     field public static final int PASSWORD_QUALITY_UNSPECIFIED = 0; // 0x0
+    field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
+    field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
+    field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index b9ad94b..d74bf6e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5827,6 +5827,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public int getPermissionPolicy(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
@@ -5885,6 +5886,8 @@
     method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
     method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
     method public void setPasswordQuality(android.content.ComponentName, int);
+    method public boolean setPermissionGranted(android.content.ComponentName, java.lang.String, java.lang.String, boolean);
+    method public void setPermissionPolicy(android.content.ComponentName, int);
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
@@ -5978,6 +5981,9 @@
     field public static final int PASSWORD_QUALITY_NUMERIC_COMPLEX = 196608; // 0x30000
     field public static final int PASSWORD_QUALITY_SOMETHING = 65536; // 0x10000
     field public static final int PASSWORD_QUALITY_UNSPECIFIED = 0; // 0x0
+    field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
+    field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
+    field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ed814c3..cf9813f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -807,6 +807,24 @@
     public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED
             = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
 
+    /**
+     * Permission policy to prompt user for new permission requests for runtime permissions.
+     * Already granted or denied permissions are not affected by this.
+     */
+    public static final int PERMISSION_POLICY_PROMPT = 0;
+
+    /**
+     * Permission policy to always grant new permission requests for runtime permissions.
+     * Already granted or denied permissions are not affected by this.
+     */
+    public static final int PERMISSION_POLICY_AUTO_GRANT = 1;
+
+    /**
+     * Permission policy to always deny new permission requests for runtime permissions.
+     * Already granted or denied permissions are not affected by this.
+     */
+    public static final int PERMISSION_POLICY_AUTO_DENY = 2;
+
 
     /**
      * Return true if the given administrator component is currently
@@ -4342,4 +4360,58 @@
             Log.w(TAG, "Failed talking with device policy service", re);
         }
     }
+
+    /**
+     * Called by profile or device owners to set the default response for future runtime permission
+     * requests by applications. The policy can allow for normal operation which prompts the
+     * user to grant a permission, or can allow automatic granting or denying of runtime
+     * permission requests by an application. This also applies to new permissions declared by app
+     * updates.
+     * @param admin Which profile or device owner this request is associated with.
+     * @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT},
+     * {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
+     */
+    public void setPermissionPolicy(ComponentName admin, int policy) {
+        try {
+            mService.setPermissionPolicy(admin, policy);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Failed talking with device policy service", re);
+        }
+    }
+
+    /**
+     * Returns the current runtime permission policy set by the device or profile owner. The
+     * default is {@link #PERMISSION_POLICY_PROMPT}.
+     * @param admin Which profile or device owner this request is associated with.
+     * @return the current policy for future permission requests.
+     */
+    public int getPermissionPolicy(ComponentName admin) {
+        try {
+            return mService.getPermissionPolicy(admin);
+        } catch (RemoteException re) {
+            return PERMISSION_POLICY_PROMPT;
+        }
+    }
+
+    /**
+     * Grants or revokes a runtime permission to a specific application so that the user
+     * does not have to be prompted. This might affect all permissions in a group that the
+     * runtime permission belongs to. This method can only be called by a profile or device
+     * owner.
+     * @param admin Which profile or device owner this request is associated with.
+     * @param packageName The application to grant or revoke a permission to.
+     * @param permission The permission to grant or revoke.
+     * @param granted Whether or not to grant the permission. If false, all permissions in the
+     * associated permission group will be denied.
+     * @return whether the permission was successfully granted or revoked
+     */
+    public boolean setPermissionGranted(ComponentName admin, String packageName,
+            String permission, boolean granted) {
+        try {
+            return mService.setPermissionGranted(admin, packageName, permission, granted);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Failed talking with device policy service", re);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a678c51..833bc00 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -229,4 +229,9 @@
     boolean getDoNotAskCredentialsOnBoot();
 
     void notifyPendingSystemUpdate(in long updateReceivedTime);
+
+    void setPermissionPolicy(in ComponentName admin, int policy);
+    int  getPermissionPolicy(in ComponentName admin);
+    boolean setPermissionGranted(in ComponentName admin, String packageName, String permission,
+            boolean granted);
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 31d7f74..1d00de9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -182,6 +182,7 @@
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
     private static final String ATTR_SETUP_COMPLETE = "setup-complete";
     private static final String ATTR_PREFERRED_SETUP_ACTIVITY = "setup-activity";
+    private static final String ATTR_PERMISSION_POLICY = "permission-policy";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
 
@@ -300,6 +301,7 @@
         int mPasswordOwner = -1;
         long mLastMaximumTimeToLock = -1;
         boolean mUserSetupComplete = false;
+        int mPermissionPolicy;
 
         final HashMap<ComponentName, ActiveAdmin> mAdminMap = new HashMap<>();
         final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
@@ -1409,6 +1411,10 @@
                 out.attribute(null, ATTR_SETUP_COMPLETE,
                         Boolean.toString(true));
             }
+            if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
+                out.attribute(null, ATTR_PERMISSION_POLICY,
+                        Integer.toString(policy.mPermissionPolicy));
+            }
             if (policy.mDelegatedCertInstallerPackage != null) {
                 out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
                         policy.mDelegatedCertInstallerPackage);
@@ -1537,6 +1543,10 @@
             if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
                 policy.mUserSetupComplete = true;
             }
+            String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
+            if (!TextUtils.isEmpty(permissionPolicy)) {
+                policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
+            }
             policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
                     ATTR_DELEGATED_CERT_INSTALLER);
             String preferredSetupActivity =
@@ -4253,14 +4263,22 @@
             return;
         }
         UserHandle callingUser = Binder.getCallingUserHandle();
+        int userId = callingUser.getIdentifier();
         // Check if this is the profile owner who is calling
         getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
         synchronized (this) {
+            // Reset some of the profile-owner policies
+            DevicePolicyData policy = getUserData(userId);
+            policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
+            policy.mDelegatedCertInstallerPackage = null;
+            policy.mStatusBarEnabledState = true;
+            saveSettingsLocked(userId);
+
             long ident = Binder.clearCallingIdentity();
             try {
                 clearUserRestrictions(callingUser);
                 if (mDeviceOwner != null) {
-                    mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
+                    mDeviceOwner.removeProfileOwner(userId);
                     mDeviceOwner.writeOwnerFile();
                 }
             } finally {
@@ -6261,4 +6279,48 @@
             }
         }
     }
+
+    @Override
+    public void setPermissionPolicy(ComponentName admin, int policy) throws RemoteException {
+        int userId = UserHandle.getCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            DevicePolicyData userPolicy = getUserData(userId);
+            if (userPolicy.mPermissionPolicy != policy) {
+                userPolicy.mPermissionPolicy = policy;
+                saveSettingsLocked(userId);
+            }
+        }
+    }
+
+    @Override
+    public int getPermissionPolicy(ComponentName admin) throws RemoteException {
+        int userId = UserHandle.getCallingUserId();
+        synchronized (this) {
+            DevicePolicyData userPolicy = getUserData(userId);
+            return userPolicy.mPermissionPolicy;
+        }
+    }
+
+    @Override
+    public boolean setPermissionGranted(ComponentName admin, String packageName,
+            String permission, boolean granted) throws RemoteException {
+        UserHandle user = Binder.getCallingUserHandle();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (granted) {
+                    mContext.getPackageManager().grantPermission(packageName, permission, user);
+                } else {
+                    mContext.getPackageManager().revokePermission(packageName, permission, user);
+                }
+                return true;
+            } catch (SecurityException se) {
+                return false;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
 }