用户管理
- 如何创建另外一个用户?如何区分访客用户及其他用户?各种用户的区别是什么?
- 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:[email protected] 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