Merge changes from topic "protectedPackages"
* changes: Allow non-DPC admins to use DeviceAdminService Give precedence to protectedPackages set locally Resolve policies that could be set locally and globally
This commit is contained in:
committed by
Android (Google) Code Review
commit
a3bf231faf
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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;
|
||||
|
||||
@@ -4005,6 +4005,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
|
||||
|
||||
@@ -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;
|
||||
*
|
||||
* <p>When publishing your {@code PolicyUpdatesReceiver} subclass as a receiver, it must
|
||||
* require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.
|
||||
*
|
||||
* <p>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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -375,10 +375,15 @@ public abstract class PackageManagerInternal {
|
||||
int deviceOwnerUserId, String deviceOwner, SparseArray<String> 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.
|
||||
*
|
||||
* <p> 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<String> packageNames);
|
||||
@UserIdInt int userId, @Nullable List<String> packageNames);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
|
||||
|
||||
@@ -354,7 +354,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
|
||||
@Override
|
||||
@Deprecated
|
||||
public final void setOwnerProtectedPackages(
|
||||
@UserIdInt int userId, @NonNull List<String> packageNames) {
|
||||
@UserIdInt int userId, @Nullable List<String> packageNames) {
|
||||
getProtectedPackages().setOwnerProtectedPackages(userId, packageNames);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> packageNames) {
|
||||
if (packageNames.isEmpty()) {
|
||||
@UserIdInt int userId, @Nullable List<String> 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}.
|
||||
|
||||
@@ -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<DevicePolicyServiceConnection> mConnections = new SparseArray<>();
|
||||
private final SparseArray<Map<String, DevicePolicyServiceConnection>> 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<IDeviceAdminService> 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();
|
||||
}
|
||||
|
||||
@@ -28,15 +28,19 @@ 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;
|
||||
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;
|
||||
@@ -54,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;
|
||||
@@ -68,6 +74,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();
|
||||
|
||||
@@ -81,20 +89,30 @@ final class DevicePolicyEngine {
|
||||
*/
|
||||
private final Map<String, PolicyState<?>> mGlobalPolicies;
|
||||
|
||||
DevicePolicyEngine(@NonNull Context context) {
|
||||
/**
|
||||
* Map containing the current set of admins in each user with active policies.
|
||||
*/
|
||||
private final SparseArray<Set<EnforcingAdmin>> 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
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
<V> boolean setLocalPolicy(
|
||||
<V> void setLocalPolicy(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin,
|
||||
@NonNull V value,
|
||||
@@ -105,45 +123,129 @@ final class DevicePolicyEngine {
|
||||
Objects.requireNonNull(value);
|
||||
|
||||
synchronized (mLock) {
|
||||
PolicyState<V> policyState = getLocalPolicyStateLocked(policyDefinition, userId);
|
||||
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
|
||||
|
||||
boolean policyChanged = policyState.setPolicy(enforcingAdmin, value);
|
||||
boolean hasGlobalPolicies = hasGlobalPolicyLocked(policyDefinition);
|
||||
boolean policyChanged;
|
||||
if (hasGlobalPolicies) {
|
||||
PolicyState<V> 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);
|
||||
|
||||
updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
|
||||
|
||||
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}.
|
||||
*/
|
||||
<V> void removeLocalPolicy(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin,
|
||||
int userId) {
|
||||
Objects.requireNonNull(policyDefinition);
|
||||
Objects.requireNonNull(enforcingAdmin);
|
||||
|
||||
synchronized (mLock) {
|
||||
if (!hasLocalPolicyLocked(policyDefinition, userId)) {
|
||||
return;
|
||||
}
|
||||
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
|
||||
|
||||
boolean policyChanged;
|
||||
if (hasGlobalPolicyLocked(policyDefinition)) {
|
||||
PolicyState<V> 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);
|
||||
}
|
||||
|
||||
updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin);
|
||||
|
||||
write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the new policy and notifies relevant admins.
|
||||
*/
|
||||
private <V> void onLocalPolicyChanged(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin,
|
||||
int userId) {
|
||||
|
||||
PolicyState<V> 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<V> 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.
|
||||
*
|
||||
*/
|
||||
<V> boolean setGlobalPolicy(
|
||||
<V> void setGlobalPolicy(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin,
|
||||
@NonNull V value) {
|
||||
@@ -153,77 +255,29 @@ final class DevicePolicyEngine {
|
||||
Objects.requireNonNull(value);
|
||||
|
||||
synchronized (mLock) {
|
||||
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
|
||||
PolicyState<V> 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);
|
||||
|
||||
updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
|
||||
|
||||
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.
|
||||
*
|
||||
*/
|
||||
<V> boolean removeLocalPolicy(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin,
|
||||
int userId) {
|
||||
|
||||
Objects.requireNonNull(policyDefinition);
|
||||
Objects.requireNonNull(enforcingAdmin);
|
||||
|
||||
synchronized (mLock) {
|
||||
PolicyState<V> 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 +285,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.
|
||||
*
|
||||
*/
|
||||
<V> boolean removeGlobalPolicy(
|
||||
<V> void removeGlobalPolicy(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin) {
|
||||
|
||||
@@ -246,61 +298,170 @@ 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);
|
||||
}
|
||||
|
||||
updateDeviceAdminServiceOnPolicyRemoveLocked(enforcingAdmin);
|
||||
|
||||
write();
|
||||
return policyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves policies set by all admins for the provided {@code policyDefinition}.
|
||||
*
|
||||
* Enforces the new policy globally and notifies relevant admins.
|
||||
*/
|
||||
<V> PolicyState<V> getLocalPolicy(@NonNull PolicyDefinition<V> policyDefinition, int userId) {
|
||||
private <V> void onGlobalPolicyChanged(
|
||||
@NonNull PolicyDefinition<V> policyDefinition,
|
||||
@NonNull EnforcingAdmin enforcingAdmin) {
|
||||
PolicyState<V> 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).
|
||||
*
|
||||
* <p> A {@code null} policy value means the policy was removed
|
||||
*
|
||||
* <p>Returns {@code true} if the policy is enforced successfully on all users.
|
||||
*/
|
||||
private <V> boolean enforceGlobalPolicyOnUsersWithLocalPoliciesLocked(
|
||||
@NonNull PolicyDefinition<V> 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<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
|
||||
PolicyState<V> 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> V getResolvedPolicy(@NonNull PolicyDefinition<V> 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}.
|
||||
*/
|
||||
<V> PolicyState<V> getGlobalPolicy(@NonNull PolicyDefinition<V> policyDefinition) {
|
||||
@Nullable
|
||||
<V> V getLocalPolicySetByAdmin(
|
||||
@NonNull PolicyDefinition<V> 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 <V> boolean hasLocalPolicyLocked(PolicyDefinition<V> 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 <V> boolean hasGlobalPolicyLocked(PolicyDefinition<V> policyDefinition) {
|
||||
if (policyDefinition.isLocalOnlyPolicy()) {
|
||||
return false;
|
||||
}
|
||||
if (!mGlobalPolicies.containsKey(policyDefinition.getPolicyKey())) {
|
||||
return false;
|
||||
}
|
||||
return !mGlobalPolicies.get(policyDefinition.getPolicyKey()).getPoliciesSetByAdmins()
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <V> PolicyState<V> getLocalPolicyStateLocked(
|
||||
PolicyDefinition<V> 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 +474,19 @@ final class DevicePolicyEngine {
|
||||
return getPolicyState(mLocalPolicies.get(userId), policyDefinition);
|
||||
}
|
||||
|
||||
private <V> void removeLocalPolicyStateLocked(
|
||||
PolicyDefinition<V> policyDefinition, int userId) {
|
||||
if (!mLocalPolicies.contains(userId)) {
|
||||
return;
|
||||
}
|
||||
mLocalPolicies.get(userId).remove(policyDefinition.getPolicyKey());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <V> PolicyState<V> getGlobalPolicyStateLocked(PolicyDefinition<V> 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 +496,10 @@ final class DevicePolicyEngine {
|
||||
return getPolicyState(mGlobalPolicies, policyDefinition);
|
||||
}
|
||||
|
||||
private <V> void removeGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) {
|
||||
mGlobalPolicies.remove(policyDefinition.getPolicyKey());
|
||||
}
|
||||
|
||||
private static <V> PolicyState<V> getPolicyState(
|
||||
Map<String, PolicyState<?>> policies, PolicyDefinition<V> policyDefinition) {
|
||||
try {
|
||||
@@ -344,14 +517,14 @@ final class DevicePolicyEngine {
|
||||
|
||||
private <V> void enforcePolicy(
|
||||
PolicyDefinition<V> 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 <V> void sendPolicyResultToAdmin(
|
||||
EnforcingAdmin admin, PolicyDefinition<V> 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 +540,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 +554,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 <V> void sendPolicyChangedToAdmins(
|
||||
Set<EnforcingAdmin> admins, EnforcingAdmin callingAdmin,
|
||||
Set<EnforcingAdmin> admins,
|
||||
EnforcingAdmin callingAdmin,
|
||||
PolicyDefinition<V> 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 <V> void maybeSendOnPolicyChanged(
|
||||
EnforcingAdmin admin, PolicyDefinition<V> policyDefinition, int reason,
|
||||
int targetUserId) {
|
||||
int userId) {
|
||||
Intent intent = new Intent(PolicyUpdatesReceiver.ACTION_DEVICE_POLICY_CHANGED);
|
||||
intent.setPackage(admin.getPackageName());
|
||||
|
||||
@@ -418,14 +593,17 @@ 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);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
|
||||
maybeSendIntentToAdminReceivers(
|
||||
intent, UserHandle.of(admin.getUserId()), receivers);
|
||||
}
|
||||
@@ -447,6 +625,184 @@ 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;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<EnforcingAdmin> 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();
|
||||
@@ -474,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";
|
||||
|
||||
@@ -516,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 {
|
||||
@@ -556,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");
|
||||
@@ -590,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);
|
||||
}
|
||||
@@ -625,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 {
|
||||
|
||||
@@ -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) {
|
||||
@@ -3300,7 +3306,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
}
|
||||
|
||||
List<String> protectedPackages = (owner == null || owner.protectedPackages == null)
|
||||
? Collections.emptyList() : owner.protectedPackages;
|
||||
? null : owner.protectedPackages;
|
||||
mInjector.binderWithCleanCallingIdentity(() ->
|
||||
mInjector.getPackageManagerInternal().setOwnerProtectedPackages(
|
||||
targetUserId, protectedPackages));
|
||||
@@ -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;
|
||||
@@ -12644,9 +12660,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 +12706,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 +12736,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 +12771,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 +12811,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.
|
||||
@@ -16751,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");
|
||||
}
|
||||
|
||||
@@ -16763,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");
|
||||
}
|
||||
|
||||
@@ -17672,12 +17690,22 @@ 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)) {
|
||||
Binder.withCleanCallingIdentity(() -> {
|
||||
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 +17716,52 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
.write();
|
||||
}
|
||||
|
||||
private void addUserControlDisabledPackages(CallerIdentity caller, Set<String> 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<String> getUserControlDisabledPackages(ComponentName who) {
|
||||
Objects.requireNonNull(who, "ComponentName is null");
|
||||
@@ -17696,10 +17770,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<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 + " }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,9 @@ final class IntegerUnion extends ResolutionMechanism<Integer> {
|
||||
}
|
||||
return unionOfPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IntegerUnion {}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,9 @@ final class MostRestrictive<V> extends ResolutionMechanism<V> {
|
||||
Map.Entry<EnforcingAdmin, V> policy = adminPolicies.entrySet().stream().findFirst().get();
|
||||
return policy.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MostRestrictive { mMostToLeastRestrictive= " + mMostToLeastRestrictive + " }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<V> {
|
||||
private static final int POLICY_FLAG_NONE = 0;
|
||||
@@ -98,10 +99,18 @@ final class PolicyDefinition<V> {
|
||||
PolicyEnforcerCallbacks.setLockTask(value, context, userId),
|
||||
new LockTaskPolicy.LockTaskPolicySerializer());
|
||||
|
||||
private static Map<String, PolicyDefinition<?>> sPolicyDefinitions = Map.of(
|
||||
static PolicyDefinition<Set<String>> USER_CONTROLLED_DISABLED_PACKAGES = new PolicyDefinition<>(
|
||||
DevicePolicyManager.USER_CONTROL_DISABLED_PACKAGES,
|
||||
new SetUnion<>(),
|
||||
(Set<String> value, Context context, Integer userId, Bundle args) ->
|
||||
PolicyEnforcerCallbacks.setUserControlDisabledPackages(value, userId),
|
||||
new SetPolicySerializer<>());
|
||||
|
||||
private static final Map<String, PolicyDefinition<?>> 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> {
|
||||
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 + " }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> packages, int userId) {
|
||||
Binder.withCleanCallingIdentity(() ->
|
||||
LocalServices.getService(PackageManagerInternal.class)
|
||||
.setOwnerProtectedPackages(
|
||||
userId,
|
||||
packages == null ? null : packages.stream().toList()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,32 @@ final class PolicyState<V> {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p> 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<EnforcingAdmin, V> 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<V> {
|
||||
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.
|
||||
*
|
||||
* <p> 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<EnforcingAdmin, V> 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.
|
||||
*
|
||||
* <p> 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<EnforcingAdmin, V> globalPoliciesSetByAdmins) {
|
||||
// Add global policies first then override with local policies for the same admin.
|
||||
LinkedHashMap<EnforcingAdmin, V> mergedPolicies =
|
||||
new LinkedHashMap<>(globalPoliciesSetByAdmins);
|
||||
mergedPolicies.putAll(mPoliciesSetByAdmins);
|
||||
|
||||
V resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies);
|
||||
boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy);
|
||||
mCurrentResolvedPolicy = resolvedPolicy;
|
||||
|
||||
return policyChanged;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
LinkedHashMap<EnforcingAdmin, V> getPoliciesSetByAdmins() {
|
||||
return mPoliciesSetByAdmins;
|
||||
return new LinkedHashMap<>(mPoliciesSetByAdmins);
|
||||
}
|
||||
|
||||
private boolean resolvePolicy() {
|
||||
@@ -95,6 +159,13 @@ final class PolicyState<V> {
|
||||
return mCurrentResolvedPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PolicyState { mPolicyDefinition= " + mPolicyDefinition + ", mPoliciesSetByAdmins= "
|
||||
+ mPoliciesSetByAdmins + ", mCurrentResolvedPolicy= " + mCurrentResolvedPolicy
|
||||
+ " }";
|
||||
}
|
||||
|
||||
void saveToXml(TypedXmlSerializer serializer) throws IOException {
|
||||
mPolicyDefinition.saveToXml(serializer);
|
||||
|
||||
|
||||
@@ -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<V> extends PolicySerializer<Set<V>> {
|
||||
|
||||
@Override
|
||||
void saveToXml(TypedXmlSerializer serializer, String attributeName, @NonNull Set<V> value)
|
||||
throws IOException {
|
||||
Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
Set<V> readFromXml(TypedXmlPullParser parser, String attributeName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -37,4 +37,9 @@ final class SetUnion<V> extends ResolutionMechanism<Set<V>> {
|
||||
}
|
||||
return unionOfPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SetUnion {}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,4 +49,10 @@ final class TopPriority<V> extends ResolutionMechanism<V> {
|
||||
Map.Entry<EnforcingAdmin, V> policy = adminPolicies.entrySet().stream().findFirst().get();
|
||||
return policy.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TopPriority { mHighestToLowestPriorityAuthorities= "
|
||||
+ mHighestToLowestPriorityAuthorities + " }";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user