Merge "Device or profile owner can let another app manage app restrictions"

This commit is contained in:
Esteban Talavera
2015-12-17 10:59:16 +00:00
committed by Android (Google) Code Review
7 changed files with 277 additions and 52 deletions

View File

@@ -5740,6 +5740,7 @@ package android.app.admin {
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> 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);
@@ -5799,6 +5801,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);

View File

@@ -5865,6 +5865,7 @@ package android.app.admin {
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> 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);
@@ -5933,6 +5935,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);

View File

@@ -5740,6 +5740,7 @@ package android.app.admin {
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> 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);
@@ -5799,6 +5801,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);

View File

@@ -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}.
* <p>
* 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.
*
* <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.
*
* <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
* <ul>
@@ -3323,24 +3384,25 @@ public class DevicePolicyManager {
* <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
* </ul>
*
* <p>The application restrictions are only made visible to the target application and the
* profile or device owner.
*
* <p>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.
*
* <p>The calling device admin must be a profile or device owner; if it is not, a security
* exception will be thrown.
* <p>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.
*
* <p>The calling device admin must be a profile or device owner; if it is not, a security
* exception will be thrown.
* <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.
*
* @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);

View File

@@ -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);

View File

@@ -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.
*/
@@ -1799,6 +1811,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<N; i++) {
@@ -1924,6 +1940,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
ATTR_DELEGATED_CERT_INSTALLER);
policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
ATTR_APPLICATION_RESTRICTIONS_MANAGER);
type = parser.next();
int outerDepth = parser.getDepth();
@@ -4819,6 +4837,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DevicePolicyData policy = getUserData(userId);
policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
policy.mDelegatedCertInstallerPackage = null;
policy.mApplicationRestrictionsManagingPackage = null;
policy.mStatusBarDisabled = false;
saveSettingsLocked(userId);
@@ -5191,18 +5210,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
Preconditions.checkNotNull(who, "ComponentName is null");
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
public void setApplicationRestrictionsManagingPackage(ComponentName admin, String packageName) {
final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
DevicePolicyData policy = getUserData(userHandle);
policy.mApplicationRestrictionsManagingPackage = packageName;
saveSettingsLocked(userHandle);
}
}
long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
} finally {
mInjector.binderRestoreCallingIdentity(id);
@Override
public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
DevicePolicyData policy = getUserData(userHandle);
return policy.mApplicationRestrictionsManagingPackage;
}
}
@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 = mContext.getPackageManager().getPackageUid(
policy.mApplicationRestrictionsManagingPackage, userHandle);
return uid == callingUid;
} catch (NameNotFoundException e) {
return false;
}
}
}
private void enforceCanManageApplicationRestrictions(ComponentName who) {
if (who != null) {
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
} else if (!isCallerApplicationRestrictionsManagingPackage()) {
throw new SecurityException(
"No admin component given, and caller cannot manage application restrictions "
+ "for other apps.");
}
}
@Override
public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
enforceCanManageApplicationRestrictions(who);
final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5768,21 +5837,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
Preconditions.checkNotNull(who, "ComponentName is null");
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
enforceCanManageApplicationRestrictions(who);
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
long id = mInjector.binderClearCallingIdentity();
try {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
// if no restrictions were saved, mUserManager.getApplicationRestrictions
// returns null, but DPM method should return an empty Bundle as per JavaDoc
return bundle != null ? bundle : Bundle.EMPTY;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final long id = mInjector.binderClearCallingIdentity();
try {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
// if no restrictions were saved, mUserManager.getApplicationRestrictions
// returns null, but DPM method should return an empty Bundle as per JavaDoc
return bundle != null ? bundle : Bundle.EMPTY;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
}

View File

@@ -944,6 +944,88 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
}
public void testApplicationRestrictionsManagingApp() throws Exception {
setAsProfileOwner(admin1);
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).getPackageUid(
eq(appRestrictionsManagerPackage),
eq(DpmMockContext.CALLER_USER_HANDLE));
mContext.binder.callingUid = appRestrictionsManagerUid;
// appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
// delegated that permission yet.
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
Bundle rest = new Bundle();
rest.putString("KEY_STRING", "Foo1");
try {
dpm.setApplicationRestrictions(null, "pkg1", rest);
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
"caller cannot manage application 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());
}
// Check via the profile owner that no restrictions were set.
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
// Let appRestrictionsManagerPackage manage app restrictions
dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
assertEquals(appRestrictionsManagerPackage,
dpm.getApplicationRestrictionsManagingPackage(admin1));
// Now that package should be able to set and retrieve app restrictions.
mContext.binder.callingUid = appRestrictionsManagerUid;
assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
dpm.setApplicationRestrictions(null, "pkg1", rest);
Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
assertEquals(1, returned.size(), 1);
assertEquals("Foo1", returned.get("KEY_STRING"));
// The same app running on a separate user shouldn't be able to manage app restrictions.
mContext.binder.callingUid = UserHandle.getUid(
UserHandle.USER_SYSTEM, appRestrictionsManagerAppId);
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
try {
dpm.setApplicationRestrictions(null, "pkg1", rest);
fail("Didn't throw expected SecurityException");
} catch (SecurityException expected) {
MoreAsserts.assertContainsRegex(
"caller cannot manage application 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;
assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
dpm.setApplicationRestrictions(admin1, "pkg1", null);
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
// Removing the ability for the package to manage app restrictions.
dpm.setApplicationRestrictionsManagingPackage(admin1, null);
assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
mContext.binder.callingUid = appRestrictionsManagerUid;
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());
}
}
public void testSetUserRestriction_asDo() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);