Modify setApplicationHidden and isApplicationHidden APIs
* Introduced new logic that allows the profile owner of an
organization-owned device (COPE PO) to hide/unhide system
apps in the personal profile.
* Modified both APIs to be callable on the parent profile
instance if called by the COPE PO.
* When called by the COPE PO, the package provided
must be a system package, otherwise an IllegalArgument
Exception is thrown.
Bug: 147413198
Test: manual testing
atest com.android.server.devicepolicy.DevicePolicyManagerTest
atest com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testApplicationHidden
atest com.android.cts.devicepolicy.ManagedProfileTest#testParentProfileApiDisabled
Change-Id: I4fae8acee9f00e3b9c805f29cf826f917cda6abd
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a35a899..b37a7d1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8199,6 +8199,11 @@
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
* {@link #setDelegatedScopes}.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance, returned by
+ * {@link #getParentProfileInstance(ComponentName)}, where the caller must be the profile owner
+ * of an organization-owned managed profile and the package must be a system package. If called
+ * on the parent instance, then the package is hidden or unhidden in the personal profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if the caller is a package access delegate.
@@ -8206,17 +8211,20 @@
* @param hidden {@code true} if the package should be hidden, {@code false} if it should be
* unhidden.
* @return boolean Whether the hidden setting of the package was successfully updated.
- * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws SecurityException if {@code admin} is not a device or profile owner or if called on
+ * the parent profile and the {@code admin} is not a profile owner of an
+ * organization-owned managed profile.
+ * @throws IllegalArgumentException if called on the parent profile and the package provided
+ * is not a system package.
* @see #setDelegatedScopes
* @see #DELEGATION_PACKAGE_ACCESS
*/
public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
boolean hidden) {
- throwIfParentInstance("setApplicationHidden");
if (mService != null) {
try {
return mService.setApplicationHidden(admin, mContext.getPackageName(), packageName,
- hidden);
+ hidden, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8228,20 +8236,30 @@
* Determine if a package is hidden. This function can be called by a device owner, profile
* owner, or by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
* {@link #setDelegatedScopes}.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance, returned by
+ * {@link #getParentProfileInstance(ComponentName)}, where the caller must be the profile owner
+ * of an organization-owned managed profile and the package must be a system package. If called
+ * on the parent instance, this will determine whether the package is hidden or unhidden in the
+ * personal profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if the caller is a package access delegate.
* @param packageName The name of the package to retrieve the hidden status of.
* @return boolean {@code true} if the package is hidden, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws SecurityException if {@code admin} is not a device or profile owner or if called on
+ * the parent profile and the {@code admin} is not a profile owner of an
+ * organization-owned managed profile.
+ * @throws IllegalArgumentException if called on the parent profile and the package provided
+ * is not a system package.
* @see #setDelegatedScopes
* @see #DELEGATION_PACKAGE_ACCESS
*/
public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("isApplicationHidden");
if (mService != null) {
try {
- return mService.isApplicationHidden(admin, mContext.getPackageName(), packageName);
+ return mService.isApplicationHidden(admin, mContext.getPackageName(), packageName,
+ mParentInstance);
} 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 a2c0856..7ed16a9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -233,8 +233,8 @@
boolean isNotificationListenerServicePermitted(in String packageName, int userId);
Intent createAdminSupportIntent(in String restriction);
- boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden);
- boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName);
+ boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
+ boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
boolean removeUser(in ComponentName who, in UserHandle userHandle);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4b90027..717c051 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10649,30 +10649,35 @@
@Override
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
- boolean hidden) {
- int callingUserId = UserHandle.getCallingUserId();
- boolean result = false;
+ boolean hidden, boolean parent) {
+ final int userId = parent ? getProfileParentId(UserHandle.getCallingUserId())
+ : UserHandle.getCallingUserId();
+ boolean result;
+
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_PACKAGE_ACCESS);
- long id = mInjector.binderClearCallingIdentity();
- try {
- result = mIPackageManager
- .setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
- } catch (RemoteException re) {
- // shouldn't happen
- Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
+ if (parent) {
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+ // Ensure the package provided is a system package, this is to ensure that this
+ // API cannot be used to leak if certain non-system package exists in the person
+ // profile.
+ mInjector.binderWithCleanCallingIdentity(() ->
+ enforcePackageIsSystemPackage(packageName, hidden, userId));
}
+
+ result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
+ .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
}
final boolean isDelegate = (who == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
.setAdmin(callerPackage)
.setBoolean(isDelegate)
+ .setBoolean(parent)
.setStrings(packageName, hidden ? "hidden" : "not_hidden")
.write();
return result;
@@ -10680,24 +10685,40 @@
@Override
public boolean isApplicationHidden(ComponentName who, String callerPackage,
- String packageName) {
- int callingUserId = UserHandle.getCallingUserId();
+ String packageName, boolean parent) {
+ final int userId = parent ? getProfileParentId(UserHandle.getCallingUserId())
+ : UserHandle.getCallingUserId();
+
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_PACKAGE_ACCESS);
- long id = mInjector.binderClearCallingIdentity();
- try {
- return mIPackageManager.getApplicationHiddenSettingAsUser(
- packageName, callingUserId);
- } catch (RemoteException re) {
- // shouldn't happen
- Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
+ if (parent) {
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+ // Ensure the package provided is a system package.
+ mInjector.binderWithCleanCallingIdentity(() ->
+ enforcePackageIsSystemPackage(packageName, false, userId));
}
- return false;
+
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> mIPackageManager.getApplicationHiddenSettingAsUser(packageName, userId));
+ }
+ }
+
+ private void enforcePackageIsSystemPackage(String packageName, boolean hidden, int userId)
+ throws RemoteException {
+ int flags = PackageManager.MATCH_SYSTEM_ONLY;
+ // If the package is currently hidden then it is considered uninstalled and
+ // the MATCH_UNINSTALLED_PACKAGES flag has to be added.
+ if (!hidden) {
+ flags |= PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ }
+ PackageInfo packageInfo = mIPackageManager.getPackageInfo(packageName, flags, userId);
+ if (packageInfo == null || !packageInfo.applicationInfo.isSystemApp()) {
+ throw new IllegalArgumentException(
+ "The provided package is not a system package");
}
}
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 43e9570..cb6a205 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2183,6 +2183,63 @@
assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
}
+ public void testSetApplicationHiddenWithDO() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ mContext.packageName = admin1.getPackageName();
+ setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+
+ String packageName = "com.google.android.test";
+
+ dpm.setApplicationHidden(admin1, packageName, true);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ true, UserHandle.USER_SYSTEM);
+
+ dpm.setApplicationHidden(admin1, packageName, false);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ false, UserHandle.USER_SYSTEM);
+
+ verify(getServices().ipackageManager, never()).getPackageInfo(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ verify(getServices().ipackageManager, never()).getPackageInfo(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+
+ public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception {
+ final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ mContext.packageName = admin1.getPackageName();
+ setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+
+ String packageName = "com.google.android.test";
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ when(getServices().ipackageManager.getPackageInfo(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)).thenReturn(
+ packageInfo);
+ when(getServices().ipackageManager.getPackageInfo(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM)).thenReturn(packageInfo);
+
+ parentDpm.setApplicationHidden(admin1, packageName, true);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ true, UserHandle.USER_SYSTEM);
+
+ parentDpm.setApplicationHidden(admin1, packageName, false);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ false, UserHandle.USER_SYSTEM);
+ }
+
public void testGetMacAddress() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);