Android中用户管理的一些总结

分类: beat365下载唯一官方网 时间: 2025-10-24 10:36:41 作者: admin

用户管理

如何创建另外一个用户?如何区分访客用户及其他用户?各种用户的区别是什么?

Android是怎么限制最多用户数量的?Flyme最多可以创建几个,在哪里控制了?

切换用户做了些什么操作?第三方应用有什么办法知道用户切换了?如何知道自己当前处于哪个用户?

用户切换后,原来的进程怎么处理的?SystemUI是新的进程吗?微信是开了两个进程吗,进程ID是怎么分配的,有什么特点?

不同用户是如何分别存储数据的?多个用户之间如何共享数据?

UserManagerService创建过程

mUsersDir = new File(dataDir, USER_INFO_DIR);

// Make zeroth user directory, for services to migrate their files to that location

File userZeroDir = new File(mUsersDir, "0");

userZeroDir.mkdirs();

FileUtils.setPermissions(mUsersDir.toString(),

FileUtils.S_IRWXU|FileUtils.S_IRWXG

|FileUtils.S_IROTH|FileUtils.S_IXOTH,

-1, -1);

创建0号用户的路径并设置对应的路径权限,下面贴一段网上找到的标志位与权限之间的关系(GRP=group, OTH=other)

S_IFMT

type of file

S_IFBLK

block special

S_IFCHR

character special

S_IFIFO

FIFO special

S_IFREG

regular

S_IFDIR

directory

S_IFLNK

symbolic link

File mode bits:

S_IRWXU

read, write, execute/search by owner

S_IRUSR

read permission, owner

S_IWUSR

write permission, owner

S_IXUSR

execute/search permission, owner

S_IRWXG

read, write, execute/search by group

S_IRGRP

read permission, group

S_IWGRP

write permission, group

S_IXGRP

execute/search permission, group

S_IRWXO

read, write, execute/search by others

S_IROTH

read permission, others

S_IWOTH

write permission, others

S_IXOTH

execute/search permission, others

S_ISUID

set-user-ID on execution

S_ISGID

set-group-ID on execution

S_ISVTX

on directories, restricted deletion flag

存储用户信息的文件路径在: data/system/users/userlist.xml

createUser 用户创建

用户创建过程首先会检查 uid 是否符合要求.

/**

* Enforces that only the system UID or root's UID or apps that have the

* {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}

* permission can make certain calls to the UserManager.

*

* @param message used as message if SecurityException is thrown

* @throws SecurityException if the caller is not system or root

*/

private static final void checkManageUsersPermission(String message) {

final int uid = Binder.getCallingUid();

if (uid != Process.SYSTEM_UID && uid != 0

&& ActivityManager.checkComponentPermission(

android.Manifest.permission.MANAGE_USERS,

uid, -1, true) != PackageManager.PERMISSION_GRANTED) {

throw new SecurityException("You need MANAGE_USERS permission to: " + message);

}

}

然后会进入到 CreateUserInternal() 方法中,在该方法中首先会检查 用户是否被赋予了 DISALLOW_ADD_USER 权限,该权限禁止用户添加用户.

if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(

UserManager.DISALLOW_ADD_USER, false)) {

Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");

return null;

}

创建用户的过程中会创建 UserInfo 对象, UserInfo 对象中的 partial 属性表明该 UserInfo 对象并没有完全创建.接着会创建用户的目录,调用 getUserSystemDirectory 创建某个用户对应的路径.

这里会判断系统中 EFS 这一功能是否打开. EFS(文件加密系统) ,如果打开了 EFS 功能,就会创建一个加密路径 /data/secure/system/, 否则创建的就是普通路径 /data/system .

userInfo.partial = true;

Environment.getUserSystemDirectory(userInfo.id).mkdirs();

/**

* Gets the system directory available for secure storage.

* If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).

* Otherwise, it returns the unencrypted /data/system directory.

* @return File object representing the secure storage system directory.

* @hide

*/

public static File getSystemSecureDirectory() {

if (isEncryptedFilesystemEnabled()) {

return new File(SECURE_DATA_DIRECTORY, "system");

} else {

return new File(DATA_DIRECTORY, "system");

}

}

最后系统会发出 ACTION_USER_ADDED 这个广播,只有声明了 MANAGE_USERS 这个权限才能接受到这个广播.

if (userInfo != null) {

Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);

addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);

mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,

android.Manifest.permission.MANAGE_USERS);

}

用户的权限类别

FLAG_PRIMARY : 只有一个用户能设置拥有这个标志位。意味着这个标志位TBD.

FLAG_ADMIN : 拥有创建和删除用户的权限.

FLAG_GUEST : 访客用户的标识.

FLAG_RESTRICTED : 受限,可能无法安装应用,或认证wifi路径.

FLAG_MANAGED_PROFILE : 表明该user是另外一个用户的 profile.

FLAG_DISABLED : 表明该用户被禁用.

各种用户拥有不同的权限组合. 例如 USER_OWNER 是 PRIMARY 和 ADMIN 的组合.

第三方应用获取当前用户

getCurrentUser() 是AMS中的一个接口,用来获取当前用户,这里会检查 是否有 INTERACT_ACROSS_USERS 和 INTERACT_ACROSS_USERS_FULL 这两个权限.

@Override

public UserInfo getCurrentUser() {

if ((checkCallingPermission(INTERACT_ACROSS_USERS)

!= PackageManager.PERMISSION_GRANTED) && (

checkCallingPermission(INTERACT_ACROSS_USERS_FULL)

!= PackageManager.PERMISSION_GRANTED)) {

String msg = "Permission Denial: getCurrentUser() from pid="

+ Binder.getCallingPid()

+ ", uid=" + Binder.getCallingUid()

+ " requires " + INTERACT_ACROSS_USERS;

Slog.w(TAG, msg);

throw new SecurityException(msg);

}

synchronized (this) {

int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;

return getUserManagerLocked().getUserInfo(userId);

}

}

切换用户

切换用户用的是 AMS 中的 SwitchUser() 函数.

@Override

public boolean switchUser(final int userId) {

enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);

String userName;

synchronized (this) {

UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);

if (userInfo == null) {

Slog.w(TAG, "No user info for user #" + userId);

return false;

}

if (userInfo.isManagedProfile()) {

Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");

return false;

}

userName = userInfo.name;

mTargetUserId = userId;

}

mHandler.removeMessages(START_USER_SWITCH_MSG);

mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));

return true;

}

这里首先会调用 enforceShellRestriction() 函数检查是否是 通过 shell终端来调用这个方法来切换用户.

如果 userHandle 小于0或者该用户受到限制,就会抛出 SecurityException 这个异常.

enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);

void enforceShellRestriction(String restriction, int userHandle) {

if (Binder.getCallingUid() == Process.SHELL_UID) {

if (userHandle < 0

|| mUserManager.hasUserRestriction(restriction, userHandle)) {

throw new SecurityException("Shell does not have permission to access user "

+ userHandle);

}

}

}

最后会发送 START_USER_SWITCH_MSG 这个广播,AMS本身接收这个广播后会调用 startUser() 方法来调起新用户.

startUser() 函数很长.

首先会检查 用户是否有 INTERACT_ACROSS_USERS_FULL 这个权限,如果没有就会直接抛出异常.

if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)

!= PackageManager.PERMISSION_GRANTED) {

String msg = "Permission Denial: switchUser() from pid="

+ Binder.getCallingPid()

+ ", uid=" + Binder.getCallingUid()

+ " requires " + INTERACT_ACROSS_USERS_FULL;

Slog.w(TAG, msg);

throw new SecurityException(msg);

}

接着这里有一行代码,作用暂时未知.

mStackSupervisor.setLockTaskModeLocked(null, false, "startUser");

这里会判断是否处于前台状态,如果处于前台状态,会进行一个动画的切换.

if (foreground) {

mWindowManager.startFreezingScreen(R.anim.screen_user_exit,

R.anim.screen_user_enter);

}

这里判断如果处于前台状态,就要隐藏掉切换后用户不可见的 display , 这里 mWindowManager.lockNow(null) 的逻辑被屏蔽掉了.如果不处于前台状态,这里就没有屏蔽图层,只是切换了用户的profile,然后会在 mUserLru 中记录最近使用的user.

if (foreground) {

mCurrentUserId = userId;

mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up

updateCurrentProfileIdsLocked();

mWindowManager.setCurrentUser(userId, mCurrentProfileIds);

//FLYME:huangxiaotao@SHELL.Feature modify for the guest mode {@

if (mzIsFalse()) {

// Once the internal notion of the active user has switched, we lock the device

// with the option to show the user switcher on the keyguard.

mWindowManager.lockNow(null);

}

//@}

} else {

final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);

updateCurrentProfileIdsLocked();

mWindowManager.setCurrentProfileIds(mCurrentProfileIds);

mUserLru.remove(currentUserIdInt);

mUserLru.add(currentUserIdInt);

}

这里会更新一波 UserState , 这里的 UserState 有些类似操作系统里的进程切换概念,

分为 STATE_BOOTING , STATE_RUNNING , STATE_STOPPING , STATE_SHUTDOWN 这几个状态.

final UserState uss = mStartedUsers.get(userId);

// Make sure user is in the started state. If it is currently

// stopping, we need to knock that off.

if (uss.mState == UserState.STATE_STOPPING) {

// If we are stopping, we haven't sent ACTION_SHUTDOWN,

// so we can just fairly silently bring the user back from

// the almost-dead.

uss.mState = UserState.STATE_RUNNING;

updateStartedUserArrayLocked();

needStart = true;

} else if (uss.mState == UserState.STATE_SHUTDOWN) {

// This means ACTION_SHUTDOWN has been sent, so we will

// need to treat this as a new boot of the user.

uss.mState = UserState.STATE_BOOTING;

updateStartedUserArrayLocked();

needStart = true;

}

if (uss.mState == UserState.STATE_BOOTING) {

// 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(SYSTEM_USER_START_MSG, userId, 0));

}

接着发送用户切换的MSG,这个message有两秒的延迟,两秒后会停止当前屏幕上用户切换的活动.

if (foreground) {

mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,

oldUserId));

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), USER_SWITCH_TIMEOUT);

}

会调用到 mWindowManager.stopFreezingScreen() 这个接口.

if (!uss.switching && !uss.initializing) {

mWindowManager.stopFreezingScreen();

unfrozen = true;

}

然后会把后台的访客模式用户停掉.

stopGuestUserIfBackground();

接着会发送调起用户的广播

if (needStart) {

/// M: Mobile Management @{

mAmPlus.monitorBootReceiver(true, "User(" + userId + ") Bootup Start");

/// @}

// Send USER_STARTED broadcast

Intent intent = new Intent(Intent.ACTION_USER_STARTED);

intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY

| Intent.FLAG_RECEIVER_FOREGROUND);

intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);

broadcastIntentLocked(null, null, intent,

null, null, 0, null, null, null, AppOpsManager.OP_NONE,

null, false, false, MY_PID, Process.SYSTEM_UID, userId);

}

切换用户的操作就到这里结束了.

用户数量的控制

关于用户数量的控制,原生代码并没有对用户数量加以限制,Flyme中对用户的限制数量为1.

可创建的最大用户数量由 UserManager 中的 getMaxSupportedUsers() 方法控制.可以在 framework/base 工程下的 config.xml 文件中进行配置.

/**

* Returns the maximum number of users that can be created on this device. A return value

* of 1 means that it is a single user device.

* @hide

* @return a value greater than or equal to 1

*/

public static int getMaxSupportedUsers() {

// Don't allow multiple users on certain builds

if (android.os.Build.ID.startsWith("JVP")) return 1;

// Svelte devices don't get multi-user.

if (ActivityManager.isLowRamDeviceStatic()) return 1;

return SystemProperties.getInt("fw.max_users",

Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));

}

配置选项如下.

1598

1599 1