From bf60f728cc7ed326fb8978afd9f589a685bb87b7 Mon Sep 17 00:00:00 2001 From: Esteban Talavera Date: Thu, 10 Dec 2015 16:26:44 +0000 Subject: [PATCH] Device or profile owner can let another app manage app restrictions The device or profile owner can allow another package to set app restrictions for any app in that user Similar to the way it can give permission to access CA certificate related APIs from M. Bug: 22541936 Change-Id: I0c1b0804ad300dfa4fbdc1c7721c5d8653d77861 --- api/current.txt | 3 + api/system-current.txt | 3 + api/test-current.txt | 3 + .../app/admin/DevicePolicyManager.java | 98 +++++++++++-- .../app/admin/IDevicePolicyManager.aidl | 3 + .../DevicePolicyManagerService.java | 137 +++++++++++++----- .../devicepolicy/DevicePolicyManagerTest.java | 82 +++++++++++ 7 files changed, 277 insertions(+), 52 deletions(-) diff --git a/api/current.txt b/api/current.txt index ec38436deb3df..d7493e9c0aeac 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5740,6 +5740,7 @@ package android.app.admin { method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); + method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName); method public boolean getAutoTimeRequired(); method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName); method public boolean getCameraDisabled(android.content.ComponentName); @@ -5783,6 +5784,7 @@ package android.app.admin { method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String); + method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); method public boolean isMasterVolumeMuted(android.content.ComponentName); @@ -5798,6 +5800,7 @@ package android.app.admin { method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); 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); method public void setAutoTimeRequired(android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean); method public void setCameraDisabled(android.content.ComponentName, boolean); diff --git a/api/system-current.txt b/api/system-current.txt index d89e2ca5aaf56..f6e427aeb6cfd 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5865,6 +5865,7 @@ package android.app.admin { method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); + method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName); method public boolean getAutoTimeRequired(); method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName); method public boolean getCameraDisabled(android.content.ComponentName); @@ -5915,6 +5916,7 @@ package android.app.admin { method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String); + method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); method public boolean isMasterVolumeMuted(android.content.ComponentName); @@ -5932,6 +5934,7 @@ package android.app.admin { method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException; 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); method public void setAutoTimeRequired(android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean); method public void setCameraDisabled(android.content.ComponentName, boolean); diff --git a/api/test-current.txt b/api/test-current.txt index 7cc7cb6bb370b..25bd3ad4f53f2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5740,6 +5740,7 @@ package android.app.admin { method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); + method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName); method public boolean getAutoTimeRequired(); method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName); method public boolean getCameraDisabled(android.content.ComponentName); @@ -5783,6 +5784,7 @@ package android.app.admin { method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String); + method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); method public boolean isMasterVolumeMuted(android.content.ComponentName); @@ -5798,6 +5800,7 @@ package android.app.admin { method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); 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); method public void setAutoTimeRequired(android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean); method public void setCameraDisabled(android.content.ComponentName, boolean); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 660ce3bd0a445..d9cd3ccaa7934 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3312,8 +3312,69 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to set the application restrictions for a given target - * application running in the profile. + * Called by a profile owner or device owner to grant permission to a package to manage + * application restrictions for the calling user via {@link #setApplicationRestrictions} and + * {@link #getApplicationRestrictions}. + *

+ * This permission is persistent until it is later cleared by calling this method with a + * {@code null} value or uninstalling the managing package. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName The package name which will be given access to application restrictions + * APIs. If {@code null} is given the current package will be cleared. + */ + public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin, + @Nullable String packageName) { + if (mService != null) { + try { + mService.setApplicationRestrictionsManagingPackage(admin, packageName); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + } + + /** + * 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. + * + * @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. + */ + public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) { + if (mService != null) { + try { + return mService.getApplicationRestrictionsManagingPackage(admin); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return null; + } + + /** + * Returns {@code true} if the calling package has been granted permission via + * {@link #setApplicationRestrictionsManagingPackage} to manage application + * restrictions for the calling user. + */ + public boolean isCallerApplicationRestrictionsManagingPackage() { + if (mService != null) { + try { + return mService.isCallerApplicationRestrictionsManagingPackage(); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + /** + * Sets the application restrictions for a given target application running in the calling user. + * + *

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. * *

The provided {@link Bundle} consists of key-value pairs, where the types of values may be: *

    @@ -3323,24 +3384,25 @@ public class DevicePolicyManager { *
  • From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]} *
* - *

The application restrictions are only made visible to the target application and the - * profile or device owner. - * *

If the restrictions are not available yet, but may be applied in the near future, - * the admin can notify the target application of that by adding + * the caller can notify the target application of that by adding * {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter. * - *

The calling device admin must be a profile or device owner; if it is not, a security - * exception will be thrown. + *

The application restrictions are only made visible to the target application via + * {@link UserManager#getApplicationRestrictions(String)}, in addition to the profile or + * device owner, and the application restrictions managing package via + * {@link #getApplicationRestrictions}. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or + * {@code null} if called by the application restrictions managing package. * @param packageName The name of the package to update restricted settings for. * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new * set of active restrictions. * + * @see #setApplicationRestrictionsManagingPackage * @see UserManager#KEY_RESTRICTIONS_PENDING */ - public void setApplicationRestrictions(@NonNull ComponentName admin, String packageName, + public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName, Bundle settings) { if (mService != null) { try { @@ -3896,19 +3958,23 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to get the application restrictions for a given target - * application running in the profile. + * Retrieves the application restrictions for a given target application running in the calling + * user. * - *

The calling device admin must be a profile or device owner; if it is not, a security - * exception will be thrown. + *

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. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or + * {@code null} if called by the application restrictions managing package. * @param packageName The name of the package to fetch restricted settings of. * @return {@link Bundle} of settings corresponding to what was set last time * {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty {@link Bundle} * if no restrictions have been set. + * + * @see {@link #setApplicationRestrictionsManagingPackage} */ - public Bundle getApplicationRestrictions(@NonNull ComponentName admin, String packageName) { + public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) { if (mService != null) { try { return mService.getApplicationRestrictions(admin, packageName); diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d8cc2eaf280d3..ac5a3030ad218 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -149,6 +149,9 @@ interface IDevicePolicyManager { void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings); Bundle getApplicationRestrictions(in ComponentName who, in String packageName); + void setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName); + String getApplicationRestrictionsManagingPackage(in ComponentName admin); + boolean isCallerApplicationRestrictionsManagingPackage(); void setRestrictionsProvider(in ComponentName who, in ComponentName provider); ComponentName getRestrictionsProvider(int userHandle); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 810ee6bcd5989..c9810fc4496dd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -193,6 +193,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String ATTR_PERMISSION_POLICY = "permission-policy"; private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer"; + private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER + = "application-restrictions-manager"; private static final int STATUS_BAR_DISABLE_MASK = StatusBarManager.DISABLE_EXPAND | @@ -322,6 +324,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean doNotAskCredentialsOnBoot = false; + String mApplicationRestrictionsManagingPackage; + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -1035,19 +1039,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { saveSettingsLocked(policy.mUserHandle); } - if (policy.mDelegatedCertInstallerPackage != null && - (packageName == null - || packageName.equals(policy.mDelegatedCertInstallerPackage))) { - try { - // Check if delegated cert installer package is removed. - if (mIPackageManager.getPackageInfo( - policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) { - policy.mDelegatedCertInstallerPackage = null; - saveSettingsLocked(policy.mUserHandle); - } - } catch (RemoteException e) { - // Shouldn't happen - } + // Check if delegated cert installer or app restrictions managing packages are removed. + if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) { + policy.mDelegatedCertInstallerPackage = null; + saveSettingsLocked(policy.mUserHandle); + } + if (isRemovedPackage( + packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) { + policy.mApplicationRestrictionsManagingPackage = null; + saveSettingsLocked(policy.mUserHandle); } } if (removed) { @@ -1056,6 +1056,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) { + try { + return targetPackage != null + && (changedPackage == null || changedPackage.equals(targetPackage)) + && mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null; + } catch (RemoteException e) { + // Shouldn't happen + } + + return false; + } + /** * Unit test will subclass it to inject mocks. */ @@ -1795,6 +1807,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER, policy.mDelegatedCertInstallerPackage); } + if (policy.mApplicationRestrictionsManagingPackage != null) { + out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER, + policy.mApplicationRestrictionsManagingPackage); + } final int N = policy.mAdminList.size(); for (int i=0; i