假设有一个预装apk,想给它赋予deviowner权限,该怎么去处理呢?
我们知道,执行指令 adb shell dpm set-device-owner com.afwsamples.testdpc/.DeviceAdminReceiver,可以给应用程序 com.afwsamples.testdpc 赋予deviceowner权限。那么,我们是不是可以参考adb指令执行流程,去看看具体怎么给APP赋予deviceowner权限呢?
1. DevicePolicyManagerServiceShellCommand
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
private static final String CMD_LIST_OWNERS = "list-owners";
private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
private static final String CMD_SET_ACTIVE_ADMIN = "set-active-admin";
private static final String CMD_SET_DEVICE_OWNER = "set-device-owner";
private static final String CMD_SET_PROFILE_OWNER = "set-profile-owner";
private static final String CMD_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private static final String CMD_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
private static final String CMD_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String CMD_FORCE_SECURITY_LOGS = "force-security-logs";
private static final String CMD_MARK_PO_ON_ORG_OWNED_DEVICE =
"mark-profile-owner-on-organization-owned-device";
DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) {
mService = Objects.requireNonNull(service);
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
try (PrintWriter pw = getOutPrintWriter()) {
switch (cmd) {
case CMD_IS_SAFE_OPERATION:
return runIsSafeOperation(pw);
case CMD_IS_SAFE_OPERATION_BY_REASON:
return runIsSafeOperationByReason(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
case CMD_LIST_OWNERS:
return runListOwners(pw);
case CMD_LIST_POLICY_EXEMPT_APPS:
return runListPolicyExemptApps(pw);
case CMD_SET_ACTIVE_ADMIN:
return runSetActiveAdmin(pw);
case CMD_SET_DEVICE_OWNER:
return runSetDeviceOwner(pw);
case CMD_SET_PROFILE_OWNER:
return runSetProfileOwner(pw);
case CMD_REMOVE_ACTIVE_ADMIN:
return runRemoveActiveAdmin(pw);
case CMD_CLEAR_FREEZE_PERIOD_RECORD:
return runClearFreezePeriodRecord(pw);
case CMD_FORCE_NETWORK_LOGS:
return runForceNetworkLogs(pw);
case CMD_FORCE_SECURITY_LOGS:
return runForceSecurityLogs(pw);
case CMD_MARK_PO_ON_ORG_OWNED_DEVICE:
return runMarkProfileOwnerOnOrganizationOwnedDevice(pw);
default:
return onInvalidCommand(pw, cmd);
}
}
}
private int runSetDeviceOwner(PrintWriter pw) {
parseArgs();
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
if (!mService.setDeviceOwner(mComponent, mUserId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ !mSetDoOnly)) {
throw new RuntimeException(
"Can't set package " + mComponent + " as device owner.");
}
} catch (Exception e) {
// Need to remove the admin that we just added.
mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
throw e;
}
mService.setUserProvisioningState(
DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
pw.printf("Success: Device owner set to package %s\n", mComponent.flattenToShortString());
pw.printf("Active admin set to component %s\n", mComponent.flattenToShortString());
return 0;
}
我们看到adb shell dpm 相关的执行指令是在DevicePolicyManagerServiceShellCommand这个类里面实现的,onCommand方法里识别到cmd指令匹配"set-device-owner"字符串时,会触发runSetDeviceOwner方法,里面有几个关键方法:
2. setActiveAdmin
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
/**
* @param adminReceiver The admin to add
* @param refreshing true = update an active admin, no error
*/
@Override
public void setActiveAdmin(
ComponentName adminReceiver, boolean refreshing, int userHandle) {
if (!mHasFeature) {
return;
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
DevicePolicyData policy = getUserData(userHandle);
DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
/* throwForMissingPermission= */ true);
synchronized (getLockObject()) {
checkActiveAdminPrecondition(adminReceiver, info, policy);
mInjector.binderWithCleanCallingIdentity(() -> {
final ActiveAdmin existingAdmin
= getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (!refreshing && existingAdmin != null) {
throw new IllegalArgumentException("Admin is already added");
}
ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
newAdmin.testOnlyAdmin =
(existingAdmin != null) ? existingAdmin.testOnlyAdmin
: isPackageTestOnly(adminReceiver.getPackageName(), userHandle);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
final int N = policy.mAdminList.size();
for (int i=0; i < N; i++) {
ActiveAdmin oldAdmin = policy.mAdminList.get(i);
if (oldAdmin.info.getComponent().equals(adminReceiver)) {
replaceIndex = i;
break;
}
}
if (replaceIndex == -1) {
policy.mAdminList.add(newAdmin);
enableIfNecessary(info.getPackageName(), userHandle);
mUsageStatsManagerInternal.onActiveAdminAdded(
adminReceiver.getPackageName(), userHandle);
} else {
policy.mAdminList.set(replaceIndex, newAdmin);
}
saveSettingsLocked(userHandle);
sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
/* adminExtras= */ null, /* result= */ null);
});
}
}
3. setDeviceOwner
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@Override
public boolean setDeviceOwner(ComponentName admin, int userId,
boolean setProfileOwnerOnCurrentUserIfNecessary) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
+ " as device owner for user " + userId);
return false;
}
Preconditions.checkArgument(admin != null);
final CallerIdentity caller = getCallerIdentity();
calculateHasIncompatibleAccountsUnderAdb(caller);
boolean hasIncompatibleAccountsOrNonAdb =
!isAdb(caller) || hasIncompatibleAccountsOnAnyUser();
if (!hasIncompatibleAccountsOrNonAdb) {
synchronized (getLockObject()) {
if (!isAdminTestOnlyLocked(admin, userId) && hasAccountsOnAnyUser()) {
Slogf.w(LOG_TAG,
"Non test-only owner can't be installed with existing accounts.");
return false;
}
}
}
synchronized (getLockObject()) {
enforceCanSetDeviceOwnerLocked(caller, admin, userId, hasIncompatibleAccountsOrNonAdb);
Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId),
"Invalid component " + admin + " for device owner");
final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
Preconditions.checkArgument(activeAdmin != null && !getUserData(
userId).mRemovingAdmins.contains(admin), "Not active admin: " + admin);
// Shutting down backup manager service permanently.
toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
if (isAdb(caller)) {
// Log device owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_ADB)
.setAdmin(admin)
.setStrings(LOG_TAG_DEVICE_OWNER)
.write();
}
mOwners.setDeviceOwner(admin, userId);
mOwners.writeDeviceOwner();
setDeviceOwnershipSystemPropertyLocked();
//TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
// hard-coded default value setting.
if (isAdb(caller)) {
activeAdmin.mAdminCanGrantSensorsPermissions = true;
mPolicyCache.setAdminCanGrantSensorsPermissions(true);
saveSettingsLocked(userId);
}
mInjector.binderWithCleanCallingIdentity(() -> {
// Restrict adding a managed profile when a device owner is set on the device.
// That is to prevent the co-existence of a managed profile and a device owner
// on the same device.
// Instead, the device may be provisioned with an organization-owned managed
// profile, such that the admin on that managed profile has extended management
// capabilities that can affect the entire device (but not access private data
// on the primary profile).
if (isHeadlessFlagEnabled()) {
for (int u : mUserManagerInternal.getUserIds()) {
mUserManager.setUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
UserHandle.of(u));
// Restrict adding a clone profile when a device owner is set on the device.
// That is to prevent the co-existence of a clone profile and a device owner
// on the same device.
// CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
true,
UserHandle.of(u));
}
} else {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
true,
UserHandle.of(userId));
// Restrict adding a clone profile when a device owner is set on the device.
// That is to prevent the co-existence of a clone profile and a device owner
// on the same device.
// CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
true,
UserHandle.of(userId));
}
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
});
mDeviceAdminServiceController.startServiceForAdmin(
admin.getPackageName(), userId, "set-device-owner");
Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
}
if (setProfileOwnerOnCurrentUserIfNecessary
&& mInjector.userManagerIsHeadlessSystemUserMode()) {
int currentForegroundUser;
synchronized (getLockObject()) {
currentForegroundUser = getCurrentForegroundUserId();
}
Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+ " as profile owner on user " + currentForegroundUser);
// Sets profile owner on current foreground user since
// the human user will complete the DO setup workflow from there.
manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
/* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
/* showDisclaimer= */ false);
}
return true;
}
4. removeActiveAdmin
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@Override
public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) {
if (!mHasFeature) {
return;
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
final CallerIdentity caller = hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)
? getCallerIdentity() : getCallerIdentity(adminReceiver);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
ActiveAdmin admin;
synchronized (getLockObject()) {
admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
return;
}
// Active device/profile owners must remain active admins.
if (isDeviceOwner(adminReceiver, userHandle)
|| isProfileOwner(adminReceiver, userHandle)) {
Slogf.e(LOG_TAG, "Device/profile owner cannot be removed: component="
+ adminReceiver);
return;
}
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
}
if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
mDevicePolicyEngine.removePoliciesForAdmin(
EnforcingAdmin.createEnterpriseEnforcingAdmin(
adminReceiver, userHandle, admin));
}
}
5. setUserProvisioningState
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@Override
public void setUserProvisioningState(int newState, int userId) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set provisioning state " + newState + " for user "
+ userId);
return;
}
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
final CallerIdentity caller = getCallerIdentity();
final long id = mInjector.binderClearCallingIdentity();
try {
int deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
// NOTE: multiple if statements are nested below so it can log more info on error
if (userId != deviceOwnerUserId) {
boolean hasProfileOwner = mOwners.hasProfileOwner(userId);
if (!hasProfileOwner) {
int managedUserId = getManagedUserId(userId);
if (managedUserId < 0 && newState != STATE_USER_UNMANAGED) {
// No managed device, user or profile, so setting provisioning state makes
// no sense.
String error = "Not allowed to change provisioning state unless a "
+ "device or profile owner is set.";
Slogf.w(LOG_TAG, "setUserProvisioningState(newState=%d, userId=%d) failed: "
+ "deviceOwnerId=%d, hasProfileOwner=%b, managedUserId=%d, err=%s",
newState, userId, deviceOwnerUserId, hasProfileOwner,
managedUserId, error);
throw new IllegalStateException(error);
}
}
}
synchronized (getLockObject()) {
boolean transitionCheckNeeded = true;
// Calling identity/permission checks.
if (isAdb(caller)) {
// ADB shell can only move directly from un-managed to finalized as part of
// directly setting profile-owner or device-owner.
if (getUserProvisioningState(userId)
!= DevicePolicyManager.STATE_USER_UNMANAGED
|| newState != STATE_USER_SETUP_FINALIZED) {
throw new IllegalStateException("Not allowed to change provisioning state "
+ "unless current provisioning state is unmanaged, and new state"
+ "is finalized.");
}
transitionCheckNeeded = false;
}
final DevicePolicyData policyData = getUserData(userId);
if (transitionCheckNeeded) {
// Optional state transition check for non-ADB case.
checkUserProvisioningStateTransition(policyData.mUserProvisioningState,
newState);
}
policyData.mUserProvisioningState = newState;
saveSettingsLocked(userId);
}
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
}
6. 思路灵感
大段的代码,看了就头痛,怎么办?仔细捋一捋,执行adb指令的目的,不就是为了执行DevicePolicyManagerService里面的这几个方法吗?那么,我们要是能通过某种途径,去完成这几个方法的执行,是不是就可以了? 框架层很多service都会监听开机广播,DevicePolicyManagerService也不例外。
@VisibleForTesting
DevicePolicyManagerService(Injector injector, PolicyPathProvider pathProvider) {
.......
mContext = Objects.requireNonNull(injector.mContext);
.......
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
filter.addAction(ACTION_TURN_PROFILE_ON_NOTIFICATION);
filter.addAction(ACTION_PROFILE_OFF_DEADLINE);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
filter.addAction(ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(ACTION_MANAGED_PROFILE_AVAILABLE);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
.......
}
灵光一现,我们先定义好预装apk的广播接收器,然后在开机广播里去判断要不要执行adb指令里面需要用到的方法,需要的话就执行,不需要的话就不用做额外处理
// the adminReceiver for the pre-installed apk
private static final String mdmDeviceOwnerPackageName = "com.afwsamples.testdpc";
private static final String mdmDeviceOwnerReceiverName = "com.afwsamples.testdpc.DeviceAdminReceiver";
private boolean isMdmDeviceOwnerSetting = false;
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final boolean inCts = SystemProperties.getBoolean("persist.sys.cts_state", false);
final String action = intent.getAction();
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
getSendingUserId());
.......
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
ComponentName cn = new ComponentName(mdmDeviceOwnerPackageName, mdmDeviceOwnerReceiverName);
boolean needSetDeviceOwner = mdmNeedSetDeviceOwner(cn, userHandle);
if (inCts || needSetDeviceOwner) {
calculateHasIncompatibleAccounts();
}
if (needSetDeviceOwner) {
mdmRunSetDeviceOwner(cn);
}
}
......
}
};
6.1 mdmNeedSetDeviceOwner 方法
private boolean mdmNeedSetDeviceOwner(ComponentName cn, int userHandle) {
if (mIPackageManager == null) {
return false;
}
try {
ActivityInfo info = mIPackageManager.getReceiverInfo(cn, GET_META_DATA, userHandle);
if (info != null && !isDeviceOwner(cn, UserHandle.USER_SYSTEM)) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
6.2 mdmRunSetDeviceOwner 方法
private void mdmRunSetDeviceOwner(ComponentName cn) {
if (mHandler == null) {
return;
}
mHandler.postDelayed(() -> {
isMdmDeviceOwnerSetting = true;
setActiveAdmin(cn, true, UserHandle.USER_SYSTEM);
try {
if (!setDeviceOwner(cn, UserHandle.USER_SYSTEM, true)) {
throw new RuntimeException("Can't set mdm package " + cn + " as device owner.");
}
setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED, UserHandle.USER_SYSTEM);
} catch (Exception e) {
removeActiveAdmin(cn, UserHandle.USER_SYSTEM);
} finally {
isMdmDeviceOwnerSetting = false;
}
}, 1000);
}
我们自信满满
编译代码make services -j16,
push jar包 adb push out/target/product/qssi_64/system/framework/services.jar system/framework/,
重启手机 adb reboot
去系统设置Settings里查看有没被赋予权限,很遗憾,没看到我们想要的结果
6.3 isAdb 方法
于是我们去仔细查看setDeviceOwner方法,发现有一个特殊的条件isAdb(CallerIdentity caller)
/**
* Defines the root UID.
*/
public static final int ROOT_UID = 0;
/**
* Defines the UID/GID under which system code runs.
*/
public static final int SYSTEM_UID = 1000;
/**
* Defines the UID/GID for the user shell.
*/
public static final int SHELL_UID = 2000;
private boolean isAdb(CallerIdentity caller) {
return isShellUid(caller) || isRootUid(caller);
}
private boolean isRootUid(CallerIdentity caller) {
return UserHandle.isSameApp(caller.getUid(), Process.ROOT_UID);
}
private boolean isShellUid(CallerIdentity caller) {
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
我们在DevicePolicyManagerService里面直接调用setDeviceOwner,UID既不符合SHELL_UID,也不符合ROOT_UID,于是我们额外添加一个条件,变成
private boolean isAdb(CallerIdentity caller) {
return isShellUid(caller) || isRootUid(caller) || isMdmDeviceOwnerSetting;
}
isMdmDeviceOwnerSetting的使用,可回看mdmRunSetDeviceOwner方法