Merge "Refactor DPMS Cert Installer and App Restrictions delegation."
diff --git a/api/current.txt b/api/current.txt
index c39bce8..861a265 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6121,16 +6121,18 @@
method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
- method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+ method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
- method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+ method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+ method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -6175,7 +6177,7 @@
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isBackupServiceEnabled(android.content.ComponentName);
- method public boolean isCallerApplicationRestrictionsManagingPackage();
+ method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isManagedProfile(android.content.ComponentName);
@@ -6203,14 +6205,15 @@
method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
- method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
- method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+ method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+ method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -6258,6 +6261,7 @@
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+ field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6267,6 +6271,8 @@
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+ field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6274,6 +6280,7 @@
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+ field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 1f3235d..cdd5dab 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6317,16 +6317,18 @@
method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
- method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+ method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
- method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+ method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+ method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public java.lang.String getDeviceOwner();
@@ -6382,7 +6384,7 @@
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isBackupServiceEnabled(android.content.ComponentName);
- method public boolean isCallerApplicationRestrictionsManagingPackage();
+ method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceManaged();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isDeviceProvisioned();
@@ -6417,14 +6419,15 @@
method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
- method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
- method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+ method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+ method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setDeviceProvisioningConfigApplied();
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
@@ -6473,6 +6476,7 @@
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+ field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6485,6 +6489,8 @@
field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+ field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6492,6 +6498,7 @@
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+ field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
diff --git a/api/test-current.txt b/api/test-current.txt
index 0d4cffa..a23c8dc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6138,16 +6138,18 @@
method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
- method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+ method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
- method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+ method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+ method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.lang.CharSequence getDeviceOwnerOrganizationName();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
@@ -6196,7 +6198,7 @@
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isBackupServiceEnabled(android.content.ComponentName);
- method public boolean isCallerApplicationRestrictionsManagingPackage();
+ method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceManaged();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
@@ -6225,14 +6227,15 @@
method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
- method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
- method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+ method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+ method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -6280,6 +6283,7 @@
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+ field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6289,6 +6293,8 @@
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+ field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6296,6 +6302,7 @@
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+ field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index aa56be6..f06d19c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1132,6 +1132,23 @@
= "android.app.action.SHOW_DEVICE_MONITORING_DIALOG";
/**
+ * Broadcast Action: Sent after application delegation scopes are changed. The new list of
+ * delegation scopes will be sent in an extra identified by the {@link #EXTRA_DELEGATION_SCOPES}
+ * key.
+ *
+ * <p class=”note”> Note: This is a protected intent that can only be sent by the system.</p>
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED =
+ "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
+
+ /**
+ * A list of Strings corresponding to the delegation scopes given to an app in the
+ * {@link #ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED} broadcast.
+ */
+ public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
+
+ /**
* Flag used by {@link #addCrossProfileIntentFilter} to allow activities in
* the parent profile to access intents sent from the managed profile.
* That is, when an app in the managed profile calls
@@ -1194,6 +1211,19 @@
public static final int PERMISSION_GRANT_STATE_DENIED = 2;
/**
+ * Delegation of certificate installation and management. This scope grants access to the
+ * {@link #getInstalledCaCerts}, {@link #hasCaCertInstalled}, {@link #installCaCert},
+ * {@link #uninstallCaCert}, {@link #uninstallAllUserCaCerts} and {@link #installKeyPair} APIs.
+ */
+ public static final String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+
+ /**
+ * Delegation of application restrictions management. This scope grants access to the
+ * {@link #setApplicationRestrictions} and {@link #getApplicationRestrictions} APIs.
+ */
+ public static final String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+
+ /**
* No management for current user in-effect. This is the default.
* @hide
*/
@@ -3246,6 +3276,10 @@
/**
* Installs the given certificate as a user CA.
*
+ * The caller must be a profile or device owner on that user, or a delegate package given the
+ * {@link #DELEGATION_CERT_INSTALL} scope via {@link #setDelegatedScopes}; otherwise a
+ * security exception will be thrown.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param certBuffer encoded form of the certificate to install.
@@ -3254,12 +3288,14 @@
* interrupted, true otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_CERT_INSTALL
*/
public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
throwIfParentInstance("installCaCert");
if (mService != null) {
try {
- return mService.installCaCert(admin, certBuffer);
+ return mService.installCaCert(admin, mContext.getPackageName(), certBuffer);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3270,18 +3306,24 @@
/**
* Uninstalls the given certificate from trusted user CAs, if present.
*
+ * The caller must be a profile or device owner on that user, or a delegate package given the
+ * {@link #DELEGATION_CERT_INSTALL} scope via {@link #setDelegatedScopes}; otherwise a
+ * security exception will be thrown.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param certBuffer encoded form of the certificate to remove.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_CERT_INSTALL
*/
public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
throwIfParentInstance("uninstallCaCert");
if (mService != null) {
try {
final String alias = getCaCertAlias(certBuffer);
- mService.uninstallCaCerts(admin, new String[] {alias});
+ mService.uninstallCaCerts(admin, mContext.getPackageName(), new String[] {alias});
} catch (CertificateException e) {
Log.w(TAG, "Unable to parse certificate", e);
} catch (RemoteException e) {
@@ -3306,7 +3348,7 @@
throwIfParentInstance("getInstalledCaCerts");
if (mService != null) {
try {
- mService.enforceCanManageCaCerts(admin);
+ mService.enforceCanManageCaCerts(admin, mContext.getPackageName());
final TrustedCertificateStore certStore = new TrustedCertificateStore();
for (String alias : certStore.userAliases()) {
try {
@@ -3335,8 +3377,8 @@
throwIfParentInstance("uninstallAllUserCaCerts");
if (mService != null) {
try {
- mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
- .toArray(new String[0]));
+ mService.uninstallCaCerts(admin, mContext.getPackageName(),
+ new TrustedCertificateStore().userAliases() .toArray(new String[0]));
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3356,7 +3398,7 @@
throwIfParentInstance("hasCaCertInstalled");
if (mService != null) {
try {
- mService.enforceCanManageCaCerts(admin);
+ mService.enforceCanManageCaCerts(admin, mContext.getPackageName());
return getCaCertAlias(certBuffer) != null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -3388,6 +3430,8 @@
* @return {@code true} if the keys were installed, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_CERT_INSTALL
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate cert, @NonNull String alias) {
@@ -3419,6 +3463,8 @@
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
* @see android.security.KeyChain#getCertificateChain
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_CERT_INSTALL
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
@@ -3431,8 +3477,8 @@
}
final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
.getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
- return mService.installKeyPair(admin, pkcs8Key, pemCert, pemChain, alias,
- requestAccess);
+ return mService.installKeyPair(admin, mContext.getPackageName(), pkcs8Key, pemCert,
+ pemChain, alias, requestAccess);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
@@ -3453,11 +3499,13 @@
* @return {@code true} if the private key alias no longer exists, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_CERT_INSTALL
*/
public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
throwIfParentInstance("removeKeyPair");
try {
- return mService.removeKeyPair(admin, alias);
+ return mService.removeKeyPair(admin, mContext.getPackageName(), alias);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3493,7 +3541,11 @@
* @param installerPackage The package name of the certificate installer which will be given
* access. If {@code null} is given the current package will be cleared.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #setDelegatedScopes}
+ * with the {@link #DELEGATION_CERT_INSTALL} scope instead.
*/
+ @Deprecated
public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
installerPackage) throws SecurityException {
throwIfParentInstance("setCertInstallerPackage");
@@ -3507,14 +3559,19 @@
}
/**
- * Called by a profile owner or device owner to retrieve the certificate installer for the user.
- * null if none is set.
+ * Called by a profile owner or device owner to retrieve the certificate installer for the user,
+ * or {@code null} if none is set. If there are multiple delegates this function will return one
+ * of them.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return The package name of the current delegated certificate installer, or {@code null} if
* none is set.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatePackages}
+ * with the {@link #DELEGATION_CERT_INSTALL} scope instead.
*/
+ @Deprecated
public @Nullable String getCertInstallerPackage(@NonNull ComponentName admin)
throws SecurityException {
throwIfParentInstance("getCertInstallerPackage");
@@ -3529,6 +3586,83 @@
}
/**
+ * Called by a profile owner or device owner to grant access to privileged APIs to another app.
+ * Granted APIs are determined by {@code scopes}, which is a list of the {@code DELEGATION_*}
+ * constants.
+ * <p>
+ * Delegated scopes are a per-user state. The delegated access is persistent until it is later
+ * cleared by calling this method with an empty {@code scopes} list or uninstalling the
+ * {@code delegatePackage}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param delegatePackage The package name of the app which will be given access.
+ * @param scopes The groups of privileged APIs whose access should be granted to
+ * {@code delegatedPackage}.
+ * @throws SecurityException if {@code admin} is not a device or a profile owner.
+ */
+ public void setDelegatedScopes(@NonNull ComponentName admin, @NonNull String delegatePackage,
+ @NonNull List<String> scopes) {
+ throwIfParentInstance("setDelegatedScopes");
+ if (mService != null) {
+ try {
+ mService.setDelegatedScopes(admin, delegatePackage, scopes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner or device owner to retrieve a list of the scopes given to a
+ * delegate package. Other apps can use this method to retrieve their own delegated scopes by
+ * passing {@code null} for {@code admin} and their own package name as
+ * {@code delegatedPackage}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if the caller is {@code delegatedPackage}.
+ * @param delegatedPackage The package name of the app whose scopes should be retrieved.
+ * @return A list containing the scopes given to {@code delegatedPackage}.
+ * @throws SecurityException if {@code admin} is not a device or a profile owner.
+ */
+ @NonNull
+ public List<String> getDelegatedScopes(@NonNull ComponentName admin,
+ @NonNull String delegatedPackage) {
+ throwIfParentInstance("getDelegatedScopes");
+ if (mService != null) {
+ try {
+ return mService.getDelegatedScopes(admin, delegatedPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called by a profile owner or device owner to retrieve a list of delegate packages that were
+ * granted a delegation scope.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param delegationScope The scope whose delegates should be retrieved.
+ * @return A list of package names of the current delegated packages for
+ {@code delegationScope}.
+ * @throws SecurityException if {@code admin} is not a device or a profile owner.
+ */
+ @Nullable
+ public List<String> getDelegatePackages(@NonNull ComponentName admin,
+ @NonNull String delegationScope) {
+ throwIfParentInstance("getDelegatePackages");
+ if (mService != null) {
+ try {
+ return mService.getDelegatePackages(admin, delegationScope);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by a device or profile owner to configure an always-on VPN connection through a
* specific application for the current user.
*
@@ -4686,7 +4820,11 @@
* APIs. If {@code null} is given the current package will be cleared.
* @throws SecurityException if {@code admin} is not a device or profile owner.
* @throws NameNotFoundException if {@code packageName} is not found
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #setDelegatedScopes}
+ * with the {@link #DELEGATION_APP_RESTRICTIONS} scope instead.
*/
+ @Deprecated
public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
@Nullable String packageName) throws NameNotFoundException {
throwIfParentInstance("setApplicationRestrictionsManagingPackage");
@@ -4703,14 +4841,20 @@
/**
* Called by a profile owner or device owner to retrieve the application restrictions managing
- * package for the current user, or {@code null} if none is set.
+ * package for the current user, or {@code null} if none is set. If there are multiple
+ * delegates this function will return one of them.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return The package name allowed to manage application restrictions on the current user, or
* {@code null} if none is set.
* @throws SecurityException if {@code admin} is not a device or profile owner.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatePackages}
+ * with the {@link #DELEGATION_APP_RESTRICTIONS} scope instead.
*/
- public @Nullable String getApplicationRestrictionsManagingPackage(
+ @Deprecated
+ @Nullable
+ public String getApplicationRestrictionsManagingPackage(
@NonNull ComponentName admin) {
throwIfParentInstance("getApplicationRestrictionsManagingPackage");
if (mService != null) {
@@ -4730,12 +4874,17 @@
*
* <p>This is done by comparing the calling Linux uid with the uid of the package specified by
* that method.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatedScopes}
+ * instead.
*/
+ @Deprecated
public boolean isCallerApplicationRestrictionsManagingPackage() {
throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
if (mService != null) {
try {
- return mService.isCallerApplicationRestrictionsManagingPackage();
+ return mService.isCallerApplicationRestrictionsManagingPackage(
+ mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4747,8 +4896,8 @@
* Sets the application restrictions for a given target application running in the calling user.
* <p>
* The caller must be a profile or device owner on that user, or the package allowed to manage
- * application restrictions via {@link #setApplicationRestrictionsManagingPackage}; otherwise a
- * security exception will be thrown.
+ * application restrictions via {@link #setDelegatedScopes} with the
+ * {@link #DELEGATION_APP_RESTRICTIONS} scope; otherwise a security exception will be thrown.
* <p>
* The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
* <ul>
@@ -4775,7 +4924,8 @@
* @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new
* set of active restrictions.
* @throws SecurityException if {@code admin} is not a device or profile owner.
- * @see #setApplicationRestrictionsManagingPackage
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_APP_RESTRICTIONS
* @see UserManager#KEY_RESTRICTIONS_PENDING
*/
@WorkerThread
@@ -4784,7 +4934,8 @@
throwIfParentInstance("setApplicationRestrictions");
if (mService != null) {
try {
- mService.setApplicationRestrictions(admin, packageName, settings);
+ mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
+ settings);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5523,8 +5674,8 @@
* user.
* <p>
* The caller must be a profile or device owner on that user, or the package allowed to manage
- * application restrictions via {@link #setApplicationRestrictionsManagingPackage}; otherwise a
- * security exception will be thrown.
+ * application restrictions via {@link #setDelegatedScopes} with the
+ * {@link #DELEGATION_APP_RESTRICTIONS} scope; otherwise a security exception will be thrown.
*
* <p>NOTE: The method performs disk I/O and shouldn't be called on the main thread
*
@@ -5535,7 +5686,8 @@
* {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty
* {@link Bundle} if no restrictions have been set.
* @throws SecurityException if {@code admin} is not a device or profile owner.
- * @see #setApplicationRestrictionsManagingPackage
+ * @see #setDelegatedScopes
+ * @see #DELEGATION_APP_RESTRICTIONS
*/
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@@ -5543,7 +5695,8 @@
throwIfParentInstance("getApplicationRestrictions");
if (mService != null) {
try {
- return mService.getApplicationRestrictions(admin, packageName);
+ return mService.getApplicationRestrictions(admin, mContext.getPackageName(),
+ packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 80ef557..983de56 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,17 +153,21 @@
String[] setPackagesSuspended(in ComponentName admin, in String[] packageNames, boolean suspended);
boolean isPackageSuspended(in ComponentName admin, String packageName);
- boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
- void uninstallCaCerts(in ComponentName admin, in String[] aliases);
- void enforceCanManageCaCerts(in ComponentName admin);
+ boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
+ void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
+ void enforceCanManageCaCerts(in ComponentName admin, in String callerPackage);
boolean approveCaCert(in String alias, int userHandle, boolean approval);
boolean isCaCertApproved(in String alias, int userHandle);
- boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer,
- in byte[] certChainBuffer, String alias, boolean requestAccess);
- boolean removeKeyPair(in ComponentName who, String alias);
+ boolean installKeyPair(in ComponentName who, in String callerPackage, in byte[] privKeyBuffer,
+ in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess);
+ boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
+ void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
+ List<String> getDelegatedScopes(in ComponentName who, String delegatePackage);
+ List<String> getDelegatePackages(in ComponentName who, String scope);
+
void setCertInstallerPackage(in ComponentName who, String installerPackage);
String getCertInstallerPackage(in ComponentName who);
@@ -173,11 +177,11 @@
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
- void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
- Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+ void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings);
+ Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName);
boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
String getApplicationRestrictionsManagingPackage(in ComponentName admin);
- boolean isCallerApplicationRestrictionsManagingPackage();
+ boolean isCallerApplicationRestrictionsManagingPackage(in String callerPackage);
void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
ComponentName getRestrictionsProvider(int userHandle);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bfbea5b..d1227eb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -514,6 +514,7 @@
<!-- Added in O -->
<!-- TODO: temporary broadcast used by AutoFillManagerServiceImpl; will be removed -->
<protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" />
+ <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c5e07b2..d4d9483 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -33,6 +33,8 @@
import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
import static android.app.admin.DevicePolicyManager.CODE_USER_SETUP_COMPLETED;
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -259,6 +261,12 @@
private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
= "application-restrictions-manager";
+ // Comprehensive list of delegations.
+ private static final String DELEGATIONS[] = {
+ DELEGATION_CERT_INSTALL,
+ DELEGATION_APP_RESTRICTIONS
+ };
+
/**
* System property whose value is either "true" or "false", indicating whether
* device owner is present.
@@ -468,12 +476,11 @@
ComponentName mRestrictionsProvider;
- String mDelegatedCertInstallerPackage;
+ // Map of delegate package to delegation scopes
+ final ArrayMap<String, List<String>> mDelegationMap = new ArrayMap<>();
boolean doNotAskCredentialsOnBoot = false;
- String mApplicationRestrictionsManagingPackage;
-
Set<String> mAffiliationIds = new ArraySet<>();
long mLastSecurityLogRetrievalTime = -1;
@@ -1397,7 +1404,7 @@
}
private void handlePackagesChanged(String packageName, int userHandle) {
- boolean removed = false;
+ boolean removedAdmin = false;
if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
@@ -1413,32 +1420,36 @@
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userHandle) == null) {
- removed = true;
+ removedAdmin = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
}
}
} catch (RemoteException re) {
- // Shouldn't happen
+ // Shouldn't happen.
}
}
- if (removed) {
+ if (removedAdmin) {
validatePasswordOwnerLocked(policy);
- saveSettingsLocked(policy.mUserHandle);
}
- // Check if delegated cert installer or app restrictions managing packages are removed.
- if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) {
- policy.mDelegatedCertInstallerPackage = null;
- saveSettingsLocked(policy.mUserHandle);
+ boolean removedDelegate = false;
+
+ // Check if a delegate was removed.
+ for (int i = policy.mDelegationMap.size() - 1; i >= 0; i--) {
+ final String delegatePackage = policy.mDelegationMap.keyAt(i);
+ if (isRemovedPackage(packageName, delegatePackage, userHandle)) {
+ policy.mDelegationMap.removeAt(i);
+ removedDelegate = true;
+ }
}
- if (isRemovedPackage(
- packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) {
- policy.mApplicationRestrictionsManagingPackage = null;
+
+ // Persist updates if the removed package was an admin or delegate.
+ if (removedAdmin || removedDelegate) {
saveSettingsLocked(policy.mUserHandle);
}
}
- if (removed) {
+ if (removedAdmin) {
// The removed admin might have disabled camera, so update user restrictions.
pushUserRestrictions(userHandle);
}
@@ -2372,13 +2383,19 @@
out.attribute(null, ATTR_PERMISSION_POLICY,
Integer.toString(policy.mPermissionPolicy));
}
- if (policy.mDelegatedCertInstallerPackage != null) {
- out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
- policy.mDelegatedCertInstallerPackage);
- }
- if (policy.mApplicationRestrictionsManagingPackage != null) {
- out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER,
- policy.mApplicationRestrictionsManagingPackage);
+
+ // Serialize delegations.
+ for (int i = 0; i < policy.mDelegationMap.size(); ++i) {
+ final String delegatePackage = policy.mDelegationMap.keyAt(i);
+ final List<String> scopes = policy.mDelegationMap.valueAt(i);
+
+ // Every "delegation" tag serializes the information of one delegate-scope pair.
+ for (String scope : scopes) {
+ out.startTag(null, "delegation");
+ out.attribute(null, "delegatePackage", delegatePackage);
+ out.attribute(null, "scope", scope);
+ out.endTag(null, "delegation");
+ }
}
final int N = policy.mAdminList.size();
@@ -2562,10 +2579,36 @@
if (!TextUtils.isEmpty(permissionPolicy)) {
policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
}
- policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
- ATTR_DELEGATED_CERT_INSTALLER);
- policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
- ATTR_APPLICATION_RESTRICTIONS_MANAGER);
+ // Check for delegation compatibility with pre-O.
+ // TODO(edmanp) remove in P.
+ {
+ final String certDelegate = parser.getAttributeValue(null,
+ ATTR_DELEGATED_CERT_INSTALLER);
+ if (certDelegate != null) {
+ List<String> scopes = policy.mDelegationMap.get(certDelegate);
+ if (scopes == null) {
+ scopes = new ArrayList<>();
+ policy.mDelegationMap.put(certDelegate, scopes);
+ }
+ if (!scopes.contains(DELEGATION_CERT_INSTALL)) {
+ scopes.add(DELEGATION_CERT_INSTALL);
+ needsRewrite = true;
+ }
+ }
+ final String appRestrictionsDelegate = parser.getAttributeValue(null,
+ ATTR_APPLICATION_RESTRICTIONS_MANAGER);
+ if (appRestrictionsDelegate != null) {
+ List<String> scopes = policy.mDelegationMap.get(appRestrictionsDelegate);
+ if (scopes == null) {
+ scopes = new ArrayList<>();
+ policy.mDelegationMap.put(appRestrictionsDelegate, scopes);
+ }
+ if (!scopes.contains(DELEGATION_APP_RESTRICTIONS)) {
+ scopes.add(DELEGATION_APP_RESTRICTIONS);
+ needsRewrite = true;
+ }
+ }
+ }
type = parser.next();
int outerDepth = parser.getDepth();
@@ -2600,6 +2643,23 @@
} catch (RuntimeException e) {
Slog.w(LOG_TAG, "Failed loading admin " + name, e);
}
+ } else if ("delegation".equals(tag)) {
+ // Parse delegation info.
+ final String delegatePackage = parser.getAttributeValue(null,
+ "delegatePackage");
+ final String scope = parser.getAttributeValue(null, "scope");
+
+ // Get a reference to the scopes list for the delegatePackage.
+ List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+ // Or make a new list if none was found.
+ if (scopes == null) {
+ scopes = new ArrayList<>();
+ policy.mDelegationMap.put(delegatePackage, scopes);
+ }
+ // Add the new scope to the list of delegatePackage if it's not already there.
+ if (!scopes.contains(scope)) {
+ scopes.add(scope);
+ }
} else if ("failed-password-attempts".equals(tag)) {
policy.mFailedPasswordAttempts = Integer.parseInt(
parser.getAttributeValue(null, "value"));
@@ -4522,9 +4582,9 @@
}
@Override
- public void enforceCanManageCaCerts(ComponentName who) {
+ public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
if (who == null) {
- if (!isCallerDelegatedCertInstaller()) {
+ if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
}
} else {
@@ -4538,35 +4598,6 @@
}
}
- private void enforceCanManageInstalledKeys(ComponentName who) {
- if (who == null) {
- if (!isCallerDelegatedCertInstaller()) {
- throw new SecurityException("who == null, but caller is not cert installer");
- }
- } else {
- enforceProfileOrDeviceOwner(who);
- }
- }
-
- private boolean isCallerDelegatedCertInstaller() {
- final int callingUid = mInjector.binderGetCallingUid();
- final int userHandle = UserHandle.getUserId(callingUid);
- synchronized (this) {
- final DevicePolicyData policy = getUserData(userHandle);
- if (policy.mDelegatedCertInstallerPackage == null) {
- return false;
- }
-
- try {
- int uid = mInjector.getPackageManager().getPackageUidAsUser(
- policy.mDelegatedCertInstallerPackage, userHandle);
- return uid == callingUid;
- } catch (NameNotFoundException e) {
- return false;
- }
- }
- }
-
@Override
public boolean approveCaCert(String alias, int userId, boolean approval) {
enforceManageUsers();
@@ -4608,8 +4639,9 @@
}
@Override
- public boolean installCaCert(ComponentName admin, byte[] certBuffer) throws RemoteException {
- enforceCanManageCaCerts(admin);
+ public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
+ throws RemoteException {
+ enforceCanManageCaCerts(admin, callerPackage);
byte[] pemCert;
try {
@@ -4651,8 +4683,8 @@
}
@Override
- public void uninstallCaCerts(ComponentName admin, String[] aliases) {
- enforceCanManageCaCerts(admin);
+ public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
+ enforceCanManageCaCerts(admin, callerPackage);
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
final long id = mInjector.binderClearCallingIdentity();
@@ -4676,9 +4708,11 @@
}
@Override
- public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain,
- String alias, boolean requestAccess) {
- enforceCanManageInstalledKeys(who);
+ public boolean installKeyPair(ComponentName who, String callerPackage, byte[] privKey,
+ byte[] cert, byte[] chain, String alias, boolean requestAccess) {
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_CERT_INSTALL);
+
final int callingUid = mInjector.binderGetCallingUid();
final long id = mInjector.binderClearCallingIdentity();
@@ -4709,8 +4743,9 @@
}
@Override
- public boolean removeKeyPair(ComponentName who, String alias) {
- enforceCanManageInstalledKeys(who);
+ public boolean removeKeyPair(ComponentName who, String callerPackage, String alias) {
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_CERT_INSTALL);
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
final long id = Binder.clearCallingIdentity();
@@ -4795,33 +4830,267 @@
}.execute();
}
+ /**
+ * Set the scopes of a device owner or profile owner delegate.
+ *
+ * @param who the device owner or profile owner.
+ * @param delegatePackage the name of the delegate package.
+ * @param scopes the list of delegation scopes to be given to the delegate package.
+ */
@Override
- public void setCertInstallerPackage(ComponentName who, String installerPackage)
- throws SecurityException {
- int userHandle = UserHandle.getCallingUserId();
+ public void setDelegatedScopes(ComponentName who, String delegatePackage,
+ List<String> scopes) throws SecurityException {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
+ Preconditions.checkCollectionElementsNotNull(scopes, "Scopes");
+ // Remove possible duplicates.
+ scopes = new ArrayList(new ArraySet(scopes));
+ // Ensure given scopes are valid.
+ if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
+ throw new IllegalArgumentException("Unexpected delegation scopes");
+ }
+
+ // Retrieve the user ID of the calling process.
+ final int userId = mInjector.userHandleGetCallingUserId();
synchronized (this) {
+ // Ensure calling process is device/profile owner.
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
- if (installerPackage != null &&
- !isPackageInstalledForUser(installerPackage, userHandle)) {
- throw new IllegalArgumentException("Package " + installerPackage
+ // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
+ if (scopes.size() == 1 && scopes.get(0).equals(DELEGATION_CERT_INSTALL) ||
+ getTargetSdk(who.getPackageName(), userId) >= Build.VERSION_CODES.N) {
+ // Throw when the delegate package is not installed.
+ if (!isPackageInstalledForUser(delegatePackage, userId)) {
+ throw new IllegalArgumentException("Package " + delegatePackage
+ " is not installed on the current user");
}
}
- DevicePolicyData policy = getUserData(userHandle);
- policy.mDelegatedCertInstallerPackage = installerPackage;
- saveSettingsLocked(userHandle);
+
+ // Set the new delegate in user policies.
+ final DevicePolicyData policy = getUserData(userId);
+ if (!scopes.isEmpty()) {
+ policy.mDelegationMap.put(delegatePackage, new ArrayList<>(scopes));
+ } else {
+ // Remove any delegation info if the given scopes list is empty.
+ policy.mDelegationMap.remove(delegatePackage);
+ }
+
+ // Notify delegate package of updates.
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
+ // Only call receivers registered in the manifest (don’t wake app if not running).
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ // Limit components this intent resolves to to the delegate package.
+ intent.setPackage(delegatePackage);
+ // Include the list of delegated scopes as an extra.
+ intent.putExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES, scopes.toArray());
+ // Send the broadcast.
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+
+ // Persist updates.
+ saveSettingsLocked(userId);
+ }
+ }
+
+ /**
+ * Get the delegation scopes given to a delegate package by a device owner or profile owner.
+ *
+ * A DO/PO can get the scopes of any package. A non DO/PO package can get its own scopes by
+ * passing in {@code null} as the {@code who} parameter and its own name as the
+ * {@code delegatepackage}.
+ *
+ * @param who the device owner or profile owner, or {@code null} if the caller is
+ * {@code delegatePackage}.
+ * @param delegatePackage the name of the delegate package whose scopes are to be retrieved.
+ * @return a list of the delegation scopes currently given to {@code delegatePackage}.
+ */
+ @Override
+ @NonNull
+ public List<String> getDelegatedScopes(ComponentName who,
+ String delegatePackage) throws SecurityException {
+ Preconditions.checkNotNull(delegatePackage, "Delegate package is null");
+
+ // Retrieve the user ID of the calling process.
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ synchronized (this) {
+ // Ensure calling process is device/profile owner.
+ if (who != null) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ // Or ensure calling process is delegatePackage itself.
+ } else {
+ int uid = 0;
+ try {
+ uid = mInjector.getPackageManager()
+ .getPackageUidAsUser(delegatePackage, userId);
+ } catch(NameNotFoundException e) {
+ }
+ if (uid != callingUid) {
+ throw new SecurityException("Caller with uid " + callingUid + " is not "
+ + delegatePackage);
+ }
+ }
+ final DevicePolicyData policy = getUserData(userId);
+ // Retrieve the scopes assigned to delegatePackage, or null if no scope was given.
+ final List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+ return scopes == null ? Collections.EMPTY_LIST : scopes;
+ }
+ }
+
+ /**
+ * Get a list of packages that were given a specific delegation scopes by a device owner or
+ * profile owner.
+ *
+ * @param who the device owner or profile owner.
+ * @param scope the scope whose delegates are to be retrieved.
+ * @return a list of the delegate packages currently given the {@code scope} delegation.
+ */
+ @NonNull
+ public List<String> getDelegatePackages(ComponentName who, String scope)
+ throws SecurityException {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(scope, "Scope is null");
+ if (!Arrays.asList(DELEGATIONS).contains(scope)) {
+ throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
+ }
+
+ // Retrieve the user ID of the calling process.
+ final int userId = mInjector.userHandleGetCallingUserId();
+ synchronized (this) {
+ // Ensure calling process is device/profile owner.
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final DevicePolicyData policy = getUserData(userId);
+
+ // Create a list to hold the resulting delegate packages.
+ final List<String> delegatePackagesWithScope = new ArrayList<>();
+ // Add all delegations containing scope to the result list.
+ for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+ if (policy.mDelegationMap.valueAt(i).contains(scope)) {
+ delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
+ }
+ }
+ return delegatePackagesWithScope;
+ }
+ }
+
+ /**
+ * Check whether a caller application has been delegated a given scope via
+ * {@link #setDelegatedScopes} to access privileged APIs on the behalf of a profile owner or
+ * device owner.
+ * <p>
+ * This is done by checking that {@code callerPackage} was granted {@code scope} delegation and
+ * then comparing the calling UID with the UID of {@code callerPackage} as reported by
+ * {@link PackageManager#getPackageUidAsUser}.
+ *
+ * @param callerPackage the name of the package that is trying to invoke a function in the DPMS.
+ * @param scope the delegation scope to be checked.
+ * @return {@code true} if the calling process is a delegate of {@code scope}.
+ */
+ private boolean isCallerDelegate(String callerPackage, String scope) {
+ Preconditions.checkNotNull(callerPackage, "callerPackage is null");
+ if (!Arrays.asList(DELEGATIONS).contains(scope)) {
+ throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
+ }
+
+ // Retrieve the UID and user ID of the calling process.
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ synchronized (this) {
+ // Retrieve user policy data.
+ final DevicePolicyData policy = getUserData(userId);
+ // Retrieve the list of delegation scopes granted to callerPackage.
+ final List<String> scopes = policy.mDelegationMap.get(callerPackage);
+ // Check callingUid only if callerPackage has the required scope delegation.
+ if (scopes != null && scopes.contains(scope)) {
+ try {
+ // Retrieve the expected UID for callerPackage.
+ final int uid = mInjector.getPackageManager()
+ .getPackageUidAsUser(callerPackage, userId);
+ // Return true if the caller is actually callerPackage.
+ return uid == callingUid;
+ } catch (NameNotFoundException e) {
+ // Ignore.
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Throw a security exception if a ComponentName is given and it is not a device/profile owner
+ * or if the calling process is not a delegate of the given scope.
+ *
+ * @param who the device owner of profile owner, or null if {@code callerPackage} is a
+ * {@code scope} delegate.
+ * @param callerPackage the name of the calling package. Required if {@code who} is
+ * {@code null}.
+ * @param reqPolicy the policy used in the API whose access permission is being checked.
+ * @param scoppe the delegation scope corresponding to the API being checked.
+ * @throws SecurityException if {@code who} is given and is not an owner for {@code reqPolicy};
+ * or when {@code who} is {@code null} and {@code callerPackage} is not a delegate
+ * of {@code scope}.
+ */
+ private void enforceCanManageScope(ComponentName who, String callerPackage, int reqPolicy,
+ String scope) {
+ // If a ComponentName is given ensure it is a device or profile owner according to policy.
+ if (who != null) {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, reqPolicy);
+ }
+ // If no ComponentName is given ensure calling process has scope delegation.
+ } else if (!isCallerDelegate(callerPackage, scope)) {
+ throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
+ + " is not a delegate of scope " + scope + ".");
+ }
+ }
+
+ /**
+ * Helper function to preserve delegation behavior pre-O when using the deprecated functions
+ * {@code #setCertInstallerPackage} and {@code #setApplicationRestrictionsManagingPackage}.
+ */
+ private void setDelegatedScopePreO(ComponentName who,
+ String delegatePackage, String scope) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+ synchronized(this) {
+ // Ensure calling process is device/profile owner.
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final DevicePolicyData policy = getUserData(userId);
+
+ if (delegatePackage != null) {
+ // Set package as a delegate for scope if it is not already one.
+ List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+ if (scopes == null) {
+ scopes = new ArrayList<>();
+ }
+ if (!scopes.contains(scope)) {
+ scopes.add(scope);
+ setDelegatedScopes(who, delegatePackage, scopes);
+ }
+ }
+
+ // Clear any existing scope delegates.
+ for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+ final String currentPackage = policy.mDelegationMap.keyAt(i);
+ final List<String> currentScopes = policy.mDelegationMap.valueAt(i);
+
+ if (!currentPackage.equals(delegatePackage) && currentScopes.remove(scope)) {
+ setDelegatedScopes(who, currentPackage, currentScopes);
+ }
+ }
}
}
@Override
+ public void setCertInstallerPackage(ComponentName who, String installerPackage)
+ throws SecurityException {
+ setDelegatedScopePreO(who, installerPackage, DELEGATION_CERT_INSTALL);
+ }
+
+ @Override
public String getCertInstallerPackage(ComponentName who) throws SecurityException {
- int userHandle = UserHandle.getCallingUserId();
- synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- DevicePolicyData policy = getUserData(userHandle);
- return policy.mDelegatedCertInstallerPackage;
- }
+ final List<String> delegatePackages = getDelegatePackages(who, DELEGATION_CERT_INSTALL);
+ return delegatePackages.size() > 0 ? delegatePackages.get(0) : null;
}
/**
@@ -6396,11 +6665,11 @@
}
private void clearUserPoliciesLocked(int userId) {
- // Reset some of the user-specific policies
- DevicePolicyData policy = getUserData(userId);
+ // Reset some of the user-specific policies.
+ final DevicePolicyData policy = getUserData(userId);
policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
- policy.mDelegatedCertInstallerPackage = null;
- policy.mApplicationRestrictionsManagingPackage = null;
+ // Clear delegations.
+ policy.mDelegationMap.clear();
policy.mStatusBarDisabled = false;
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
saveSettingsLocked(userId);
@@ -6984,66 +7253,31 @@
@Override
public boolean setApplicationRestrictionsManagingPackage(ComponentName admin,
String packageName) {
- Preconditions.checkNotNull(admin, "ComponentName is null");
-
- final int userHandle = mInjector.userHandleGetCallingUserId();
- synchronized (this) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (packageName != null && !isPackageInstalledForUser(packageName, userHandle)) {
- return false;
- }
- DevicePolicyData policy = getUserData(userHandle);
- policy.mApplicationRestrictionsManagingPackage = packageName;
- saveSettingsLocked(userHandle);
- return true;
+ try {
+ setDelegatedScopePreO(admin, packageName, DELEGATION_APP_RESTRICTIONS);
+ } catch (IllegalArgumentException e) {
+ return false;
}
+ return true;
}
@Override
public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
- Preconditions.checkNotNull(admin, "ComponentName is null");
-
- final int userHandle = mInjector.userHandleGetCallingUserId();
- synchronized (this) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- DevicePolicyData policy = getUserData(userHandle);
- return policy.mApplicationRestrictionsManagingPackage;
- }
+ final List<String> delegatePackages = getDelegatePackages(admin,
+ DELEGATION_APP_RESTRICTIONS);
+ return delegatePackages.size() > 0 ? delegatePackages.get(0) : null;
}
@Override
- public boolean isCallerApplicationRestrictionsManagingPackage() {
- final int callingUid = mInjector.binderGetCallingUid();
- final int userHandle = UserHandle.getUserId(callingUid);
- synchronized (this) {
- final DevicePolicyData policy = getUserData(userHandle);
- if (policy.mApplicationRestrictionsManagingPackage == null) {
- return false;
- }
-
- try {
- int uid = mInjector.getPackageManager().getPackageUidAsUser(
- policy.mApplicationRestrictionsManagingPackage, userHandle);
- return uid == callingUid;
- } catch (NameNotFoundException e) {
- return false;
- }
- }
- }
-
- private void enforceCanManageApplicationRestrictions(ComponentName who) {
- if (who != null) {
- enforceProfileOrDeviceOwner(who);
- } else if (!isCallerApplicationRestrictionsManagingPackage()) {
- throw new SecurityException(
- "No admin component given, and caller cannot manage application restrictions "
- + "for other apps.");
- }
+ public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
+ return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
}
@Override
- public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
- enforceCanManageApplicationRestrictions(who);
+ public void setApplicationRestrictions(ComponentName who, String callerPackage,
+ String packageName, Bundle settings) {
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_APP_RESTRICTIONS);
final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
@@ -7733,8 +7967,10 @@
}
@Override
- public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
- enforceCanManageApplicationRestrictions(who);
+ public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
+ String packageName) {
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_APP_RESTRICTIONS);
final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8fad3eff..3ceab6f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -68,6 +68,9 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
@@ -1128,6 +1131,7 @@
public void testSetGetApplicationRestriction() {
setAsProfileOwner(admin1);
+ mContext.packageName = admin1.getPackageName();
{
Bundle rest = new Bundle();
@@ -1159,29 +1163,131 @@
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
}
+ /**
+ * Setup a package in the package manager mock. Useful for faking installed applications.
+ *
+ * @param packageName the name of the package to be setup
+ * @param appId the application ID to be given to the package
+ * @return the UID of the package as known by the mock package manager
+ */
+ private int setupPackageInPackageManager(final String packageName, final int appId)
+ throws Exception {
+ // Make the PackageManager return the package instead of throwing a NameNotFoundException
+ final PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(packageName),
+ anyInt(),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ // Setup application UID with the PackageManager
+ final int uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, appId);
+ doReturn(uid).when(mContext.packageManager).getPackageUidAsUser(
+ eq(packageName),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ // Associate packageName to uid
+ doReturn(packageName).when(mContext.ipackageManager).getNameForUid(eq(uid));
+ doReturn(new String[]{packageName})
+ .when(mContext.ipackageManager).getPackagesForUid(eq(uid));
+ return uid;
+ }
+
+ /**
+ * Simple test for delegate set/get and general delegation. Tests verifying that delegated
+ * privileges can acually be exercised by a delegate are not covered here.
+ */
+ public void testDelegation() throws Exception {
+ setAsProfileOwner(admin1);
+
+ final int userHandle = DpmMockContext.CALLER_USER_HANDLE;
+
+ // Given two packages
+ final String CERT_DELEGATE = "com.delegate.certs";
+ final String RESTRICTIONS_DELEGATE = "com.delegate.apprestrictions";
+ final int CERT_DELEGATE_UID = setupPackageInPackageManager(CERT_DELEGATE, 20988);
+ final int RESTRICTIONS_DELEGATE_UID = setupPackageInPackageManager(RESTRICTIONS_DELEGATE,
+ 20989);
+
+ // On delegation
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
+ dpm.setCertInstallerPackage(admin1, CERT_DELEGATE);
+ dpm.setApplicationRestrictionsManagingPackage(admin1, RESTRICTIONS_DELEGATE);
+
+ // DPMS correctly stores and retrieves the delegates
+ DevicePolicyManagerService.DevicePolicyData policy = dpms.mUserData.get(userHandle);
+ assertEquals(2, policy.mDelegationMap.size());
+ MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(CERT_DELEGATE),
+ DELEGATION_CERT_INSTALL);
+ MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, CERT_DELEGATE),
+ DELEGATION_CERT_INSTALL);
+ assertEquals(CERT_DELEGATE, dpm.getCertInstallerPackage(admin1));
+ MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(RESTRICTIONS_DELEGATE),
+ DELEGATION_APP_RESTRICTIONS);
+ MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, RESTRICTIONS_DELEGATE),
+ DELEGATION_APP_RESTRICTIONS);
+ assertEquals(RESTRICTIONS_DELEGATE, dpm.getApplicationRestrictionsManagingPackage(admin1));
+
+ // On calling install certificate APIs from an unauthorized process
+ mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
+ mContext.packageName = RESTRICTIONS_DELEGATE;
+
+ // DPMS throws a SecurityException
+ try {
+ dpm.installCaCert(null, null);
+ fail("Didn't throw SecurityException on unauthorized access");
+ } catch (SecurityException expected) {
+ }
+
+ // On calling install certificate APIs from an authorized process
+ mContext.binder.callingUid = CERT_DELEGATE_UID;
+ mContext.packageName = CERT_DELEGATE;
+
+ // DPMS executes without a SecurityException
+ try {
+ dpm.installCaCert(null, null);
+ } catch (SecurityException unexpected) {
+ fail("Threw SecurityException on authorized access");
+ } catch (NullPointerException expected) {
+ }
+
+ // On removing a delegate
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
+ dpm.setCertInstallerPackage(admin1, null);
+
+ // DPMS does not allow access to ex-delegate
+ mContext.binder.callingUid = CERT_DELEGATE_UID;
+ mContext.packageName = CERT_DELEGATE;
+ try {
+ dpm.installCaCert(null, null);
+ fail("Didn't throw SecurityException on unauthorized access");
+ } catch (SecurityException expected) {
+ }
+
+ // But still allows access to other existing delegates
+ mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
+ mContext.packageName = RESTRICTIONS_DELEGATE;
+ try {
+ dpm.getApplicationRestrictions(null, "pkg");
+ } catch (SecurityException expected) {
+ fail("Threw SecurityException on authorized access");
+ }
+ }
+
public void testApplicationRestrictionsManagingApp() throws Exception {
setAsProfileOwner(admin1);
final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
final int appRestrictionsManagerAppId = 20987;
- final int appRestrictionsManagerUid = UserHandle.getUid(
- DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
- doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUidAsUser(
- eq(appRestrictionsManagerPackage),
- eq(DpmMockContext.CALLER_USER_HANDLE));
- mContext.binder.callingUid = appRestrictionsManagerUid;
-
- final PackageInfo pi = new PackageInfo();
- pi.applicationInfo = new ApplicationInfo();
- pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
- doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
- eq(appRestrictionsManagerPackage),
- anyInt(),
- eq(DpmMockContext.CALLER_USER_HANDLE));
+ final int appRestrictionsManagerUid = setupPackageInPackageManager(
+ appRestrictionsManagerPackage, appRestrictionsManagerAppId);
// appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
// delegated that permission yet.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
Bundle rest = new Bundle();
rest.putString("KEY_STRING", "Foo1");
@@ -1190,18 +1296,21 @@
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
- "caller cannot manage application restrictions", expected.getMessage());
+ "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+ expected.getMessage());
}
try {
dpm.getApplicationRestrictions(null, "pkg1");
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
- "caller cannot manage application restrictions", expected.getMessage());
+ "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+ expected.getMessage());
}
// Check via the profile owner that no restrictions were set.
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
// Check the API does not allow setting a non-existent package
@@ -1221,6 +1330,7 @@
// Now that package should be able to set and retrieve app restrictions.
mContext.binder.callingUid = appRestrictionsManagerUid;
+ mContext.packageName = appRestrictionsManagerPackage;
assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
dpm.setApplicationRestrictions(null, "pkg1", rest);
Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
@@ -1236,12 +1346,14 @@
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
- "caller cannot manage application restrictions", expected.getMessage());
+ "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+ expected.getMessage());
}
// The DPM is still able to manage app restrictions, even if it allowed another app to do it
// too.
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
dpm.setApplicationRestrictions(admin1, "pkg1", null);
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
@@ -1250,13 +1362,15 @@
dpm.setApplicationRestrictionsManagingPackage(admin1, null);
assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
mContext.binder.callingUid = appRestrictionsManagerUid;
+ mContext.packageName = appRestrictionsManagerPackage;
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
try {
dpm.setApplicationRestrictions(null, "pkg1", null);
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
- "caller cannot manage application restrictions", expected.getMessage());
+ "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+ expected.getMessage());
}
}