原生多用户源码分析

多用户创建
UserManager#createProfile源码流程

@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
        Manifest.permission.CREATE_USERS})
@UserHandleAware
public @Nullable UserHandle createProfile(@NonNull String name, @NonNull String userType,
        @NonNull Set<String> disallowedPackages) throws UserOperationException {
    try {
        return mService.createProfileForUserWithThrow(name, userType, 0,
                mUserId, disallowedPackages.toArray(
                        new String[disallowedPackages.size()])).getUserHandle();
    } catch (ServiceSpecificException e) {
        return returnNullOrThrowUserOperationException(e,
                mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
    } catch (RemoteException re) {
        throw re.rethrowFromSystemServer();
    }
}

UserManagerService#createProfileForUserWithThrow
1.为新用户创建一个新的userId (新用户的userId从10开始递增)

private @NonNull UserInfo createUserInternalUncheckedNoTracing(
        @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
        @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
        @NonNull TimingsTraceAndSlog t, @Nullable Object token)
        throws UserManager.CheckedUserOperationException {
        ...
        
           userId = getNextAvailableId();
         
         
        }

2.存储新用户信息。构造包含新用户信息的UserData,并固化到 /data/system/users/${userId}.xml文件中

private void writeUserLP(UserData userData) {
    if (DBG) {
        debug("writeUserLP " + userData);
    }
    try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
        FileOutputStream fos = null;
        try {
            fos = userFile.startWrite();
            writeUserLP(userData, fos);
            userFile.finishWrite(fos);
        } catch (Exception ioe) {
            Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
            userFile.failWrite(fos);
        }
    }
}

3:将新创建新userId固化到 "/data/system/users/userlist.xml"文件中

private void writeUserListLP() {
    if (DBG) {
        debug("writeUserList");
    }

    try (ResilientAtomicFile file = getUserListFile()) {
        FileOutputStream fos = null;
        try {
            fos = file.startWrite();

            final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
            serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
                    true);

            serializer.startTag(null, TAG_USERS);
            serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
            serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
            serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
     ...
          
          }

3、准备文件系统
通过vold(Android存储守护进程)为新用户进行文件系统加密;
创建/data/misc/users/{userId} 目录并 权限; 创建新用户的数据空间与设备加密数据存储空间,对应的路径为 /data/user/{userId}、/data/user_ce/${userId}等等。

        t.traceBegin("createUserStorageKeys");
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        storage.createUserStorageKeys(userId, userInfo.isEphemeral());
        t.traceEnd();

        // Only prepare DE storage here.  CE storage will be prepared later, when the user is
        // unlocked.  We do this to ensure that CE storage isn't prepared before the CE key is
        // saved to disk.  This also matches what is done for user 0.
        t.traceBegin("prepareUserData");
        mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_DE);
        t.traceEnd();

4、生成一个唯一的合成密码,通常通过复杂的密码生成算法创建,用于加密和解密用户数据。
LockSettingsService

SyntheticPassword initializeSyntheticPassword(int userId) {
    synchronized (mSpManager) {
        Slogf.i(TAG, "Initializing synthetic password for user %d", userId);
        Preconditions.checkState(getCurrentLskfBasedProtectorId(userId) ==
                SyntheticPasswordManager.NULL_PROTECTOR_ID,
                "Cannot reinitialize SP");

        final SyntheticPassword sp = mSpManager.newSyntheticPassword(userId);
        final long protectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
                LockscreenCredential.createNone(), sp, userId);
        setCurrentLskfBasedProtectorId(protectorId, userId);
        setCeStorageProtection(userId, sp);
        if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
            initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
        }
        onSyntheticPasswordCreated(userId, sp);
        Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
        return sp;
    }
}

void createNewUser(int userId, @Nullable Set<String> userTypeInstallablePackages,
String[] disallowedPackages) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
mSettings.createNewUserLI(this, mInstaller, userId,
userTypeInstallablePackages, disallowedPackages);
}
synchronized (mLock) {
scheduleWritePackageRestrictions(userId);
scheduleWritePackageListLocked(userId);
mAppsFilter.onUserCreated(snapshotComputer(), userId);
}
}

6、通知PMS为新用户和应用赋予默认的权限
PackageManagerService

void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
    if (DEBUG_PERMISSIONS) {
        Slog.d(TAG, "onNewUserCreated(id=" + userId
                + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
    }
    if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
        mPermissionManager.onUserCreated(userId);
        mLegacyPermissionManager.grantDefaultPermissions(userId);
        mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId);
        mDomainVerificationManager.clearUser(userId);
    }
}

7、发送 "ACTION_USER_ADDED"广播,通知新用户创建完成
UserManagerService

private void dispatchUserAdded(@NonNull UserInfo userInfo, @Nullable Object token) {
 
    //...then external ones
    Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
    addedIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
    // In HSUM, MainUser might be created before PHASE_ACTIVITY_MANAGER_READY has been sent.
    addedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
    // Also, add the UserHandle for mainline modules which can't use the @hide
    // EXTRA_USER_HANDLE.
    addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userInfo.id));
    mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
            android.Manifest.permission.MANAGE_USERS);
    MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
            : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);

  通过以上工作流程可以看到,多用户其实是系统为应用的data目录分配了一份不同且独立的存储空间,不同用户下的存储空间互不影响且没有权限访问。同时,系统中的AMS、PMS、WMS等各大服务都会针对userId/UserHandle进行多用户适配,并在用户启动、切换、停止、删除等生命周期时做出相应策略的改变。通过以上,Android创造出来一个虚拟的多用户运行环境。

多用户启切换
多用户切换最终调用到UserControl#startUserInternal,多用户切换时userStartMode = USER_START_MODE_FOREGROUND,后台启动时userStartMode =
USER_START_MODE_BACKGROUND_VISIBLE, 这里主要分析startUserInternal方法

1.如果是启动前台用户,则通知AMS清除所有锁定任务,并请求结束LockTask模式
if (foreground) {
t.traceBegin("clearAllLockedTasks");
mInjector.clearAllLockedTasks("startUser");
t.traceEnd();
}
2.获取目标用户的相关信息 ,经过一系列逻辑判断,最后在开始时将目标用户分配给显示器,返回分配是否成功
final UserInfo userInfo = getUserInfo(userId);
t.traceEnd();

        if (userInfo == null) {
            Slogf.w(TAG, "No user info for user #" + userId);
            return false;
        }
        if (foreground && userInfo.isProfile()) {
            Slogf.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
            return false;
        }

        if ((foreground || onSecondaryDisplay) && userInfo.preCreated) {
            Slogf.w(TAG, "Cannot start pre-created user #" + userId + " in foreground or on "
                    + "secondary display");
            return false;
        }
        
        t.traceBegin("assignUserToDisplayOnStart");
        int result = mInjector.getUserManagerInternal().assignUserToDisplayOnStart(userId,
                userInfo.profileGroupId, userStartMode, displayId);
        t.traceEnd();

        if (result == USER_ASSIGNMENT_RESULT_FAILURE) {
            Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s",
                    userStartModeToString(userStartMode), userId, displayId,
                    userAssignmentResultToString(result));
            return false;
        }

3.为目标用户创建UserState,并放入mStartedUsers数据结构,调整用户在mUserLru中的位置,将目标用户位于末尾,根据mUserLru可以知道当前系统运行的用户信息,接着会移除停止用户允许的message
t.traceBegin("updateStartedUserArrayStarting");
synchronized (mLock) {
uss = mStartedUsers.get(userId);
if (uss == null) {//这里
uss = new UserState(UserHandle.of(userId));
uss.mUnlockProgress.addListener(new UserProgressListener());
mStartedUsers.put(userId, uss);
updateStartedUserArrayLU();
needStart = true;
updateUmState = true;
} else if (uss.state == UserState.STATE_SHUTDOWN) {
Slogf.i(TAG, "User #" + userId
+ " is shutting down - will start after full shutdown");
mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
unlockListener));
t.traceEnd(); // updateStartedUserArrayStarting
return true;
}
}

        // No matter what, the fact that we're requested to start the user (even if it is
        // already running) puts it towards the end of the mUserLru list.
        addUserToUserLru(userId);
        if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
        
            mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG,
                    Integer.valueOf(userId));//这里
        }

4.将目标用户的UserState设置为BOOTING状态,将启动的userId设置为mCurrentUserId ,然后更新mCurrentUserId的配置,并将切换状态置为true
if (updateUmState) {
t.traceBegin("setUserState");
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
t.traceEnd();
}
t.traceBegin("updateConfigurationAndProfileIds");
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
boolean userSwitchUiEnabled;
synchronized (mLock) {
mCurrentUserId = userId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
mInjector.updateUserConfiguration();
// NOTE: updateProfileRelatedCaches() is called on both if and else parts, ideally
// it should be moved outside, but for now it's not as there are many calls to
// external components here afterwards
updateProfileRelatedCaches();
mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
if (userSwitchUiEnabled) {
mInjector.getWindowManager().setSwitchingUser(true);
}

        } 

5.准备相关用户的限制及存储 ,发送用户启动消息
if (uss.state == UserState.STATE_BOOTING) {
t.traceBegin("updateStateBooting");
// Give user manager a chance to propagate user restrictions
// to other services and prepare app storage
mInjector.getUserManager().onBeforeStartUser(userId);

            // Booting up a new user, need to tell system services about it.
            // Note that this is on the same handler as scheduling of broadcasts,
            // which is important because it needs to go first.
            mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, NO_ARG2));
            t.traceEnd();
        }

6.发送REPORT_USER_SWITCH_MSG消息、REPORT_USER_SWITCH_MSG消息和USER_SWITCH_TIMEOUT_MSG消息
if (foreground) {
mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));//onUserSwitching
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
oldUserId, userId, uss));
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
oldUserId, userId, uss), getUserSwitchTimeoutMs());
}
REPORT_USER_SWITCH_MSG消息是告知系统正在切换用户

REPORT_USER_SWITCH_MSG消息里逻辑很绕,就不详细展开,最重要的是干了2件事:
1.调用showUserSwitchDialog显示弹窗
2.并且会监听动态壁纸的显示状态变化。当动态壁纸的渲染引擎(Engine)启动并成功显示时,系统会通过aidl通知UserControl用以dismissUserSwitchDialog

USER_SWITCH_TIMEOUT_MSG消息主要是发送一个5s的超时消息,超时了以后会取消弹窗并处理相关超时逻辑

7.移动新的user到前台

        if (foreground) {
            t.traceBegin("moveUserToForeground");
            moveUserToForeground(uss, userId);
            t.traceEnd();
        } else {
            t.traceBegin("finishUserBoot");
            finishUserBoot(uss);//这里
            t.traceEnd();
        }

private void moveUserToForeground(UserState uss, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivity(newUserId, "moveUserToForeground");
} else {
mInjector.taskSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
}

多用户删除
UserManager#removeUser源码流程
直接看UserManagerService#removeUser实现
1、检查调用者是否有删除用户的权限
public boolean removeUser(@UserIdInt int userId) {
Slog.i(LOG_TAG, "removeUser u" + userId);
checkCreateUsersPermission("Only the system can remove users");

    final String restriction = getUserRemovalRestriction(userId);
    if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
        Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
        return false;
    }
    return removeUserWithProfilesUnchecked(userId);
}

2、保存要删除的userId,防止重复删除;
把partial变量修改为true, 开机后如果该变量还是true会删除相关文件
将userData的flag增加UserInfo.FLAG_DISABLED,更新/data/system/users/${id}.xml文件
private boolean removeUserUnchecked(@UserIdInt int userId) {
final long ident = Binder.clearCallingIdentity();
try {
final UserData userData;
synchronized (mPackagesLock) {
synchronized (mUsersLock) {
final int userRemovability = getUserRemovabilityLocked(userId, "removed");
if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) {
return UserManager.isRemoveResultSuccessful(userRemovability);
}
userData = mUsers.get(userId);
Slog.i(LOG_TAG, "Removing user " + userId);
addRemovingUserIdLocked(userId);
}
// Set this to a partially created user, so that the user will be purged
// on next startup, in case the runtime stops now before stopping and
// removing the user completely.
userData.info.partial = true;
// Mark it as disabled, so that it isn't returned any more when
// profiles are queried.
userData.info.flags |= UserInfo.FLAG_DISABLED;
writeUserLP(userData);
}
...
3、删除该用户的UidState状态
发送删除用户的广播

        try {
            mAppOpsService.removeUser(userId);
        } catch (RemoteException e) {
            Slog.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
        }

        if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                && userData.info.isProfile()) {
            sendProfileRemovedBroadcast(userData.info.profileGroupId, userData.info.id,
                    userData.info.userType);
        }

4、停止正在运行的用户
用户停止后, 删除用户相关应用信息
try {
res = ActivityManager.getService().stopUserWithCallback(userId,
new IStopUserCallback.Stub() {
@Override
public void userStopped(int userIdParam) {
finishRemoveUser(userIdParam);
int originUserId = UserManagerService.this.getCurrentUserId();
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
userData.info, USER_JOURNEY_USER_REMOVE,
ERROR_CODE_UNSPECIFIED);
mUserJourneyLogger
.logDelayedUserJourneyFinishWithError(originUserId,
userData.info, USER_JOURNEY_USER_LIFECYCLE,
ERROR_CODE_UNSPECIFIED);
}
@Override
public void userStopAborted(int userIdParam) {
int originUserId = UserManagerService.this.getCurrentUserId();
mUserJourneyLogger.logUserJourneyFinishWithError(originUserId,
userData.info, USER_JOURNEY_USER_REMOVE,
ERROR_CODE_ABORTED);
mUserJourneyLogger
.logDelayedUserJourneyFinishWithError(originUserId,
userData.info, USER_JOURNEY_USER_LIFECYCLE,
ERROR_CODE_ABORTED);
}
});
} catch (RemoteException e) {
Slog.w(LOG_TAG, "Failed to stop user during removal.", e);
return false;
}
finishRemoveUser内部会开线程执行removeUserState方法
private void removeUserState(final @UserIdInt int userId) {
Slog.i(LOG_TAG, "Removing user state of user " + userId);

    // Cleanup lock settings.  This requires that the user's DE storage still be accessible, so
    // this must happen before destroyUserStorageKeys().
    mLockPatternUtils.removeUser(userId);

    // Evict and destroy the user's CE and DE encryption keys.  At this point, the user's CE and
    // DE storage is made inaccessible, except to delete its contents.
    try {
        mContext.getSystemService(StorageManager.class).destroyUserStorageKeys(userId);
    } catch (IllegalStateException e) {
        // This may be simply because the user was partially created.
        Slog.i(LOG_TAG, "Destroying storage keys for user " + userId
                + " failed, continuing anyway", e);
    }

    // Cleanup package manager settings
    mPm.cleanUpUser(this, userId);

    // Clean up all data before removing metadata
    mUserDataPreparer.destroyUserData(userId,
            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);

    // Remove this user from the list
    synchronized (mUsersLock) {
        mUsers.remove(userId);
        mIsUserManaged.delete(userId);
    }
    synchronized (mUserStates) {
        mUserStates.delete(userId);
    }
    synchronized (mRestrictionsLock) {
        mBaseUserRestrictions.remove(userId);
        mAppliedUserRestrictions.remove(userId);
        mCachedEffectiveUserRestrictions.remove(userId);
        // Remove restrictions affecting the user
        if (mDevicePolicyUserRestrictions.remove(userId)) {
            applyUserRestrictionsForAllUsersLR();
        }
    }
    // Update the user list
    synchronized (mPackagesLock) {
        writeUserListLP();
    }
    // Remove user file(s)
    getUserFile(userId).delete();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容