From 1be7bd6aa65488331510de8bdb142c4281db9bbb Mon Sep 17 00:00:00 2001 From: Kholoud Mohamed Date: Wed, 14 Dec 2022 10:13:19 +0000 Subject: [PATCH 1/3] Resolve policies that could be set locally and globally Also added userControlDisabledPackages policy Bug: 258442697 Bug: 232918480 Test: atest android.devicepolicy.cts.UserControlDisabledPackagesTest Change-Id: I4ce87c0351f5b76ac77ba0ec556a9b4dbf6b0ac1 --- core/api/current.txt | 1 + .../app/admin/DevicePolicyManager.java | 6 + core/java/android/app/admin/TargetUser.java | 14 + .../devicepolicy/DevicePolicyEngine.java | 423 +++++++++++++----- .../DevicePolicyManagerService.java | 107 ++++- .../server/devicepolicy/EnforcingAdmin.java | 7 + .../server/devicepolicy/IntegerUnion.java | 5 + .../server/devicepolicy/MostRestrictive.java | 5 + .../server/devicepolicy/PolicyDefinition.java | 20 +- .../devicepolicy/PolicyEnforcerCallbacks.java | 13 + .../server/devicepolicy/PolicyState.java | 70 ++- .../devicepolicy/SetPolicySerializer.java | 43 ++ .../android/server/devicepolicy/SetUnion.java | 5 + .../server/devicepolicy/TopPriority.java | 6 + 14 files changed, 575 insertions(+), 150 deletions(-) create mode 100644 services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java diff --git a/core/api/current.txt b/core/api/current.txt index 5dd1b3938f40b..298147c813e45 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -8160,6 +8160,7 @@ package android.app.admin { field @NonNull public static final android.app.admin.TargetUser GLOBAL; field @NonNull public static final android.app.admin.TargetUser LOCAL_USER; field @NonNull public static final android.app.admin.TargetUser PARENT_USER; + field @NonNull public static final android.app.admin.TargetUser UNKNOWN_USER; } public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 209b112ec9edf..28a9b51394b1a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3996,6 +3996,12 @@ public class DevicePolicyManager { */ public static final String LOCK_TASK_POLICY = "lockTask"; + // TODO: Expose this as SystemAPI once we add the query API + /** + * @hide + */ + public static final String USER_CONTROL_DISABLED_PACKAGES = "userControlDisabledPackages"; + /** * This object is a single place to tack on invalidation and disable calls. All * binder caches in this class derive from this Config, so all can be invalidated or diff --git a/core/java/android/app/admin/TargetUser.java b/core/java/android/app/admin/TargetUser.java index acbac29dabe6f..1ec2d5225f4a3 100644 --- a/core/java/android/app/admin/TargetUser.java +++ b/core/java/android/app/admin/TargetUser.java @@ -43,6 +43,11 @@ public final class TargetUser { */ public static final int GLOBAL_USER_ID = -3; + /** + * @hide + */ + public static final int UNKNOWN_USER_ID = -3; + /** * Indicates that the policy relates to the user the admin is installed on. */ @@ -61,6 +66,15 @@ public final class TargetUser { @NonNull public static final TargetUser GLOBAL = new TargetUser(GLOBAL_USER_ID); + /** + * Indicates that the policy relates to some unknown user on the device. For example, if Admin1 + * has set a global policy on a device and Admin2 has set a conflicting local + * policy on some other secondary user, Admin1 will get a policy update callback with + * {@code UNKNOWN_USER} as the target user. + */ + @NonNull + public static final TargetUser UNKNOWN_USER = new TargetUser(UNKNOWN_USER_ID); + private final int mUserId; /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index d796ddf7a462c..a795c3fcfb88d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -34,9 +34,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; +import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.UserHandle; +import android.os.UserManager; import android.util.AtomicFile; import android.util.Log; import android.util.SparseArray; @@ -68,6 +71,8 @@ final class DevicePolicyEngine { static final String TAG = "DevicePolicyEngine"; private final Context mContext; + private final UserManager mUserManager; + // TODO(b/256849338): add more granular locks private final Object mLock = new Object(); @@ -83,6 +88,7 @@ final class DevicePolicyEngine { DevicePolicyEngine(@NonNull Context context) { mContext = Objects.requireNonNull(context); + mUserManager = mContext.getSystemService(UserManager.class); mLocalPolicies = new SparseArray<>(); mGlobalPolicies = new HashMap<>(); } @@ -91,10 +97,8 @@ final class DevicePolicyEngine { /** * Set the policy for the provided {@code policyDefinition} * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}. - * Returns {@code true} if the enforced policy has been changed. - * */ - boolean setLocalPolicy( + void setLocalPolicy( @NonNull PolicyDefinition policyDefinition, @NonNull EnforcingAdmin enforcingAdmin, @NonNull V value, @@ -105,45 +109,125 @@ final class DevicePolicyEngine { Objects.requireNonNull(value); synchronized (mLock) { - PolicyState policyState = getLocalPolicyStateLocked(policyDefinition, userId); + PolicyState localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); - boolean policyChanged = policyState.setPolicy(enforcingAdmin, value); + boolean hasGlobalPolicies = hasGlobalPolicyLocked(policyDefinition); + boolean policyChanged; + if (hasGlobalPolicies) { + PolicyState globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); + policyChanged = localPolicyState.addPolicy( + enforcingAdmin, + value, + globalPolicyState.getPoliciesSetByAdmins()); + } else { + policyChanged = localPolicyState.addPolicy(enforcingAdmin, value); + } if (policyChanged) { - enforcePolicy( - policyDefinition, policyState.getCurrentResolvedPolicy(), userId); - sendPolicyChangedToAdmins( - policyState.getPoliciesSetByAdmins().keySet(), - enforcingAdmin, - policyDefinition, - userId == enforcingAdmin.getUserId() - ? TargetUser.LOCAL_USER_ID : TargetUser.PARENT_USER_ID); - + onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); } - boolean wasAdminPolicyEnforced = Objects.equals( - policyState.getCurrentResolvedPolicy(), value); + + boolean policyEnforced = Objects.equals( + localPolicyState.getCurrentResolvedPolicy(), value); sendPolicyResultToAdmin( enforcingAdmin, policyDefinition, - wasAdminPolicyEnforced, + policyEnforced, // TODO: we're always sending this for now, should properly handle errors. REASON_CONFLICTING_ADMIN_POLICY, - userId == enforcingAdmin.getUserId() - ? TargetUser.LOCAL_USER_ID : TargetUser.PARENT_USER_ID); + userId); write(); - return policyChanged; } } + // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values + /** + * Removes any previously set policy for the provided {@code policyDefinition} + * (see {@link PolicyDefinition}) and {@code enforcingAdmin}. + */ + void removeLocalPolicy( + @NonNull PolicyDefinition policyDefinition, + @NonNull EnforcingAdmin enforcingAdmin, + int userId) { + Objects.requireNonNull(policyDefinition); + Objects.requireNonNull(enforcingAdmin); + + synchronized (mLock) { + if (!hasLocalPolicyLocked(policyDefinition, userId)) { + return; + } + PolicyState localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); + + boolean policyChanged; + if (hasGlobalPolicyLocked(policyDefinition)) { + PolicyState globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); + policyChanged = localPolicyState.removePolicy( + enforcingAdmin, + globalPolicyState.getPoliciesSetByAdmins()); + } else { + policyChanged = localPolicyState.removePolicy(enforcingAdmin); + } + + if (policyChanged) { + onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); + } + + // For a removePolicy to be enforced, it means no current policy exists + boolean policyEnforced = localPolicyState.getCurrentResolvedPolicy() == null; + sendPolicyResultToAdmin( + enforcingAdmin, + policyDefinition, + policyEnforced, + // TODO: we're always sending this for now, should properly handle errors. + REASON_CONFLICTING_ADMIN_POLICY, + userId); + + if (localPolicyState.getPoliciesSetByAdmins().isEmpty()) { + removeLocalPolicyStateLocked(policyDefinition, userId); + } + + write(); + } + } + + /** + * Enforces the new policy and notifies relevant admins. + */ + private void onLocalPolicyChanged( + @NonNull PolicyDefinition policyDefinition, + @NonNull EnforcingAdmin enforcingAdmin, + int userId) { + + PolicyState localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); + enforcePolicy( + policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); + + // Send policy updates to admins who've set it locally + sendPolicyChangedToAdmins( + localPolicyState.getPoliciesSetByAdmins().keySet(), + enforcingAdmin, + policyDefinition, + // This policy change is only relevant to a single user, not the global + // policy value, + userId); + + // Send policy updates to admins who've set it globally + if (hasGlobalPolicyLocked(policyDefinition)) { + PolicyState globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); + sendPolicyChangedToAdmins( + globalPolicyState.getPoliciesSetByAdmins().keySet(), + enforcingAdmin, + policyDefinition, + userId); + } + } // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values /** * Set the policy for the provided {@code policyDefinition} * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}. - * Returns {@code true} if the enforced policy has been changed. - * */ - boolean setGlobalPolicy( + void setGlobalPolicy( @NonNull PolicyDefinition policyDefinition, @NonNull EnforcingAdmin enforcingAdmin, @NonNull V value) { @@ -153,77 +237,27 @@ final class DevicePolicyEngine { Objects.requireNonNull(value); synchronized (mLock) { - PolicyState policyState = getGlobalPolicyStateLocked(policyDefinition); + PolicyState globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); - - boolean policyChanged = policyState.setPolicy(enforcingAdmin, value); + boolean policyChanged = globalPolicyState.addPolicy(enforcingAdmin, value); if (policyChanged) { - enforcePolicy(policyDefinition, policyState.getCurrentResolvedPolicy(), - UserHandle.USER_ALL); - sendPolicyChangedToAdmins( - policyState.getPoliciesSetByAdmins().keySet(), - enforcingAdmin, - policyDefinition, - TargetUser.GLOBAL_USER_ID); + onGlobalPolicyChanged(policyDefinition, enforcingAdmin); } - boolean wasAdminPolicyEnforced = Objects.equals( - policyState.getCurrentResolvedPolicy(), value); + + boolean policyEnforcedOnAllUsers = enforceGlobalPolicyOnUsersWithLocalPoliciesLocked( + policyDefinition, enforcingAdmin, value); + boolean policyEnforcedGlobally = Objects.equals( + globalPolicyState.getCurrentResolvedPolicy(), value); + sendPolicyResultToAdmin( enforcingAdmin, policyDefinition, - wasAdminPolicyEnforced, + policyEnforcedGlobally && policyEnforcedOnAllUsers, // TODO: we're always sending this for now, should properly handle errors. REASON_CONFLICTING_ADMIN_POLICY, - TargetUser.GLOBAL_USER_ID); + UserHandle.USER_ALL); write(); - return policyChanged; - } - } - - - // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values - /** - * Removes any previously set policy for the provided {@code policyDefinition} - * (see {@link PolicyDefinition}) and {@code enforcingAdmin}. - * Returns {@code true} if the enforced policy has been changed. - * - */ - boolean removeLocalPolicy( - @NonNull PolicyDefinition policyDefinition, - @NonNull EnforcingAdmin enforcingAdmin, - int userId) { - - Objects.requireNonNull(policyDefinition); - Objects.requireNonNull(enforcingAdmin); - - synchronized (mLock) { - PolicyState policyState = getLocalPolicyStateLocked(policyDefinition, userId); - boolean policyChanged = policyState.removePolicy(enforcingAdmin); - - if (policyChanged) { - enforcePolicy( - policyDefinition, policyState.getCurrentResolvedPolicy(), userId); - sendPolicyChangedToAdmins( - policyState.getPoliciesSetByAdmins().keySet(), - enforcingAdmin, - policyDefinition, - userId == enforcingAdmin.getUserId() - ? TargetUser.LOCAL_USER_ID : TargetUser.PARENT_USER_ID); - } - // for a remove policy to be enforced, it means no current policy exists - boolean wasAdminPolicyEnforced = policyState.getCurrentResolvedPolicy() == null; - sendPolicyResultToAdmin( - enforcingAdmin, - policyDefinition, - wasAdminPolicyEnforced, - // TODO: we're always sending this for now, should properly handle errors. - REASON_CONFLICTING_ADMIN_POLICY, - userId == enforcingAdmin.getUserId() - ? TargetUser.LOCAL_USER_ID : TargetUser.PARENT_USER_ID); - - write(); - return policyChanged; } } @@ -231,10 +265,8 @@ final class DevicePolicyEngine { /** * Removes any previously set policy for the provided {@code policyDefinition} * (see {@link PolicyDefinition}) and {@code enforcingAdmin}. - * Returns {@code true} if the enforced policy has been changed. - * */ - boolean removeGlobalPolicy( + void removeGlobalPolicy( @NonNull PolicyDefinition policyDefinition, @NonNull EnforcingAdmin enforcingAdmin) { @@ -246,61 +278,168 @@ final class DevicePolicyEngine { boolean policyChanged = policyState.removePolicy(enforcingAdmin); if (policyChanged) { - enforcePolicy(policyDefinition, policyState.getCurrentResolvedPolicy(), - UserHandle.USER_ALL); - - sendPolicyChangedToAdmins( - policyState.getPoliciesSetByAdmins().keySet(), - enforcingAdmin, - policyDefinition, - TargetUser.GLOBAL_USER_ID); + onGlobalPolicyChanged(policyDefinition, enforcingAdmin); } - // for a remove policy to be enforced, it means no current policy exists - boolean wasAdminPolicyEnforced = policyState.getCurrentResolvedPolicy() == null; + + boolean policyEnforcedOnAllUsers = enforceGlobalPolicyOnUsersWithLocalPoliciesLocked( + policyDefinition, enforcingAdmin, /* value= */ null); + // For a removePolicy to be enforced, it means no current policy exists + boolean policyEnforcedGlobally = policyState.getCurrentResolvedPolicy() == null; + sendPolicyResultToAdmin( enforcingAdmin, policyDefinition, - wasAdminPolicyEnforced, + policyEnforcedGlobally && policyEnforcedOnAllUsers, // TODO: we're always sending this for now, should properly handle errors. REASON_CONFLICTING_ADMIN_POLICY, - TargetUser.GLOBAL_USER_ID); + UserHandle.USER_ALL); + + if (policyState.getPoliciesSetByAdmins().isEmpty()) { + removeGlobalPolicyStateLocked(policyDefinition); + } write(); - return policyChanged; } } /** - * Retrieves policies set by all admins for the provided {@code policyDefinition}. - * + * Enforces the new policy globally and notifies relevant admins. */ - PolicyState getLocalPolicy(@NonNull PolicyDefinition policyDefinition, int userId) { + private void onGlobalPolicyChanged( + @NonNull PolicyDefinition policyDefinition, + @NonNull EnforcingAdmin enforcingAdmin) { + PolicyState policyState = getGlobalPolicyStateLocked(policyDefinition); + + enforcePolicy(policyDefinition, policyState.getCurrentResolvedPolicy(), + UserHandle.USER_ALL); + + sendPolicyChangedToAdmins( + policyState.getPoliciesSetByAdmins().keySet(), + enforcingAdmin, + policyDefinition, + UserHandle.USER_ALL); + } + + /** + * Tries to enforce the global policy locally on all users that have the same policy set + * locally, this is only applicable to policies that can be set locally or globally + * (e.g. setCameraDisabled, setScreenCaptureDisabled) rather than + * policies that are global by nature (e.g. setting Wifi enabled/disabled). + * + *

A {@code null} policy value means the policy was removed + * + *

Returns {@code true} if the policy is enforced successfully on all users. + */ + private boolean enforceGlobalPolicyOnUsersWithLocalPoliciesLocked( + @NonNull PolicyDefinition policyDefinition, + @NonNull EnforcingAdmin enforcingAdmin, + @Nullable V value) { + // Global only policies can't be applied locally, return early. + if (policyDefinition.isGlobalOnlyPolicy()) { + return true; + } + boolean isAdminPolicyEnforced = true; + for (int i = 0; i < mLocalPolicies.size(); i++) { + int userId = mLocalPolicies.keyAt(i); + if (!hasLocalPolicyLocked(policyDefinition, userId)) { + continue; + } + + PolicyState localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); + PolicyState globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); + + boolean policyChanged = localPolicyState.resolvePolicy( + globalPolicyState.getPoliciesSetByAdmins()); + if (policyChanged) { + enforcePolicy( + policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); + sendPolicyChangedToAdmins( + localPolicyState.getPoliciesSetByAdmins().keySet(), + enforcingAdmin, + policyDefinition, + // Even though this is caused by a global policy change, admins who've set + // it locally should only care about the local user state. + userId); + + } + isAdminPolicyEnforced &= Objects.equals( + value, localPolicyState.getCurrentResolvedPolicy()); + } + return isAdminPolicyEnforced; + } + + /** + * Retrieves the resolved policy for the provided {@code policyDefinition} and {@code userId}. + */ + @Nullable + V getResolvedPolicy(@NonNull PolicyDefinition policyDefinition, int userId) { Objects.requireNonNull(policyDefinition); synchronized (mLock) { - return getLocalPolicyStateLocked(policyDefinition, userId); + if (hasLocalPolicyLocked(policyDefinition, userId)) { + return getLocalPolicyStateLocked( + policyDefinition, userId).getCurrentResolvedPolicy(); + } + if (hasGlobalPolicyLocked(policyDefinition)) { + return getGlobalPolicyStateLocked(policyDefinition).getCurrentResolvedPolicy(); + } + return null; } } /** - * Retrieves policies set by all admins for the provided {@code policyDefinition}. - * + * Retrieves the policy set by the admin for the provided {@code policyDefinition} and + * {@code userId} if one was set, otherwise returns {@code null}. */ - PolicyState getGlobalPolicy(@NonNull PolicyDefinition policyDefinition) { + @Nullable + V getLocalPolicySetByAdmin( + @NonNull PolicyDefinition policyDefinition, + @NonNull EnforcingAdmin enforcingAdmin, + int userId) { Objects.requireNonNull(policyDefinition); + Objects.requireNonNull(enforcingAdmin); synchronized (mLock) { - return getGlobalPolicyStateLocked(policyDefinition); + if (!hasLocalPolicyLocked(policyDefinition, userId)) { + return null; + } + return getLocalPolicyStateLocked(policyDefinition, userId) + .getPoliciesSetByAdmins().get(enforcingAdmin); } } + private boolean hasLocalPolicyLocked(PolicyDefinition policyDefinition, int userId) { + if (policyDefinition.isGlobalOnlyPolicy()) { + return false; + } + if (!mLocalPolicies.contains(userId)) { + return false; + } + if (!mLocalPolicies.get(userId).containsKey(policyDefinition.getPolicyKey())) { + return false; + } + return !mLocalPolicies.get(userId).get(policyDefinition.getPolicyKey()) + .getPoliciesSetByAdmins().isEmpty(); + } + + private boolean hasGlobalPolicyLocked(PolicyDefinition policyDefinition) { + if (policyDefinition.isLocalOnlyPolicy()) { + return false; + } + if (!mGlobalPolicies.containsKey(policyDefinition.getPolicyKey())) { + return false; + } + return !mGlobalPolicies.get(policyDefinition.getPolicyKey()).getPoliciesSetByAdmins() + .isEmpty(); + } + @NonNull private PolicyState getLocalPolicyStateLocked( PolicyDefinition policyDefinition, int userId) { if (policyDefinition.isGlobalOnlyPolicy()) { - throw new IllegalArgumentException("Can't set global policy " - + policyDefinition.getPolicyKey() + " locally."); + throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a global only" + + "policy."); } if (!mLocalPolicies.contains(userId)) { @@ -313,11 +452,19 @@ final class DevicePolicyEngine { return getPolicyState(mLocalPolicies.get(userId), policyDefinition); } + private void removeLocalPolicyStateLocked( + PolicyDefinition policyDefinition, int userId) { + if (!mLocalPolicies.contains(userId)) { + return; + } + mLocalPolicies.get(userId).remove(policyDefinition.getPolicyKey()); + } + @NonNull private PolicyState getGlobalPolicyStateLocked(PolicyDefinition policyDefinition) { if (policyDefinition.isLocalOnlyPolicy()) { - throw new IllegalArgumentException("Can't set local policy " - + policyDefinition.getPolicyKey() + " globally."); + throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a local only" + + "policy."); } if (!mGlobalPolicies.containsKey(policyDefinition.getPolicyKey())) { @@ -327,6 +474,10 @@ final class DevicePolicyEngine { return getPolicyState(mGlobalPolicies, policyDefinition); } + private void removeGlobalPolicyStateLocked(PolicyDefinition policyDefinition) { + mGlobalPolicies.remove(policyDefinition.getPolicyKey()); + } + private static PolicyState getPolicyState( Map> policies, PolicyDefinition policyDefinition) { try { @@ -344,14 +495,14 @@ final class DevicePolicyEngine { private void enforcePolicy( PolicyDefinition policyDefinition, @Nullable V policyValue, int userId) { - // TODO: null policyValue means remove any enforced policies, ensure callbacks handle this - // properly + // null policyValue means remove any enforced policies, ensure callbacks handle this + // properly policyDefinition.enforcePolicy(policyValue, mContext, userId); } private void sendPolicyResultToAdmin( EnforcingAdmin admin, PolicyDefinition policyDefinition, boolean success, - int reason, int targetUserId) { + int reason, int userId) { Intent intent = new Intent(PolicyUpdatesReceiver.ACTION_DEVICE_POLICY_SET_RESULT); intent.setPackage(admin.getPackageName()); @@ -367,12 +518,13 @@ final class DevicePolicyEngine { Bundle extras = new Bundle(); extras.putString(EXTRA_POLICY_KEY, policyDefinition.getPolicyDefinitionKey()); - extras.putInt(EXTRA_POLICY_TARGET_USER_ID, targetUserId); - if (policyDefinition.getCallbackArgs() != null && !policyDefinition.getCallbackArgs().isEmpty()) { extras.putBundle(EXTRA_POLICY_BUNDLE_KEY, policyDefinition.getCallbackArgs()); } + extras.putInt( + EXTRA_POLICY_TARGET_USER_ID, + getTargetUser(admin.getUserId(), userId)); extras.putInt( EXTRA_POLICY_SET_RESULT_KEY, success ? POLICY_SET_RESULT_SUCCESS : POLICY_SET_RESULT_FAILURE); @@ -380,29 +532,30 @@ final class DevicePolicyEngine { if (!success) { extras.putInt(EXTRA_POLICY_UPDATE_REASON_KEY, reason); } - intent.putExtras(extras); + maybeSendIntentToAdminReceivers(intent, UserHandle.of(admin.getUserId()), receivers); } // TODO(b/261430877): Finalise the decision on which admins to send the updates to. private void sendPolicyChangedToAdmins( - Set admins, EnforcingAdmin callingAdmin, + Set admins, + EnforcingAdmin callingAdmin, PolicyDefinition policyDefinition, - int targetUserId) { + int userId) { for (EnforcingAdmin admin: admins) { // We're sending a separate broadcast for the calling admin with the result. if (admin.equals(callingAdmin)) { continue; } maybeSendOnPolicyChanged( - admin, policyDefinition, REASON_CONFLICTING_ADMIN_POLICY, targetUserId); + admin, policyDefinition, REASON_CONFLICTING_ADMIN_POLICY, userId); } } private void maybeSendOnPolicyChanged( EnforcingAdmin admin, PolicyDefinition policyDefinition, int reason, - int targetUserId) { + int userId) { Intent intent = new Intent(PolicyUpdatesReceiver.ACTION_DEVICE_POLICY_CHANGED); intent.setPackage(admin.getPackageName()); @@ -418,14 +571,16 @@ final class DevicePolicyEngine { Bundle extras = new Bundle(); extras.putString(EXTRA_POLICY_KEY, policyDefinition.getPolicyDefinitionKey()); - extras.putInt(EXTRA_POLICY_TARGET_USER_ID, targetUserId); - if (policyDefinition.getCallbackArgs() != null && !policyDefinition.getCallbackArgs().isEmpty()) { extras.putBundle(EXTRA_POLICY_BUNDLE_KEY, policyDefinition.getCallbackArgs()); } + extras.putInt( + EXTRA_POLICY_TARGET_USER_ID, + getTargetUser(admin.getUserId(), userId)); extras.putInt(EXTRA_POLICY_UPDATE_REASON_KEY, reason); intent.putExtras(extras); + maybeSendIntentToAdminReceivers( intent, UserHandle.of(admin.getUserId()), receivers); } @@ -447,6 +602,26 @@ final class DevicePolicyEngine { } } + private int getTargetUser(int adminUserId, int targetUserId) { + if (targetUserId == UserHandle.USER_ALL) { + return TargetUser.GLOBAL_USER_ID; + } + if (adminUserId == targetUserId) { + return TargetUser.LOCAL_USER_ID; + } + if (getProfileParentId(adminUserId) == targetUserId) { + return TargetUser.PARENT_USER_ID; + } + return TargetUser.UNKNOWN_USER_ID; + } + + private int getProfileParentId(int userId) { + return Binder.withCleanCallingIdentity(() -> { + UserInfo parentUser = mUserManager.getProfileParent(userId); + return parentUser != null ? parentUser.id : userId; + }); + } + private void write() { Log.d(TAG, "Writing device policies to file."); new DevicePoliciesReaderWriter().writeToFileLocked(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 51f3e321338d5..a79fd894bed91 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3300,7 +3300,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } List protectedPackages = (owner == null || owner.protectedPackages == null) - ? Collections.emptyList() : owner.protectedPackages; + ? null : owner.protectedPackages; mInjector.binderWithCleanCallingIdentity(() -> mInjector.getPackageManagerInternal().setOwnerProtectedPackages( targetUserId, protectedPackages)); @@ -12644,9 +12644,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin, caller.getUserId()); } else { - LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicy( + LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( PolicyDefinition.LOCK_TASK, - caller.getUserId()).getPoliciesSetByAdmins().get(admin); + admin, + caller.getUserId()); LockTaskPolicy policy; if (currentPolicy == null) { policy = new LockTaskPolicy(Set.of(packages)); @@ -12689,8 +12690,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isCoexistenceEnabled(caller)) { - LockTaskPolicy policy = mDevicePolicyEngine.getLocalPolicy( - PolicyDefinition.LOCK_TASK, userHandle).getCurrentResolvedPolicy(); + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userHandle); if (policy == null) { return new String[0]; } else { @@ -12719,8 +12720,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO(b/260560985): This is not the right check, as the flag could be enabled but there // could be an admin that hasn't targeted U. if (isCoexistenceFlagEnabled()) { - LockTaskPolicy policy = mDevicePolicyEngine.getLocalPolicy( - PolicyDefinition.LOCK_TASK, userId).getCurrentResolvedPolicy(); + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userId); if (policy == null) { return false; } @@ -12754,9 +12755,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isCoexistenceEnabled(caller)) { EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userHandle); - LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicy( + LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( PolicyDefinition.LOCK_TASK, - caller.getUserId()).getPoliciesSetByAdmins().get(admin); + admin, + caller.getUserId()); if (currentPolicy == null) { throw new IllegalArgumentException("Can't set a lock task flags without setting " + "lock task packages first."); @@ -12793,8 +12795,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isCoexistenceEnabled(caller)) { - LockTaskPolicy policy = mDevicePolicyEngine.getLocalPolicy( - PolicyDefinition.LOCK_TASK, userHandle).getCurrentResolvedPolicy(); + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userHandle); if (policy == null) { // We default on the power button menu, in order to be consistent with pre-P // behaviour. @@ -17672,12 +17674,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { checkCanExecuteOrThrowUnsafe( DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES); - synchronized (getLockObject()) { - ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); - if (!Objects.equals(owner.protectedPackages, packages)) { - owner.protectedPackages = packages.isEmpty() ? null : packages; - saveSettingsLocked(caller.getUserId()); - pushUserControlDisabledPackagesLocked(caller.getUserId()); + if (isCoexistenceEnabled(caller)) { + if (packages.isEmpty()) { + removeUserControlDisabledPackages(caller); + } else { + addUserControlDisabledPackages(caller, new HashSet<>(packages)); + } + } else { + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); + if (!Objects.equals(owner.protectedPackages, packages)) { + owner.protectedPackages = packages.isEmpty() ? null : packages; + saveSettingsLocked(caller.getUserId()); + pushUserControlDisabledPackagesLocked(caller.getUserId()); + } } } @@ -17688,6 +17698,52 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); } + private void addUserControlDisabledPackages(CallerIdentity caller, Set packages) { + if (isCallerDeviceOwner(caller)) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + // TODO(b/260573124): add correct enforcing admin when permission changes are + // merged. + EnforcingAdmin.createEnterpriseEnforcingAdmin( + caller.getComponentName(), caller.getUserId()), + packages); + } else { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + // TODO(b/260573124): add correct enforcing admin when permission changes are + // merged. + EnforcingAdmin.createEnterpriseEnforcingAdmin( + caller.getComponentName(), caller.getUserId()), + packages, + caller.getUserId()); + } + } + + private void removeUserControlDisabledPackages(CallerIdentity caller) { + if (isCallerDeviceOwner(caller)) { + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + // TODO(b/260573124): add correct enforcing admin when permission changes are + // merged. + EnforcingAdmin.createEnterpriseEnforcingAdmin( + caller.getComponentName(), caller.getUserId())); + } else { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + // TODO(b/260573124): add correct enforcing admin when permission changes are + // merged. + EnforcingAdmin.createEnterpriseEnforcingAdmin( + caller.getComponentName(), caller.getUserId()), + caller.getUserId()); + } + } + + private boolean isCallerDeviceOwner(CallerIdentity caller) { + synchronized (getLockObject()) { + return getDeviceOwnerUserIdUncheckedLocked() == caller.getUserId(); + } + } + @Override public List getUserControlDisabledPackages(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); @@ -17696,10 +17752,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isFinancedDeviceOwner(caller)); - synchronized (getLockObject()) { - ActiveAdmin deviceOwner = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); - return deviceOwner.protectedPackages != null - ? deviceOwner.protectedPackages : Collections.emptyList(); + if (isCoexistenceEnabled(caller)) { + // This retrieves the policy for the calling user only, DOs for example can't know + // what's enforced globally or on another user. + Set packages = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + caller.getUserId()); + return packages == null ? Collections.emptyList() : packages.stream().toList(); + } else { + synchronized (getLockObject()) { + ActiveAdmin deviceOwner = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); + return deviceOwner.protectedPackages != null + ? deviceOwner.protectedPackages : Collections.emptyList(); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java index 00e48eb67ab00..da895f46f1aae 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java @@ -227,4 +227,11 @@ final class EnforcingAdmin { return new EnforcingAdmin(packageName, componentName, authorities, userId); } } + + @Override + public String toString() { + return "EnforcingAdmin { mPackageName= " + mPackageName + ", mComponentName= " + + mComponentName + ", mAuthorities= " + mAuthorities + ", mUserId= " + + mUserId + ", mIsRoleAuthority= " + mIsRoleAuthority + " }"; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java b/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java index 00bc261be9d02..a051a2bebae6f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/IntegerUnion.java @@ -36,4 +36,9 @@ final class IntegerUnion extends ResolutionMechanism { } return unionOfPolicies; } + + @Override + public String toString() { + return "IntegerUnion {}"; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java index 9a24dcf2395ce..edb3d2ef48561 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java @@ -44,4 +44,9 @@ final class MostRestrictive extends ResolutionMechanism { Map.Entry policy = adminPolicies.entrySet().stream().findFirst().get(); return policy.getValue(); } + + @Override + public String toString() { + return "MostRestrictive { mMostToLeastRestrictive= " + mMostToLeastRestrictive + " }"; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index c684af39a25fa..cfb3db02cd8a5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; final class PolicyDefinition { private static final int POLICY_FLAG_NONE = 0; @@ -98,10 +99,18 @@ final class PolicyDefinition { PolicyEnforcerCallbacks.setLockTask(value, context, userId), new LockTaskPolicy.LockTaskPolicySerializer()); - private static Map> sPolicyDefinitions = Map.of( + static PolicyDefinition> USER_CONTROLLED_DISABLED_PACKAGES = new PolicyDefinition<>( + DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES, + new SetUnion<>(), + (Set value, Context context, Integer userId, Bundle args) -> + PolicyEnforcerCallbacks.setUserControlDisabledPackages(value, userId), + new SetPolicySerializer<>()); + + private static final Map> sPolicyDefinitions = Map.of( DevicePolicyManager.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE, DevicePolicyManager.PERMISSION_GRANT_POLICY_KEY, PERMISSION_GRANT_NO_ARGS, - DevicePolicyManager.LOCK_TASK_POLICY, LOCK_TASK + DevicePolicyManager.LOCK_TASK_POLICY, LOCK_TASK, + DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES, USER_CONTROLLED_DISABLED_PACKAGES ); @@ -261,4 +270,11 @@ final class PolicyDefinition { V readPolicyValueFromXml(TypedXmlPullParser parser, String attributeName) { return mPolicySerializer.readFromXml(parser, attributeName); } + + @Override + public String toString() { + return "PolicyDefinition { mPolicyKey= " + mPolicyKey + ", mPolicyDefinitionKey= " + + mPolicyDefinitionKey + ", mResolutionMechanism= " + mResolutionMechanism + + ", mCallbackArgs= " + mCallbackArgs + ", mPolicyFlags= " + mPolicyFlags + " }"; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index c745b31afd9c3..5664d2b55230a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.PolicyUpdatesReceiver; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.UserHandle; @@ -29,11 +30,13 @@ import android.permission.AdminPermissionControlParams; import android.permission.PermissionControllerManager; import android.provider.Settings; +import com.android.server.LocalServices; import com.android.server.utils.Slogf; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -137,4 +140,14 @@ final class PolicyEnforcerCallbacks { return mValue.get(); } } + + static boolean setUserControlDisabledPackages( + @Nullable Set packages, int userId) { + Binder.withCleanCallingIdentity(() -> + LocalServices.getService(PackageManagerInternal.class) + .setOwnerProtectedPackages( + userId, + packages == null ? null : packages.stream().toList())); + return true; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java index ffde5f858ce69..54d3f54b05ee4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java @@ -62,12 +62,32 @@ final class PolicyState { /** * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. */ - boolean setPolicy(@NonNull EnforcingAdmin admin, @NonNull V value) { - mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(value)); + boolean addPolicy(@NonNull EnforcingAdmin admin, @NonNull V policy) { + mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(policy)); return resolvePolicy(); } + /** + * Takes into account global policies set by the admin when resolving the policy, this is only + * relevant to local policies that can be applied globally as well. + * + *

Note that local policies set by an admin takes precedence over global policies set by the + * same admin. + * + * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. + */ + boolean addPolicy( + @NonNull EnforcingAdmin admin, @NonNull V policy, + LinkedHashMap globalPoliciesSetByAdmins) { + mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(policy)); + + return resolvePolicy(globalPoliciesSetByAdmins); + } + + /** + * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. + */ boolean removePolicy(@NonNull EnforcingAdmin admin) { Objects.requireNonNull(admin); @@ -78,8 +98,52 @@ final class PolicyState { return resolvePolicy(); } + /** + * Takes into account global policies set by the admin when resolving the policy, this is only + * relevant to local policies that can be applied globally as well. + * + *

Note that local policies set by an admin takes precedence over global policies set by the + * same admin. + * + * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. + */ + boolean removePolicy( + @NonNull EnforcingAdmin admin, + LinkedHashMap globalPoliciesSetByAdmins) { + Objects.requireNonNull(admin); + + if (mPoliciesSetByAdmins.remove(admin) == null) { + return false; + } + + return resolvePolicy(globalPoliciesSetByAdmins); + } + + /** + * Takes into account global policies set by the admin when resolving the policy, this is only + * relevant to local policies that can be applied globally as well. + * + *

Note that local policies set by an admin takes precedence over global policies set by the + * same admin. + * + * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. + */ + boolean resolvePolicy(LinkedHashMap globalPoliciesSetByAdmins) { + // Add global policies first then override with local policies for the same admin. + LinkedHashMap mergedPolicies = + new LinkedHashMap<>(globalPoliciesSetByAdmins); + mergedPolicies.putAll(mPoliciesSetByAdmins); + + V resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies); + boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy); + mCurrentResolvedPolicy = resolvedPolicy; + + return policyChanged; + } + + @NonNull LinkedHashMap getPoliciesSetByAdmins() { - return mPoliciesSetByAdmins; + return new LinkedHashMap<>(mPoliciesSetByAdmins); } private boolean resolvePolicy() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java new file mode 100644 index 0000000000000..736627b610b04 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SetPolicySerializer.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicepolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; + +import java.io.IOException; +import java.util.Objects; +import java.util.Set; + +// TODO(scottjonathan): Replace with actual implementation +final class SetPolicySerializer extends PolicySerializer> { + + @Override + void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull Set value) + throws IOException { + Objects.requireNonNull(value); + } + + @Nullable + @Override + Set readFromXml(TypedXmlPullParser parser, String attributeName) { + return null; + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java b/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java index 8a932c397745e..cf2698357d2c5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SetUnion.java @@ -37,4 +37,9 @@ final class SetUnion extends ResolutionMechanism> { } return unionOfPolicies; } + + @Override + public String toString() { + return "SetUnion {}"; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java b/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java index 4467b22c21160..571cf64978d8c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/TopPriority.java @@ -49,4 +49,10 @@ final class TopPriority extends ResolutionMechanism { Map.Entry policy = adminPolicies.entrySet().stream().findFirst().get(); return policy.getValue(); } + + @Override + public String toString() { + return "TopPriority { mHighestToLowestPriorityAuthorities= " + + mHighestToLowestPriorityAuthorities + " }"; + } } From d4e5ca032a81a6c04278c5b6b448c4379c055fa5 Mon Sep 17 00:00:00 2001 From: Kholoud Mohamed Date: Thu, 15 Dec 2022 17:47:06 +0000 Subject: [PATCH 2/3] Give precedence to protectedPackages set locally Bug: 258442697 Test: android.devicepolicy.cts.UserControlDisabledPackagesTest Change-Id: I8f6646f01a9810fb0b680f5099f40414d01c14ee --- .../content/pm/PackageManagerInternal.java | 9 +++++++-- .../server/pm/PackageManagerInternalBase.java | 2 +- .../android/server/pm/ProtectedPackages.java | 17 +++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 5f5327da412f4..f101e7364c31f 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -375,10 +375,15 @@ public abstract class PackageManagerInternal { int deviceOwnerUserId, String deviceOwner, SparseArray profileOwners); /** - * Marks packages as protected for a given user or all users in case of USER_ALL. + * Marks packages as protected for a given user or all users in case of USER_ALL. Setting + * {@code packageNames} to {@code null} means unset all existing protected packages for the + * given user. + * + *

Note that setting it if set for a specific user, it takes precedence over the packages + * set globally using USER_ALL. */ public abstract void setOwnerProtectedPackages( - @UserIdInt int userId, @NonNull List packageNames); + @UserIdInt int userId, @Nullable List packageNames); /** * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}. diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index cc9c1e0ac1799..fb47c8aabbf31 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -354,7 +354,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated public final void setOwnerProtectedPackages( - @UserIdInt int userId, @NonNull List packageNames) { + @UserIdInt int userId, @Nullable List packageNames) { getProtectedPackages().setOwnerProtectedPackages(userId, packageNames); } diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java index e9239889973a0..98533725371f7 100644 --- a/services/core/java/com/android/server/pm/ProtectedPackages.java +++ b/services/core/java/com/android/server/pm/ProtectedPackages.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; @@ -81,8 +80,8 @@ public class ProtectedPackages { /** Sets packages protected by a device or profile owner. */ public synchronized void setOwnerProtectedPackages( - @UserIdInt int userId, @NonNull List packageNames) { - if (packageNames.isEmpty()) { + @UserIdInt int userId, @Nullable List packageNames) { + if (packageNames == null) { mOwnerProtectedPackages.remove(userId); } else { mOwnerProtectedPackages.put(userId, new ArraySet<>(packageNames)); @@ -134,15 +133,21 @@ public class ProtectedPackages { */ private synchronized boolean isOwnerProtectedPackage( @UserIdInt int userId, String packageName) { - return isPackageProtectedForUser(UserHandle.USER_ALL, packageName) - || isPackageProtectedForUser(userId, packageName); + return hasProtectedPackages(userId) + ? isPackageProtectedForUser(userId, packageName) + : isPackageProtectedForUser(UserHandle.USER_ALL, packageName); } - private synchronized boolean isPackageProtectedForUser(int userId, String packageName) { + private synchronized boolean isPackageProtectedForUser( + @UserIdInt int userId, String packageName) { int userIdx = mOwnerProtectedPackages.indexOfKey(userId); return userIdx >= 0 && mOwnerProtectedPackages.valueAt(userIdx).contains(packageName); } + private synchronized boolean hasProtectedPackages(@UserIdInt int userId) { + return mOwnerProtectedPackages.indexOfKey(userId) >= 0; + } + /** * Returns {@code true} if a given package's state is protected. Otherwise, returns * {@code false}. From 3c8d8c6ffbaa0ef4afc1f0742a9507150c5c1024 Mon Sep 17 00:00:00 2001 From: Kholoud Mohamed Date: Tue, 20 Dec 2022 16:13:04 +0000 Subject: [PATCH 3/3] Allow non-DPC admins to use DeviceAdminService Bug: 261432333 Bug: 232918480 Test: manual Test: atest com.android.cts.devicepolicy.DeviceAdminServiceDeviceOwnerTest Test: atest com.android.cts.devicepolicy.DeviceAdminServiceProfileOwnerTest Change-Id: I6c57458fcb9823e58381bb278d90e7673a576789 --- .../android/app/admin/DeviceAdminService.java | 6 + .../app/admin/PolicyUpdatesReceiver.java | 5 +- .../DeviceAdminServiceController.java | 101 +++++++-- .../devicepolicy/DevicePolicyEngine.java | 210 +++++++++++++++++- .../DevicePolicyManagerService.java | 54 +++-- .../server/devicepolicy/PolicyState.java | 7 + 6 files changed, 341 insertions(+), 42 deletions(-) diff --git a/core/java/android/app/admin/DeviceAdminService.java b/core/java/android/app/admin/DeviceAdminService.java index 04fff0497cb2b..e6f04c64eb3a0 100644 --- a/core/java/android/app/admin/DeviceAdminService.java +++ b/core/java/android/app/admin/DeviceAdminService.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; +// TODO(b/263363091): Restrict to DPC and holders of a new role permission and update javadocs /** * Base class for a service that device owner/profile owners can optionally have. * @@ -45,6 +46,11 @@ import android.os.IBinder; * *

Note the process may still be killed if the system is under heavy memory pressure, in which * case the process will be re-started later. + * + *

Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * non-DPC admins can also optionally implement this service using the details + * mentioned above to ensure they receive policy update broadcasts + * (see {@link PolicyUpdatesReceiver}). */ public class DeviceAdminService extends Service { private final IDeviceAdminServiceImpl mImpl; diff --git a/core/java/android/app/admin/PolicyUpdatesReceiver.java b/core/java/android/app/admin/PolicyUpdatesReceiver.java index ff30a5f8a037f..3ad315753a29d 100644 --- a/core/java/android/app/admin/PolicyUpdatesReceiver.java +++ b/core/java/android/app/admin/PolicyUpdatesReceiver.java @@ -32,7 +32,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; -// TODO(b/261432333): Add more detailed javadocs on using DeviceAdminService. /** * Base class for implementing a policy update receiver. This class provides a convenience for * interpreting the raw intent actions ({@link #ACTION_DEVICE_POLICY_SET_RESULT} and @@ -43,6 +42,10 @@ import java.util.Objects; * *

When publishing your {@code PolicyUpdatesReceiver} subclass as a receiver, it must * require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission. + * + *

Admins can implement {@link DeviceAdminService} to ensure they receive all policy updates + * (for policies they have set) via {@link #onPolicyChanged} by constantly being bound to by the + * system. For more information see {@link DeviceAdminService}. */ public abstract class PolicyUpdatesReceiver extends BroadcastReceiver { private static String TAG = "PolicyUpdatesReceiver"; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java index 8f0af918cb53b..e080fc7a18544 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java @@ -35,8 +35,11 @@ import com.android.server.am.PersistentConnection; import com.android.server.appbinding.AppBindingUtils; import com.android.server.utils.Slogf; +import java.util.HashMap; +import java.util.Map; + /** - * Manages connections to persistent services in owner packages. + * Manages connections to persistent services in admin packages. */ public class DeviceAdminServiceController { static final String TAG = DevicePolicyManagerService.LOG_TAG; @@ -76,7 +79,8 @@ public class DeviceAdminServiceController { * User-ID -> {@link PersistentConnection}. */ @GuardedBy("mLock") - private final SparseArray mConnections = new SparseArray<>(); + private final SparseArray> mConnections = + new SparseArray<>(); public DeviceAdminServiceController(DevicePolicyManagerService service, DevicePolicyConstants constants) { @@ -104,9 +108,9 @@ public class DeviceAdminServiceController { /** * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} - * in an owner package and connect to it. + * in an admin package and connect to it. */ - public void startServiceForOwner(@NonNull String packageName, int userId, + public void startServiceForAdmin(@NonNull String packageName, int userId, @NonNull String actionForLog) { final long token = mInjector.binderClearCallingIdentity(); try { @@ -114,15 +118,16 @@ public class DeviceAdminServiceController { final ServiceInfo service = findService(packageName, userId); if (service == null) { if (DEBUG) { - Slogf.d(TAG, "Owner package %s on u%d has no service.", packageName, + Slogf.d(TAG, "Admin package %s on u%d has no service.", packageName, userId); } - disconnectServiceOnUserLocked(userId, actionForLog); + disconnectServiceOnUserLocked(packageName, userId, actionForLog); return; } // See if it's already running. final PersistentConnection existing = - mConnections.get(userId); + mConnections.contains(userId) + ? mConnections.get(userId).get(packageName) : null; if (existing != null) { // Note even when we're already connected to the same service, the binding // would have died at this point due to a package update. So we disconnect @@ -131,18 +136,21 @@ public class DeviceAdminServiceController { Slogf.d("Disconnecting from existing service connection.", packageName, userId); } - disconnectServiceOnUserLocked(userId, actionForLog); + disconnectServiceOnUserLocked(packageName, userId, actionForLog); } if (DEBUG) { - Slogf.d("Owner package %s on u%d has service %s for %s", packageName, userId, + Slogf.d("Admin package %s on u%d has service %s for %s", packageName, userId, service.getComponentName().flattenToShortString(), actionForLog); } final DevicePolicyServiceConnection conn = new DevicePolicyServiceConnection( userId, service.getComponentName()); - mConnections.put(userId, conn); + if (!mConnections.contains(userId)) { + mConnections.put(userId, new HashMap<>()); + } + mConnections.get(userId).put(packageName, conn); conn.bind(); } } finally { @@ -151,9 +159,24 @@ public class DeviceAdminServiceController { } /** - * Stop an owner service on a given user. + * Stop an admin service on a given user. */ - public void stopServiceForOwner(int userId, @NonNull String actionForLog) { + public void stopServiceForAdmin( + @NonNull String packageName, int userId, @NonNull String actionForLog) { + final long token = mInjector.binderClearCallingIdentity(); + try { + synchronized (mLock) { + disconnectServiceOnUserLocked(packageName, userId, actionForLog); + } + } finally { + mInjector.binderRestoreCallingIdentity(token); + } + } + + /** + * Stop all admin services on a given user. + */ + public void stopServicesForUser(int userId, @NonNull String actionForLog) { final long token = mInjector.binderClearCallingIdentity(); try { synchronized (mLock) { @@ -165,34 +188,68 @@ public class DeviceAdminServiceController { } @GuardedBy("mLock") - private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { - final DevicePolicyServiceConnection conn = mConnections.get(userId); + private void disconnectServiceOnUserLocked( + @NonNull String packageName, int userId, @NonNull String actionForLog) { + final DevicePolicyServiceConnection conn = mConnections.contains(userId) + ? mConnections.get(userId).get(packageName) : null; if (conn != null) { if (DEBUG) { - Slogf.d(TAG, "Stopping service for u%d if already running for %s.", userId, + Slogf.d(TAG, "Stopping service for package %s on u%d if already running for %s.", + packageName, + userId, actionForLog); } conn.unbind(); - mConnections.remove(userId); + mConnections.get(userId).remove(packageName); + if (mConnections.get(userId).isEmpty()) { + mConnections.remove(userId); + } } } + @GuardedBy("mLock") + private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { + if (!mConnections.contains(userId)) { + return; + } + for (String packageName : mConnections.get(userId).keySet()) { + DevicePolicyServiceConnection conn = mConnections.get(userId).get(packageName); + if (DEBUG) { + Slogf.d(TAG, + "Stopping service for package %s on u%d if already running for %s.", + packageName, + userId, + actionForLog); + } + conn.unbind(); + } + mConnections.remove(userId); + } + /** dump content */ public void dump(IndentingPrintWriter pw) { synchronized (mLock) { if (mConnections.size() == 0) { return; } - pw.println("Owner Services:"); + pw.println("Admin Services:"); pw.increaseIndent(); for (int i = 0; i < mConnections.size(); i++) { final int userId = mConnections.keyAt(i); - pw.print("User: "); pw.println(userId); + pw.print("User: "); + pw.println(userId); + for (String packageName : mConnections.get(userId).keySet()) { + pw.increaseIndent(); + pw.print("Package: "); + pw.println(packageName); - final DevicePolicyServiceConnection con = mConnections.valueAt(i); - pw.increaseIndent(); - con.dump("", pw); - pw.decreaseIndent(); + final DevicePolicyServiceConnection con = mConnections.valueAt(i) + .get(packageName); + pw.increaseIndent(); + con.dump("", pw); + pw.decreaseIndent(); + pw.decreaseIndent(); + } } pw.decreaseIndent(); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index a795c3fcfb88d..7ec809fb737b6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -28,6 +28,7 @@ import static android.app.admin.PolicyUpdatesReceiver.POLICY_SET_RESULT_SUCCESS; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; import android.app.admin.PolicyUpdatesReceiver; import android.app.admin.TargetUser; import android.content.Context; @@ -57,7 +58,9 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -86,11 +89,22 @@ final class DevicePolicyEngine { */ private final Map> mGlobalPolicies; - DevicePolicyEngine(@NonNull Context context) { + /** + * Map containing the current set of admins in each user with active policies. + */ + private final SparseArray> mEnforcingAdmins; + + private final DeviceAdminServiceController mDeviceAdminServiceController; + + DevicePolicyEngine( + @NonNull Context context, + @NonNull DeviceAdminServiceController deviceAdminServiceController) { mContext = Objects.requireNonNull(context); + mDeviceAdminServiceController = Objects.requireNonNull(deviceAdminServiceController); mUserManager = mContext.getSystemService(UserManager.class); mLocalPolicies = new SparseArray<>(); mGlobalPolicies = new HashMap<>(); + mEnforcingAdmins = new SparseArray<>(); } // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values @@ -137,6 +151,8 @@ final class DevicePolicyEngine { REASON_CONFLICTING_ADMIN_POLICY, userId); + updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin); + write(); } } @@ -187,6 +203,8 @@ final class DevicePolicyEngine { removeLocalPolicyStateLocked(policyDefinition, userId); } + updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin); + write(); } } @@ -257,6 +275,8 @@ final class DevicePolicyEngine { REASON_CONFLICTING_ADMIN_POLICY, UserHandle.USER_ALL); + updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin); + write(); } } @@ -298,6 +318,8 @@ final class DevicePolicyEngine { removeGlobalPolicyStateLocked(policyDefinition); } + updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin); + write(); } } @@ -580,6 +602,7 @@ final class DevicePolicyEngine { getTargetUser(admin.getUserId(), userId)); extras.putInt(EXTRA_POLICY_UPDATE_REASON_KEY, reason); intent.putExtras(extras); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); maybeSendIntentToAdminReceivers( intent, UserHandle.of(admin.getUserId()), receivers); @@ -622,6 +645,164 @@ final class DevicePolicyEngine { }); } + /** + * Starts/Stops the services that handle {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} + * in the enforcing admins for the given {@code userId}. + */ + private void updateDeviceAdminsServicesForUser( + int userId, boolean enable, @NonNull String actionForLog) { + if (!enable) { + mDeviceAdminServiceController.stopServicesForUser( + userId, actionForLog); + } else { + for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) { + // DPCs are handled separately in DPMS, no need to reestablish the connection here. + if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { + continue; + } + mDeviceAdminServiceController.startServiceForAdmin( + admin.getPackageName(), userId, actionForLog); + } + } + } + + /** + * Handles internal state related to a user getting started. + */ + void handleStartUser(int userId) { + updateDeviceAdminsServicesForUser( + userId, /* enable= */ true, /* actionForLog= */ "start-user"); + } + + /** + * Handles internal state related to a user getting started. + */ + void handleUnlockUser(int userId) { + updateDeviceAdminsServicesForUser( + userId, /* enable= */ true, /* actionForLog= */ "unlock-user"); + } + + /** + * Handles internal state related to a user getting stopped. + */ + void handleStopUser(int userId) { + updateDeviceAdminsServicesForUser( + userId, /* enable= */ false, /* actionForLog= */ "stop-user"); + } + + /** + * Handles internal state related to packages getting updated. + */ + void handlePackageChanged(@Nullable String updatedPackage, int userId) { + if (updatedPackage == null) { + return; + } + updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId); + } + + /** + * Reestablishes the service that handles + * {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} in the enforcing admin if the package + * was updated, as a package update results in the persistent connection getting reset. + */ + private void updateDeviceAdminServiceOnPackageChanged( + @NonNull String updatedPackage, int userId) { + for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) { + // DPCs are handled separately in DPMS, no need to reestablish the connection here. + if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { + continue; + } + if (updatedPackage.equals(admin.getPackageName())) { + mDeviceAdminServiceController.startServiceForAdmin( + updatedPackage, userId, /* actionForLog= */ "package-broadcast"); + } + } + } + + /** + * Called after an admin policy has been added to start binding to the admin if a connection + * was not already established. + */ + private void updateDeviceAdminServiceOnPolicyAddLocked(@NonNull EnforcingAdmin enforcingAdmin) { + int userId = enforcingAdmin.getUserId(); + + // A connection is established with DPCs as soon as they are provisioned, so no need to + // connect when a policy is set. + if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { + return; + } + if (mEnforcingAdmins.contains(userId) + && mEnforcingAdmins.get(userId).contains(enforcingAdmin)) { + return; + } + + if (!mEnforcingAdmins.contains(enforcingAdmin.getUserId())) { + mEnforcingAdmins.put(enforcingAdmin.getUserId(), new HashSet<>()); + } + mEnforcingAdmins.get(enforcingAdmin.getUserId()).add(enforcingAdmin); + + mDeviceAdminServiceController.startServiceForAdmin( + enforcingAdmin.getPackageName(), + userId, + /* actionForLog= */ "policy-added"); + } + + /** + * Called after an admin policy has been removed to stop binding to the admin if they no longer + * have any policies set. + */ + private void updateDeviceAdminServiceOnPolicyRemoveLocked( + @NonNull EnforcingAdmin enforcingAdmin) { + // TODO(b/263364434): centralise handling in one place. + // DPCs rely on a constant connection being established as soon as they are provisioned, + // so we shouldn't disconnect it even if they no longer have policies set. + if (enforcingAdmin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { + return; + } + if (doesAdminHavePolicies(enforcingAdmin)) { + return; + } + + int userId = enforcingAdmin.getUserId(); + + if (mEnforcingAdmins.contains(userId)) { + mEnforcingAdmins.get(userId).remove(enforcingAdmin); + if (mEnforcingAdmins.get(userId).isEmpty()) { + mEnforcingAdmins.remove(enforcingAdmin.getUserId()); + } + } + + mDeviceAdminServiceController.stopServiceForAdmin( + enforcingAdmin.getPackageName(), + userId, + /* actionForLog= */ "policy-removed"); + } + + private boolean doesAdminHavePolicies(@NonNull EnforcingAdmin enforcingAdmin) { + for (String policy : mGlobalPolicies.keySet()) { + PolicyState policyState = mGlobalPolicies.get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) { + return true; + } + } + for (int i = 0; i < mLocalPolicies.size(); i++) { + for (String policy : mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()) { + PolicyState policyState = mLocalPolicies.get( + mLocalPolicies.keyAt(i)).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) { + return true; + } + } + } + return false; + } + + @NonNull + private Set getEnforcingAdminsForUser(int userId) { + return mEnforcingAdmins.contains(userId) + ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + } + private void write() { Log.d(TAG, "Writing device policies to file."); new DevicePoliciesReaderWriter().writeToFileLocked(); @@ -649,6 +830,7 @@ final class DevicePolicyEngine { private static final String TAG_LOCAL_POLICY_ENTRY = "local-policy-entry"; private static final String TAG_GLOBAL_POLICY_ENTRY = "global-policy-entry"; private static final String TAG_ADMINS_POLICY_ENTRY = "admins-policy-entry"; + private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry"; private static final String ATTR_USER_ID = "user-id"; private static final String ATTR_POLICY_ID = "policy-id"; @@ -691,6 +873,7 @@ final class DevicePolicyEngine { void writeInner(TypedXmlSerializer serializer) throws IOException { writeLocalPoliciesInner(serializer); writeGlobalPoliciesInner(serializer); + writeEnforcingAdminsInner(serializer); } private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException { @@ -731,6 +914,19 @@ final class DevicePolicyEngine { } } + private void writeEnforcingAdminsInner(TypedXmlSerializer serializer) throws IOException { + if (mEnforcingAdmins != null) { + for (int i = 0; i < mEnforcingAdmins.size(); i++) { + int userId = mEnforcingAdmins.keyAt(i); + for (EnforcingAdmin admin : mEnforcingAdmins.get(userId)) { + serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMINS_ENTRY); + admin.saveToXml(serializer); + serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMINS_ENTRY); + } + } + } + } + void readFromFileLocked() { if (!mFile.exists()) { Log.d(TAG, "" + mFile + " doesn't exist"); @@ -765,6 +961,9 @@ final class DevicePolicyEngine { case TAG_GLOBAL_POLICY_ENTRY: readGlobalPoliciesInner(parser); break; + case TAG_ENFORCING_ADMINS_ENTRY: + readEnforcingAdminsInner(parser); + break; default: Log.e(TAG, "Unknown tag " + tag); } @@ -800,6 +999,15 @@ final class DevicePolicyEngine { } } + private void readEnforcingAdminsInner(TypedXmlPullParser parser) + throws XmlPullParserException { + EnforcingAdmin admin = EnforcingAdmin.readFromXml(parser); + if (!mEnforcingAdmins.contains(admin.getUserId())) { + mEnforcingAdmins.put(admin.getUserId(), new HashSet<>()); + } + mEnforcingAdmins.get(admin.getUserId()).add(admin); + } + @Nullable private PolicyState parseAdminsPolicy(TypedXmlPullParser parser) throws XmlPullParserException, IOException { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a79fd894bed91..2b92b227844e5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1276,6 +1276,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { && (owner.getPackageName().equals(packageName))) { startOwnerService(userHandle, "package-broadcast"); } + if (isCoexistenceFlagEnabled()) { + mDevicePolicyEngine.handlePackageChanged(packageName, userHandle); + } // Persist updates if the removed package was an admin or delegate. if (removedAdmin || removedDelegate) { @@ -1919,7 +1922,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUserData = new SparseArray<>(); mOwners = makeOwners(injector, pathProvider); - mDevicePolicyEngine = new DevicePolicyEngine(mContext); + mDevicePolicyEngine = new DevicePolicyEngine(mContext, mDeviceAdminServiceController); if (!mHasFeature) { // Skip the rest of the initialization @@ -3286,6 +3289,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs); startOwnerService(userId, "start-user"); + if (isCoexistenceFlagEnabled()) { + mDevicePolicyEngine.handleStartUser(userId); + } } void pushUserControlDisabledPackagesLocked(int userId) { @@ -3309,6 +3315,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override void handleUnlockUser(int userId) { startOwnerService(userId, "unlock-user"); + if (isCoexistenceFlagEnabled()) { + mDevicePolicyEngine.handleUnlockUser(userId); + } } @Override @@ -3319,22 +3328,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override void handleStopUser(int userId) { updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT)); - stopOwnerService(userId, "stop-user"); + mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user"); + if (isCoexistenceFlagEnabled()) { + mDevicePolicyEngine.handleStopUser(userId); + } } private void startOwnerService(int userId, String actionForLog) { final ComponentName owner = getOwnerComponent(userId); if (owner != null) { - mDeviceAdminServiceController.startServiceForOwner( + mDeviceAdminServiceController.startServiceForAdmin( owner.getPackageName(), userId, actionForLog); invalidateBinderCaches(); } } - private void stopOwnerService(int userId, String actionForLog) { - mDeviceAdminServiceController.stopServiceForOwner(userId, actionForLog); - } - private void cleanUpOldUsers() { // This is needed in case the broadcast {@link Intent.ACTION_USER_REMOVED} was not handled // before reboot @@ -8608,7 +8616,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO Send to system too? sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId); }); - mDeviceAdminServiceController.startServiceForOwner( + mDeviceAdminServiceController.startServiceForAdmin( admin.getPackageName(), userId, "set-device-owner"); Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); @@ -8973,7 +8981,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) { - mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-device-owner"); + String ownersPackage = mOwners.getDeviceOwnerPackageName(); + if (ownersPackage != null) { + mDeviceAdminServiceController.stopServiceForAdmin( + ownersPackage, userId, "clear-device-owner"); + } if (admin != null) { admin.disableCamera = false; @@ -9085,7 +9097,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED, userHandle); }); - mDeviceAdminServiceController.startServiceForOwner( + mDeviceAdminServiceController.startServiceForAdmin( who.getPackageName(), userHandle, "set-profile-owner"); return true; } @@ -9134,7 +9146,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) { - mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-profile-owner"); + String ownersPackage = mOwners.getProfileOwnerPackage(userId); + if (ownersPackage != null) { + mDeviceAdminServiceController.stopServiceForAdmin( + ownersPackage, userId, "clear-profile-owner"); + } if (admin != null) { admin.disableCamera = false; @@ -16753,7 +16769,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.transferProfileOwner(target, profileOwnerUserId); Slogf.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId); mOwners.writeProfileOwner(profileOwnerUserId); - mDeviceAdminServiceController.startServiceForOwner( + mDeviceAdminServiceController.startServiceForAdmin( target.getPackageName(), profileOwnerUserId, "transfer-profile-owner"); } @@ -16765,7 +16781,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.transferDeviceOwnership(target); Slogf.i(LOG_TAG, "Device owner set: " + target + " on user " + userId); mOwners.writeDeviceOwner(); - mDeviceAdminServiceController.startServiceForOwner( + mDeviceAdminServiceController.startServiceForAdmin( target.getPackageName(), userId, "transfer-device-owner"); } @@ -17675,11 +17691,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES); if (isCoexistenceEnabled(caller)) { - if (packages.isEmpty()) { - removeUserControlDisabledPackages(caller); - } else { - addUserControlDisabledPackages(caller, new HashSet<>(packages)); - } + Binder.withCleanCallingIdentity(() -> { + if (packages.isEmpty()) { + removeUserControlDisabledPackages(caller); + } else { + addUserControlDisabledPackages(caller, new HashSet<>(packages)); + } + }); } else { synchronized (getLockObject()) { ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java index 54d3f54b05ee4..db0a623c953b5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java @@ -159,6 +159,13 @@ final class PolicyState { return mCurrentResolvedPolicy; } + @Override + public String toString() { + return "PolicyState { mPolicyDefinition= " + mPolicyDefinition + ", mPoliciesSetByAdmins= " + + mPoliciesSetByAdmins + ", mCurrentResolvedPolicy= " + mCurrentResolvedPolicy + + " }"; + } + void saveToXml(TypedXmlSerializer serializer) throws IOException { mPolicyDefinition.saveToXml(serializer);