用戶管理
- 如何創(chuàng)建另外一個(gè)用戶扮超?如何區(qū)分訪客用戶及其他用戶取刃?各種用戶的區(qū)別是什么?
- Android是怎么限制最多用戶數(shù)量的瞒津?Flyme最多可以創(chuàng)建幾個(gè)蝉衣,在哪里控制了?
- 切換用戶做了些什么操作巷蚪?第三方應(yīng)用有什么辦法知道用戶切換了病毡?如何知道自己當(dāng)前處于哪個(gè)用戶?
- 用戶切換后屁柏,原來(lái)的進(jìn)程怎么處理的啦膜?SystemUI是新的進(jìn)程嗎?微信是開(kāi)了兩個(gè)進(jìn)程嗎淌喻,進(jìn)程ID是怎么分配的僧家,有什么特點(diǎn)?
- 不同用戶是如何分別存儲(chǔ)數(shù)據(jù)的裸删?多個(gè)用戶之間如何共享數(shù)據(jù)八拱?
UserManagerService創(chuàng)建過(guò)程
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);
創(chuàng)建0號(hào)用戶的路徑并設(shè)置對(duì)應(yīng)的路徑權(quán)限,下面貼一段網(wǎng)上找到的標(biāo)志位與權(quán)限之間的關(guān)系(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
存儲(chǔ)用戶信息的文件路徑在: data/system/users/userlist.xml
createUser 用戶創(chuàng)建
用戶創(chuàng)建過(guò)程首先會(huì)檢查 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);
}
}
然后會(huì)進(jìn)入到 CreateUserInternal() 方法中,在該方法中首先會(huì)檢查 用戶是否被賦予了 DISALLOW_ADD_USER 權(quán)限,該權(quán)限禁止用戶添加用戶.
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
UserManager.DISALLOW_ADD_USER, false)) {
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
創(chuàng)建用戶的過(guò)程中會(huì)創(chuàng)建 UserInfo 對(duì)象, UserInfo 對(duì)象中的 partial 屬性表明該 UserInfo 對(duì)象并沒(méi)有完全創(chuàng)建.接著會(huì)創(chuàng)建用戶的目錄,調(diào)用 getUserSystemDirectory 創(chuàng)建某個(gè)用戶對(duì)應(yīng)的路徑.
這里會(huì)判斷系統(tǒng)中 EFS 這一功能是否打開(kāi). EFS(文件加密系統(tǒng)) ,如果打開(kāi)了 EFS 功能涯塔,就會(huì)創(chuàng)建一個(gè)加密路徑 /data/secure/system/, 否則創(chuàng)建的就是普通路徑 /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");
}
}
最后系統(tǒng)會(huì)發(fā)出 ACTION_USER_ADDED 這個(gè)廣播,只有聲明了 MANAGE_USERS 這個(gè)權(quán)限才能接受到這個(gè)廣播.
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);
}
用戶的權(quán)限類別
- FLAG_PRIMARY : 只有一個(gè)用戶能設(shè)置擁有這個(gè)標(biāo)志位肌稻。意味著這個(gè)標(biāo)志位TBD.
- FLAG_ADMIN : 擁有創(chuàng)建和刪除用戶的權(quán)限.
- FLAG_GUEST : 訪客用戶的標(biāo)識(shí).
- FLAG_RESTRICTED : 受限,可能無(wú)法安裝應(yīng)用匕荸,或認(rèn)證wifi路徑.
- FLAG_MANAGED_PROFILE : 表明該user是另外一個(gè)用戶的 profile.
- FLAG_DISABLED : 表明該用戶被禁用.
各種用戶擁有不同的權(quán)限組合. 例如 USER_OWNER 是 PRIMARY 和 ADMIN 的組合.
第三方應(yīng)用獲取當(dāng)前用戶
getCurrentUser() 是AMS中的一個(gè)接口爹谭,用來(lái)獲取當(dāng)前用戶,這里會(huì)檢查 是否有 INTERACT_ACROSS_USERS 和 INTERACT_ACROSS_USERS_FULL 這兩個(gè)權(quán)限.
@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() 函數(shù).
@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;
}
這里首先會(huì)調(diào)用 enforceShellRestriction() 函數(shù)檢查是否是 通過(guò) shell終端來(lái)調(diào)用這個(gè)方法來(lái)切換用戶.
如果 userHandle 小于0或者該用戶受到限制,就會(huì)拋出 SecurityException 這個(gè)異常.
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);
}
}
}
最后會(huì)發(fā)送 START_USER_SWITCH_MSG 這個(gè)廣播,AMS本身接收這個(gè)廣播后會(huì)調(diào)用 startUser() 方法來(lái)調(diào)起新用戶.
startUser() 函數(shù)很長(zhǎng).
首先會(huì)檢查 用戶是否有 INTERACT_ACROSS_USERS_FULL 這個(gè)權(quán)限榛搔,如果沒(méi)有就會(huì)直接拋出異常.
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);
}
接著這里有一行代碼诺凡,作用暫時(shí)未知.
mStackSupervisor.setLockTaskModeLocked(null, false, "startUser");
這里會(huì)判斷是否處于前臺(tái)狀態(tài)东揣,如果處于前臺(tái)狀態(tài),會(huì)進(jìn)行一個(gè)動(dòng)畫(huà)的切換.
if (foreground) {
mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
R.anim.screen_user_enter);
}
這里判斷如果處于前臺(tái)狀態(tài)腹泌,就要隱藏掉切換后用戶不可見(jiàn)的 display , 這里 mWindowManager.lockNow(null) 的邏輯被屏蔽掉了.如果不處于前臺(tái)狀態(tài)嘶卧,這里就沒(méi)有屏蔽圖層,只是切換了用戶的profile真屯,然后會(huì)在 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);
}
這里會(huì)更新一波 UserState , 這里的 UserState 有些類似操作系統(tǒng)里的進(jìn)程切換概念脸候,
分為 STATE_BOOTING , STATE_RUNNING , STATE_STOPPING , STATE_SHUTDOWN 這幾個(gè)狀態(tài).
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));
}
接著發(fā)送用戶切換的MSG,這個(gè)message有兩秒的延遲,兩秒后會(huì)停止當(dāng)前屏幕上用戶切換的活動(dòng).
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);
}
會(huì)調(diào)用到 mWindowManager.stopFreezingScreen() 這個(gè)接口.
if (!uss.switching && !uss.initializing) {
mWindowManager.stopFreezingScreen();
unfrozen = true;
}
然后會(huì)把后臺(tái)的訪客模式用戶停掉.
stopGuestUserIfBackground();
接著會(huì)發(fā)送調(diào)起用戶的廣播
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);
}
切換用戶的操作就到這里結(jié)束了.
用戶數(shù)量的控制
關(guān)于用戶數(shù)量的控制绑蔫,原生代碼并沒(méi)有對(duì)用戶數(shù)量加以限制运沦,F(xiàn)lyme中對(duì)用戶的限制數(shù)量為1.
可創(chuàng)建的最大用戶數(shù)量由 UserManager 中的 getMaxSupportedUsers() 方法控制.可以在 framework/base 工程下的 config.xml 文件中進(jìn)行配置.
/**
* 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));
}
配置選項(xiàng)如下.
1598 <!-- Maximum number of supported users -->
1599 <integer name="config_multiuserMaximumUsers">1</integer>