blob: 0c1c406dd7e2d4e63bfe3a8316afd5e5cbf6932a [file] [log] [blame]
/*
* Copyright (C) 2014 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 static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static com.android.server.devicepolicy.DeviceStateCacheImpl.NO_DEVICE_OWNER;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManagerInternal;
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.devicepolicy.OwnersData.OwnerInfo;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Stores and restores state for the Device and Profile owners and related device-wide information.
* By definition there can be only one device owner, but there may be a profile owner for each user.
*
* <p>This class is thread safe, so individual methods can safely be called without locking.
* However, caller must still synchronize on their side to ensure integrity between multiple calls.
*/
class Owners {
private static final String TAG = "DevicePolicyManagerService";
private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
private final UserManager mUserManager;
private final UserManagerInternal mUserManagerInternal;
private final PackageManagerInternal mPackageManagerInternal;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
private final DeviceStateCacheImpl mDeviceStateCache;
@GuardedBy("mData")
private final OwnersData mData;
private boolean mSystemReady;
@VisibleForTesting
Owners(UserManager userManager,
UserManagerInternal userManagerInternal,
PackageManagerInternal packageManagerInternal,
ActivityTaskManagerInternal activityTaskManagerInternal,
ActivityManagerInternal activityManagerInternal,
DeviceStateCacheImpl deviceStateCache,
PolicyPathProvider pathProvider) {
mUserManager = userManager;
mUserManagerInternal = userManagerInternal;
mPackageManagerInternal = packageManagerInternal;
mActivityTaskManagerInternal = activityTaskManagerInternal;
mActivityManagerInternal = activityManagerInternal;
mDeviceStateCache = deviceStateCache;
mData = new OwnersData(pathProvider);
}
/**
* Load configuration from the disk.
*/
void load() {
synchronized (mData) {
int[] usersIds =
mUserManager.getAliveUsers().stream().mapToInt(u -> u.id).toArray();
mData.load(usersIds);
// TODO(b/258213147): Remove
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT)) {
if (hasDeviceOwner()) {
int deviceOwnerType = mData.mDeviceOwnerTypes.getOrDefault(
mData.mDeviceOwner.packageName,
/* defaultValue= */ DEVICE_OWNER_TYPE_DEFAULT);
mDeviceStateCache.setDeviceOwnerType(deviceOwnerType);
} else {
mDeviceStateCache.setDeviceOwnerType(NO_DEVICE_OWNER);
}
for (int userId : usersIds) {
mDeviceStateCache.setHasProfileOwner(userId, hasProfileOwner(userId));
}
} else {
mUserManagerInternal.setDeviceManaged(hasDeviceOwner());
for (int userId : usersIds) {
mUserManagerInternal.setUserManaged(userId, hasProfileOwner(userId));
}
}
notifyChangeLocked();
pushDeviceOwnerUidToActivityTaskManagerLocked();
pushProfileOwnerUidsToActivityTaskManagerLocked();
}
}
// Notify interested parties that things have changed. This does not notify the
// ActivityTaskManager.
@GuardedBy("mData")
private void notifyChangeLocked() {
pushToDevicePolicyManager();
pushToPackageManagerLocked();
pushToActivityManagerLocked();
pushToAppOpsLocked();
}
private void pushToDevicePolicyManager() {
// Not every change here must invalidate the DPM caches, but there is no harm in
// invalidating the caches unnecessarily, provided the invalidation is infrequent.
DevicePolicyManagerService.invalidateBinderCaches();
}
@GuardedBy("mData")
private void pushToPackageManagerLocked() {
final SparseArray<String> po = new SparseArray<>();
for (int i = mData.mProfileOwners.size() - 1; i >= 0; i--) {
po.put(mData.mProfileOwners.keyAt(i), mData.mProfileOwners.valueAt(i).packageName);
}
final String doPackage = mData.mDeviceOwner != null ? mData.mDeviceOwner.packageName : null;
mPackageManagerInternal.setDeviceAndProfileOwnerPackages(
mData.mDeviceOwnerUserId, doPackage, po);
}
@GuardedBy("mData")
private void pushDeviceOwnerUidToActivityTaskManagerLocked() {
mActivityTaskManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked());
}
@GuardedBy("mData")
private void pushProfileOwnerUidsToActivityTaskManagerLocked() {
mActivityTaskManagerInternal.setProfileOwnerUids(getProfileOwnerUidsLocked());
}
@GuardedBy("mData")
private void pushToActivityManagerLocked() {
mActivityManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked());
final ArraySet<Integer> profileOwners = new ArraySet<>();
for (int poi = mData.mProfileOwners.size() - 1; poi >= 0; poi--) {
final int userId = mData.mProfileOwners.keyAt(poi);
final int profileOwnerUid = mPackageManagerInternal.getPackageUid(
mData.mProfileOwners.valueAt(poi).packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
userId);
if (profileOwnerUid >= 0) {
profileOwners.add(profileOwnerUid);
}
}
mActivityManagerInternal.setProfileOwnerUid(profileOwners);
}
@GuardedBy("mData")
int getDeviceOwnerUidLocked() {
if (mData.mDeviceOwner != null) {
return mPackageManagerInternal.getPackageUid(mData.mDeviceOwner.packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
mData.mDeviceOwnerUserId);
} else {
return Process.INVALID_UID;
}
}
@GuardedBy("mData")
Set<Integer> getProfileOwnerUidsLocked() {
return mData.mProfileOwners.keySet();
}
String getDeviceOwnerPackageName() {
synchronized (mData) {
return mData.mDeviceOwner != null ? mData.mDeviceOwner.packageName : null;
}
}
int getDeviceOwnerUserId() {
synchronized (mData) {
return mData.mDeviceOwnerUserId;
}
}
@Nullable
Pair<Integer, ComponentName> getDeviceOwnerUserIdAndComponent() {
synchronized (mData) {
if (mData.mDeviceOwner == null) {
return null;
} else {
return Pair.create(mData.mDeviceOwnerUserId, mData.mDeviceOwner.admin);
}
}
}
ComponentName getDeviceOwnerComponent() {
synchronized (mData) {
return mData.mDeviceOwner != null ? mData.mDeviceOwner.admin : null;
}
}
String getDeviceOwnerRemoteBugreportUri() {
synchronized (mData) {
return mData.mDeviceOwner != null ? mData.mDeviceOwner.remoteBugreportUri : null;
}
}
String getDeviceOwnerRemoteBugreportHash() {
synchronized (mData) {
return mData.mDeviceOwner != null ? mData.mDeviceOwner.remoteBugreportHash : null;
}
}
void setDeviceOwner(ComponentName admin, int userId) {
if (userId < 0) {
Slog.e(TAG, "Invalid user id for device owner user: " + userId);
return;
}
synchronized (mData) {
// A device owner is allowed to access device identifiers. Even though this flag
// is not currently checked for device owner, it is set to true here so that it is
// semantically compatible with the meaning of this flag.
mData.mDeviceOwner = new OwnerInfo(admin, /* remoteBugreportUri =*/ null,
/* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ true);
mData.mDeviceOwnerUserId = userId;
// TODO(b/258213147): Remove
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT)) {
int deviceOwnerType = mData.mDeviceOwnerTypes.getOrDefault(
mData.mDeviceOwner.packageName,
/* defaultValue= */ DEVICE_OWNER_TYPE_DEFAULT);
mDeviceStateCache.setDeviceOwnerType(deviceOwnerType);
} else {
mUserManagerInternal.setDeviceManaged(true);
}
notifyChangeLocked();
pushDeviceOwnerUidToActivityTaskManagerLocked();
}
}
void clearDeviceOwner() {
synchronized (mData) {
mData.mDeviceOwnerTypes.remove(mData.mDeviceOwner.packageName);
mData.mDeviceOwner = null;
mData.mDeviceOwnerUserId = UserHandle.USER_NULL;
// TODO(b/258213147): Remove
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT)) {
mDeviceStateCache.setDeviceOwnerType(NO_DEVICE_OWNER);
} else {
mUserManagerInternal.setDeviceManaged(false);
}
notifyChangeLocked();
pushDeviceOwnerUidToActivityTaskManagerLocked();
}
}
void setProfileOwner(ComponentName admin, int userId) {
synchronized (mData) {
// For a newly set PO, there's no need for migration.
mData.mProfileOwners.put(userId, new OwnerInfo(admin,
/* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null,
/* isOrganizationOwnedDevice =*/ false));
// TODO(b/258213147): Remove
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT)) {
mDeviceStateCache.setHasProfileOwner(userId, true);
} else {
mUserManagerInternal.setUserManaged(userId, true);
}
notifyChangeLocked();
pushProfileOwnerUidsToActivityTaskManagerLocked();
}
}
void removeProfileOwner(int userId) {
synchronized (mData) {
mData.mProfileOwners.remove(userId);
// TODO(b/258213147): Remove
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG,
DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT)) {
mDeviceStateCache.setHasProfileOwner(userId, false);
} else {
mUserManagerInternal.setUserManaged(userId, false);
}
notifyChangeLocked();
pushProfileOwnerUidsToActivityTaskManagerLocked();
}
}
void transferProfileOwner(ComponentName target, int userId) {
synchronized (mData) {
final OwnerInfo ownerInfo = mData.mProfileOwners.get(userId);
final OwnerInfo newOwnerInfo = new OwnerInfo(target,
ownerInfo.remoteBugreportUri, ownerInfo.remoteBugreportHash,
ownerInfo.isOrganizationOwnedDevice);
mData.mProfileOwners.put(userId, newOwnerInfo);
notifyChangeLocked();
pushProfileOwnerUidsToActivityTaskManagerLocked();
}
}
void transferDeviceOwnership(ComponentName target) {
synchronized (mData) {
Integer previousDeviceOwnerType = mData.mDeviceOwnerTypes.remove(
mData.mDeviceOwner.packageName);
mData.mDeviceOwner = new OwnerInfo(target,
mData.mDeviceOwner.remoteBugreportUri,
mData.mDeviceOwner.remoteBugreportHash,
mData.mDeviceOwner.isOrganizationOwnedDevice);
if (previousDeviceOwnerType != null) {
mData.mDeviceOwnerTypes.put(
mData.mDeviceOwner.packageName, previousDeviceOwnerType);
}
notifyChangeLocked();
pushDeviceOwnerUidToActivityTaskManagerLocked();
}
}
ComponentName getProfileOwnerComponent(int userId) {
synchronized (mData) {
OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
return profileOwner != null ? profileOwner.admin : null;
}
}
String getProfileOwnerPackage(int userId) {
synchronized (mData) {
OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
return profileOwner != null ? profileOwner.packageName : null;
}
}
/**
* Returns true if {@code userId} has a profile owner and that profile owner is on an
* organization-owned device, as indicated by the provisioning flow.
*/
boolean isProfileOwnerOfOrganizationOwnedDevice(int userId) {
synchronized (mData) {
OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
return profileOwner != null ? profileOwner.isOrganizationOwnedDevice : false;
}
}
Set<Integer> getProfileOwnerKeys() {
synchronized (mData) {
return mData.mProfileOwners.keySet();
}
}
List<OwnerShellData> listAllOwners() {
List<OwnerShellData> owners = new ArrayList<>();
synchronized (mData) {
if (mData.mDeviceOwner != null) {
owners.add(OwnerShellData.forDeviceOwner(mData.mDeviceOwnerUserId,
mData.mDeviceOwner.admin));
}
for (int i = 0; i < mData.mProfileOwners.size(); i++) {
int userId = mData.mProfileOwners.keyAt(i);
OwnerInfo info = mData.mProfileOwners.valueAt(i);
owners.add(OwnerShellData.forUserProfileOwner(userId, info.admin));
}
}
return owners;
}
SystemUpdatePolicy getSystemUpdatePolicy() {
synchronized (mData) {
return mData.mSystemUpdatePolicy;
}
}
void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) {
synchronized (mData) {
mData.mSystemUpdatePolicy = systemUpdatePolicy;
}
}
void clearSystemUpdatePolicy() {
synchronized (mData) {
mData.mSystemUpdatePolicy = null;
}
}
Pair<LocalDate, LocalDate> getSystemUpdateFreezePeriodRecord() {
synchronized (mData) {
return new Pair<>(mData.mSystemUpdateFreezeStart,
mData.mSystemUpdateFreezeEnd);
}
}
String getSystemUpdateFreezePeriodRecordAsString() {
synchronized (mData) {
return mData.getSystemUpdateFreezePeriodRecordAsString();
}
}
/**
* Returns {@code true} if the freeze period record is changed, {@code false} otherwise.
*/
boolean setSystemUpdateFreezePeriodRecord(LocalDate start, LocalDate end) {
boolean changed = false;
synchronized (mData) {
if (!Objects.equals(mData.mSystemUpdateFreezeStart, start)) {
mData.mSystemUpdateFreezeStart = start;
changed = true;
}
if (!Objects.equals(mData.mSystemUpdateFreezeEnd, end)) {
mData.mSystemUpdateFreezeEnd = end;
changed = true;
}
}
return changed;
}
boolean hasDeviceOwner() {
synchronized (mData) {
return mData.mDeviceOwner != null;
}
}
boolean isDeviceOwnerUserId(int userId) {
synchronized (mData) {
return mData.mDeviceOwner != null && mData.mDeviceOwnerUserId == userId;
}
}
boolean isDefaultDeviceOwnerUserId(int userId) {
synchronized (mData) {
return mData.mDeviceOwner != null
&& mData.mDeviceOwnerUserId == userId
&& getDeviceOwnerType(getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT;
}
}
boolean isFinancedDeviceOwnerUserId(int userId) {
synchronized (mData) {
return mData.mDeviceOwner != null
&& mData.mDeviceOwnerUserId == userId
&& getDeviceOwnerType(getDeviceOwnerPackageName())
== DEVICE_OWNER_TYPE_FINANCED;
}
}
boolean hasProfileOwner(int userId) {
synchronized (mData) {
return getProfileOwnerComponent(userId) != null;
}
}
/** Sets the remote bugreport uri and hash, and also writes to the file. */
void setDeviceOwnerRemoteBugreportUriAndHash(String remoteBugreportUri,
String remoteBugreportHash) {
synchronized (mData) {
if (mData.mDeviceOwner != null) {
mData.mDeviceOwner.remoteBugreportUri = remoteBugreportUri;
mData.mDeviceOwner.remoteBugreportHash = remoteBugreportHash;
}
writeDeviceOwner();
}
}
/** Set whether the profile owner manages an organization-owned device, then write to file. */
void setProfileOwnerOfOrganizationOwnedDevice(int userId, boolean isOrganizationOwnedDevice) {
synchronized (mData) {
OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
if (profileOwner != null) {
profileOwner.isOrganizationOwnedDevice = isOrganizationOwnedDevice;
} else {
Slog.e(TAG, String.format(
"No profile owner for user %d to set org-owned flag.", userId));
}
writeProfileOwner(userId);
}
}
void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType,
boolean isAdminTestOnly) {
synchronized (mData) {
if (!hasDeviceOwner()) {
Slog.e(TAG, "Attempting to set a device owner type when there is no device owner");
return;
} else if (!isAdminTestOnly && isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
Slog.e(TAG, "Setting the device owner type more than once is only allowed"
+ " for test only admins");
return;
}
mData.mDeviceOwnerTypes.put(packageName, deviceOwnerType);
writeDeviceOwner();
}
}
@DeviceOwnerType
int getDeviceOwnerType(String packageName) {
synchronized (mData) {
if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
return mData.mDeviceOwnerTypes.get(packageName);
}
return DEVICE_OWNER_TYPE_DEFAULT;
}
}
boolean isDeviceOwnerTypeSetForDeviceOwner(String packageName) {
synchronized (mData) {
return !mData.mDeviceOwnerTypes.isEmpty()
&& mData.mDeviceOwnerTypes.containsKey(packageName);
}
}
void writeDeviceOwner() {
synchronized (mData) {
pushToDevicePolicyManager();
mData.writeDeviceOwner();
}
}
void writeProfileOwner(int userId) {
synchronized (mData) {
pushToDevicePolicyManager();
mData.writeProfileOwner(userId);
}
}
/**
* Saves the given {@link SystemUpdateInfo} if it is different from the existing one, or if
* none exists.
*
* @return Whether the saved system update information has changed.
*/
boolean saveSystemUpdateInfo(@Nullable SystemUpdateInfo newInfo) {
synchronized (mData) {
// Check if we already have the same update information.
if (Objects.equals(newInfo, mData.mSystemUpdateInfo)) {
return false;
}
mData.mSystemUpdateInfo = newInfo;
mData.writeDeviceOwner();
return true;
}
}
@Nullable
public SystemUpdateInfo getSystemUpdateInfo() {
synchronized (mData) {
return mData.mSystemUpdateInfo;
}
}
void markMigrationToPolicyEngine() {
synchronized (mData) {
mData.mMigratedToPolicyEngine = true;
mData.writeDeviceOwner();
}
}
boolean isMigratedToPolicyEngine() {
synchronized (mData) {
return mData.mMigratedToPolicyEngine;
}
}
@GuardedBy("mData")
void pushToAppOpsLocked() {
if (!mSystemReady) {
return;
}
final long ident = Binder.clearCallingIdentity();
try {
final SparseIntArray owners = new SparseIntArray();
if (mData.mDeviceOwner != null) {
final int uid = getDeviceOwnerUidLocked();
if (uid >= 0) {
owners.put(mData.mDeviceOwnerUserId, uid);
}
}
if (mData.mProfileOwners != null) {
for (int poi = mData.mProfileOwners.size() - 1; poi >= 0; poi--) {
final int uid = mPackageManagerInternal.getPackageUid(
mData.mProfileOwners.valueAt(poi).packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
mData.mProfileOwners.keyAt(poi));
if (uid >= 0) {
owners.put(mData.mProfileOwners.keyAt(poi), uid);
}
}
}
AppOpsManagerInternal appops = LocalServices.getService(AppOpsManagerInternal.class);
if (appops != null) {
appops.setDeviceAndProfileOwners(owners.size() > 0 ? owners : null);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
public void systemReady() {
synchronized (mData) {
mSystemReady = true;
pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
public void dump(IndentingPrintWriter pw) {
synchronized (mData) {
mData.dump(pw);
}
}
@VisibleForTesting
File getDeviceOwnerFile() {
return mData.getDeviceOwnerFile();
}
@VisibleForTesting
File getProfileOwnerFile(int userId) {
return mData.getProfileOwnerFile(userId);
}
}