MDM项目给预装 apk 设置 deviceowner 权限

100 阅读6分钟

dpc官方Demo

假设有一个预装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方法