代码参考AOSP 中 API 23
(android 6.0.1)
Android从4.2开始支持多用户模式,不同的用户运行在独立的用户空间,不同用户安装的应用和应用数据都是不一样的,但是系统中与硬件相关的设置则是共用的,比如网络设置等。
通常第一个在系统中注册的用户将成为系统管理员,可以管理手机上的其他用户。但由于专利的原因,目前手机上并没有开启多用户模式,这种模式只能用在平板上面。
手机上经常见到的一个功能就是访客模式,它是多用户模式的一种应用。访客模式下用户的所有数据(通讯录,短信,应用等)会被隐藏,访客只能查看和使用手机的基本功能,此外你还可以设置访客是否有接听电话、发送短信等权限。
代表不同的用户,即手机里的主机、访客等多用户,类似于windows
==>Linux uid
Linux是多用户系统,每个用户都拥有一个uid
,这个uid
由系统和用户名做映射绑定。同时,为了便于用户管理(譬如管理文档权限),Linux引入了群组的概念,可以将多个用户归于一个群组。每一个群组拥有一个群组id(gid
)。
==>Android uid
Android的应用的uid
的范围是从10000
到19999
(FIRST_APPLICATION_UID
到LAST_APPLICATION_UID
)。
使用adb shell
的ps
命令查看到的pid
,例如 u0_a94
,a
表示FIRST_APPLICATION_UID
, 后面的数字就是该应用的UID
值减去FIRST_APPLICATION_UID
所得的值,也就是appid
, 具体字符串构造规则可参考后面介绍的formatUid
函数。
应用安装后,系统重启和应用重启都不会改变uid
。
uid
记录在data/system/packages.xml
中,如查询VA的uid
generic_x86:/ # dumpsys package io.busniess.va |grep uid
uid=10085 gids=null type=0 prot=signature
但要切记,userid
用户不同,包名相同的uid
也会不同,转换关系:
uid = userId * 100000 + appId
也就是真正不变的是appId
跟userid
用户无关的应用程序id
, 取值范围 0<= appId <100000
即使是不同用户,包名相同的appid
都一样。比如前面列举的 u0_a94
中的94
由UserHandle
的PER_USER_RANGE
可知每个userid
用户最大可以有100000个appid
userid
用户区间分布如下:
用户 | 区间 |
---|---|
system | [1000, 9999] |
application | [10000, 19999] |
SharedAppGid | [50000, 59999] |
isolated | [99000, 99999] |
isolated
是隔离空间,也就是沙箱。
常见函数如下:(结合前面提到的慨念,较好理解)
方法 | 用途 |
---|---|
public static final boolean isSameUser (int uid1, int uid2) | 比较两个uid 的userId 是否相同 |
public static final boolean isSameApp (int uid1, int uid2) | 比较两个uid 的appId 是否相同 |
public static boolean isApp (int uid) | appId 是否属于区间[10000,19999] |
public static final int getUserId (int uid) | 得到 userId ,其实就是uid /10000 |
public static final int getCallingUserId () | 得到调用者的 userId |
public static final UserHandle getCallingUserHandle () | 得到调用者的UserHandle 类对象 |
public static final int getUid (int userId, int appId) | 得到Uid ,其实就是uid = userId * 100000 + appId |
public static final int getAppId (int uid) | 得到appId |
public int getIdentifier () | 得到当前UserHandle 类对象所在的 userId |
public static void formatUid (StringBuilder sb, int uid) | 将uid 组成字符串,如u0_a94 |
常见成员变量:(UserHandle
的成员变量mHandle
便是它所在的userId
)
userId | 赋值 | 含义 |
---|---|---|
USER_OWNER | 0 | 拥有者 |
USER_ALL | -1 | 所有用户 |
USER_CURRENT | -2 | 当前活动用户 |
USER_CURRENT_OR_SELF | -3 | 当前用户或者调用者所在用户 |
USER_NULL | -1000 | 未定义用户 |
类成员变量:
UserHandle OWNER = new UserHandle(USER_OWNER); // 0
UserHandle ALL = new UserHandle(USER_ALL); // -1
UserHandle CURRENT = new UserHandle(USER_CURRENT); // -2
UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF); // -3
要得到当前进程的userId
(mHandle
即userId
),客户端可以使用Process.myUserHandle().getIdentifier()
, 代码实现如下:
public static final UserHandle myUserHandle() {
return new UserHandle(UserHandle.getUserId(myUid()));
}
--->UserHandle构造如下:
public UserHandle(Parcel in) {
mHandle = in.readInt();
}
--->UserHandle的getIdentifier如下:
public int getIdentifier() {
return mHandle;
}
要得到调用者的进程userId
,,服务端直接使用UserHandle.getCallingUserId()
。
前面列举的 u0_a94
,它的字符串组成可以参考formatUid
方法:
public static void formatUid(StringBuilder sb, int uid) {
if (uid < Process.FIRST_APPLICATION_UID) {
sb.append(uid);
} else {
sb.append('u');
sb.append(getUserId(uid));
final int appId = getAppId(uid);
if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
sb.append('i');
sb.append(appId - Process.FIRST_ISOLATED_UID);
} else if (appId >= Process.FIRST_APPLICATION_UID) {
sb.append('a');
sb.append(appId - Process.FIRST_APPLICATION_UID);
} else {
sb.append('s');
sb.append(appId);
}
}
}
所以 u0_a94
可以这样拆分,0是userId
,a是Process.FIRST_APPLICATION_UID
(10000)为基数,前面提到,由UserHandle
的PER_USER_RANGE
可知每个userid
用户最大可以有100000(十万)个appid
,所以 u0_a94
= 0*100000 + (10000 + 94)= 10094
UserInfo
代表的是一个用户的信息, 涉及到的flags及其含义,如下:
* *************************** NOTE ***************************
* 这些标志值无法更改,因为它们已被写入直接存储
*/
/**
* 主用户 仅仅只有一个user具有该标识. Meaning of this
* flag TBD.
*/
public static final int FLAG_PRIMARY = 0x00000001;
/**
* 具有管理特权的用户,例如它能创建或删除其他用户
*/
public static final int FLAG_ADMIN = 0x00000002;
/**
* 访客用户,可能是临时的
*/
public static final int FLAG_GUEST = 0x00000004;
/**
* 限制性用户,较普通用户具有更多限制,例如禁止安装app或者管理wifi等
*/
public static final int FLAG_RESTRICTED = 0x00000008;
/**
* 表明用户已初始化
*/
public static final int FLAG_INITIALIZED = 0x00000010;
/**
* 标志该UserInfo是否是另外一个用户的一份profile,Android中允许一个用户拥有另一份profile
* 比如现在好多定制系统开发的应用分身,就是基于这个开发的,比如可以在同一个手机上启动两个微信帐号
https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#ACTION_PROVISION_MANAGED_PROFILE
*/
public static final int FLAG_MANAGED_PROFILE = 0x00000020;
/**
* 表明该用户处于禁用状态
*/
public static final int FLAG_DISABLED = 0x00000040;
// 无效值定义
public static final int NO_PROFILE_GROUP_ID = -1;
public int id; //用户id
public int serialNumber; //用户序列号,唯一的,不会重复
public String name; //用户名称
public String iconPath; //用户头像的路径
public int flags; //用户的标记信息
public long creationTime; //用户创建时间
public long lastLoggedInTime;//用户上次登陆时间
public int profileGroupId; //用户的profile group id
// 用来标记没有创建完成的用户
public boolean partial;
public boolean guestToRemove;
需要注意的是,用户的Id
用来表示用户,如果用户被删除了它的Id
会分配给下一个新建的用户,用来保持Id
的连续性;
但是serialNumber
是一个不会重复的数字,是不会被重复利用的,用来唯一标识一个用户。
public final class UserState {
// 用户正在启动状态
public final static int STATE_BOOTING = 0;
// 用户正在运行状态
public final static int STATE_RUNNING = 1;
// 用户正在关闭中
public final static int STATE_STOPPING = 2;
// 用户已经被关闭, sending Intent.ACTION_SHUTDOWN.
public final static int STATE_SHUTDOWN = 3;
生命周期线:STATE_BOOTING -> STATE_RUNNING -> STATE_STOPPING -> STATE_SHUTDOWN
从源码来分析:
AMS
通过 UserManagerService getUserManagerLocked()
得到UMS
PMS
通过sUserManager
得到UMS
UMS
通过mPm
得到PMS
===>创建UMS
的过程和AMS
,PMS
一样位于startBootstrapServices
中,它由PMS
构造函数初始化。
启动代码位于frameworks/base/services/java/com/android/server/SystemServer.java
中,代码如下:
private void startBootstrapServices() {
...
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
...
}
-->PackageManagerService构造函数如下:
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
....
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
}
===>接着看UMS
的构造过程,可以看到PMS
保存UMS
(sUserManager
成员),而UMS
也保存PMS
(mPm
成员), 这个方法部分功能为:
PMS
对象到mPm
成员/data/system/users/0
/data/system/users/userlist.xml
,这个文件保存了系统中所有用户的id信息userlist.xml
文件,将用户的信息解析成UserInfo
,并保存在mUsers
列表中代码如下:
UserManagerService(Context context, PackageManagerService pm,
Object installLock, Object packagesLock) {
this(context, pm, installLock, packagesLock,
Environment.getDataDirectory(),
new File(Environment.getDataDirectory(), "user"));
}
--->跳转到如下私有构造函数:
private UserManagerService(Context context, PackageManagerService pm,
Object installLock, Object packagesLock,
File dataDir,// dataDir = /data
File baseUserPath) {// baseUserPath = /data/user
mContext = context;
mPm = pm;
mInstallLock = installLock;
mPackagesLock = packagesLock;
mHandler = new MainHandler();
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
// mUsersDir是/data/system/users目录
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
// 创建第一个用户的目录/data/system/users/0
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);
// 创建代表/data/system/users/userlist.xml文件的对象
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
// 添加一些对访客用户的默认限制,DISALLOW_OUTGOING_CALLS和DISALLOW_SMS,不允许打电话和发信息
initDefaultGuestRestrictions();
// 读取userlist.xml文件,将用户的信息保存在mUsers列表中
// 如果该文件不存在则创建该文件
readUserListLocked();
sInstance = this;
}
}
--->mUsers的定义如下:
private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
userlist.xml
示例:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<users nextSerialNumber="11" version="5">
<guestRestrictions>
<restrictions no_outgoing_calls="true" no_sms="true" />
</guestRestrictions>
<user id="0" />
<user id="10" />
</users>
nextSerialNumber
指的是创建下一个用户时它的serialNumber
,version
指的是当前多用户的版本, userlist.xml
文件是可以升级的。
guestRestrictions
标签指的是为访客用户设置的权限,可以通过UserManager.setDefaultGuestRestrictions()
来设置。
前面userlist.xml
示例里面有两个用户的信息,分别为 id 为 0 和 10 的用户 ,分别对应/data/system/users
目录下0.xml
和10.xml
(和userlist.xml
同目录)以0.xml
为示例:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<user id="0" serialNumber="0" flags="19" created="0" lastLoggedIn="1493886997408">
<name>机主</name>
<restrictions />
</user>
标签的属性值对应了前面UserInfo
里面的成员变量, readUserListLocked
会根据文件的内容来创建和初始化一个UserInfo
来保存到mUsers
列表中去。
==>接着再看一下systemReady()
函数,这里面也有一些初始化的工作,它的调用是在PackageManagerService.systemReady()
中进行的。
--->PMS中的systemReady如下:
public void systemReady() {
...
sUserManager.systemReady();
}
--->UMS中的systemReady如下:
void systemReady() {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
// 寻找那些在userlist.xml文件中partially或者partial为true的用户
ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
for (int i = 0; i < mUsers.size(); i++) {
UserInfo ui = mUsers.valueAt(i);
if ((ui.partial || ui.guestToRemove) && i != 0) {
partials.add(ui);
}
}
// 将这些未创建完成的用户从系统中移除掉
for (int i = 0; i < partials.size(); i++) {
UserInfo ui = partials.get(i);
removeUserStateLocked(ui.id);
}
}
}
// 更新一下OWNER用户的登陆时间
onUserForeground(UserHandle.USER_OWNER);
// 调用AppOpsManager(权限管理器)设置每个用户的权限
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
for (int i = 0; i < mUserIds.length; ++i) {
try {
mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
}
}
}
简单的说UMS
的初始化工作主要就是分析userlist.xml
文件、创建用户列表mUsers
以及设置用户权限。
UMS
中创建用户是在createUser()
方法中实现的:
@Override
public UserInfo createUser(String name, int flags) {
checkManageOrCreateUsersPermission(flags);
return createUserInternal(name, flags, UserHandle.USER_NULL);
}
--->
// UID 是 system UID 或 root's UID 或APP有permission.MANAGE_USERS权限
private static final void checkManageOrCreateUsersPermission(String message) {
if (!hasManageOrCreateUsersPermission()) {
throw new SecurityException(
"You either need MANAGE_USERS or CREATE_USERS permission to: " + message);
}
}
首先检查调用者的权限,UID 是 system UID 或 root’s UID 或APP有permission.MANAGE_USERS权限才有权限,否则抛出异常。然后就调用createUserInternal
来执行真正的创建工作。
===>createUserInternal
这个方法部分功能为:
userlist.xml
,注意,此时的userInfo.partial = true
,表示正在创建/data/user/
或者/mnt/expand/user/
PMS
的createNewUserLILPw
方法,这个函数会在新建用户的目录下面为所有应用创建数据目录userInfo.partial
设置为false
,创建用户的信息文件,比如0.xml
Intent.ACTION_USER_ADDE
代码如下:
private UserInfo createUserInternal(String name, int flags, int parentId) {
// 检查一下该用户是否被限制创建用户
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
UserManager.DISALLOW_ADD_USER, false)) {
return null;
}
// 检查是否是低内存设备
if (ActivityManager.isLowRamDeviceStatic()) {
return null;
}
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo = null;
final int userId;
try {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
UserInfo parent = null;
if (parentId != UserHandle.USER_NULL) {
parent = getUserInfoLocked(parentId);
if (parent == null) return null;
}
if (isManagedProfile && !canAddMoreManagedProfiles()) {
return null;
}
// 如果添加的不是Guest用户,也不是用户的profile,而且已经到达用户的上限,则不允许再添加
if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) {
return null;
}
// 如果添加的是Guest用户,但是系统中已经存在一个,则不允许再添加
if (isGuest && findCurrentGuestUserLocked() != null) {
return null;
}
// 得到新用户的Id
userId = getNextAvailableIdLocked();
// 为该新用户创建UserInfo
userInfo = new UserInfo(userId, name, null, flags);
// 设置序列号
userInfo.serialNumber = mNextSerialNumber++;
// 设置创建时间
long now = System.currentTimeMillis();
userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
// 设置partial,表示正在创建
userInfo.partial = true;
Environment.getUserSystemDirectory(userInfo.id).mkdirs();
// 放入mUsers列表
mUsers.put(userId, userInfo);
// 把用户信息写入userlist.xml
writeUserListLocked();
if (parent != null) {
if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.profileGroupId = parent.id;
scheduleWriteUserLocked(parent);
}
userInfo.profileGroupId = parent.profileGroupId;
}
// 创建用户目录
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
try {
final File userDir = Environment.getDataUserDirectory(volumeUuid,
userId);
// 创建userDir,如果volumeUuid为空,创建/data/user/<userId>/,不为空,创建/mnt/expand/<volumeUuid>/user/<userId>/
prepareUserDirectory(userDir);
enforceSerialNumber(userDir, userInfo.serialNumber);
} catch (IOException e) {
Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
}
}
// 调用PackageManagerService的createNewUserLILPw方法,此方法很重要,后面会单独分析
// 这个函数会在新建用户的userDir目录下面为所有应用创建数据
// 此方法将新用户可以使用的App在/data/user/<用户Id>文件夹下创建数据目录,目录名称为包名
mPm.createNewUserLILPw(userId);
// 创建成功,将partial改为false
userInfo.partial = false;
// 会调用writeUserLocked()创建xxx.xml,类似0.xml,xxx是用户Id
scheduleWriteUserLocked(userInfo);
// 更新mUserIds数组
updateUserIdsLocked();
Bundle restrictions = new Bundle();
// 添加为该用户设置的限制条件
mUserRestrictions.append(userId, restrictions);
}
}
// 发送用户创建完成的广播,广播附带用户的id
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);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return userInfo;
}
===>前面函数调用了 mPm.createNewUserLILPw(userId)
, PMS
中createNewUserLILPw
用于创建用户数据,由UMS
调用,代码如下:
/** Called by UserManagerService */
void createNewUserLILPw(int userHandle) {
if (mInstaller != null) {
//通过mInstaller调用守护进程installd执行mkuserconfig,创建用户配置文件。
mInstaller.createUserConfig(userHandle);
// 调用mSettings.createNewUserLILP为新用户中的应用创建应用数据目录
mSettings.createNewUserLILPw(this, mInstaller, userHandle);
// 设置默认的Browser
applyFactoryDefaultBrowserLPw(userHandle);
primeDomainVerificationsLPw(userHandle);
}
}
===> mSettings.createNewUserLILPw实现如下:
void createNewUserLILPw(PackageManagerService service, Installer installer, int userHandle) {
// 为每一个应用创建数据目录
for (PackageSetting ps : mPackages.values()) {
if (ps.pkg == null || ps.pkg.applicationInfo == null) {
continue;
}
// Only system apps are initially installed.
//为系统应用修改PackageUserState的installed标志
ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
// 通过mInstaller调用守护进程installd执行mkuserdata,为用户创建用户目录。
installer.createUserData(ps.volumeUuid, ps.name,
UserHandle.getUid(userHandle, ps.appId), userHandle,
ps.pkg.applicationInfo.seinfo);
}
// TODO
applyDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
writePackageListLPr(userHandle);
}
这个函数会在新建用户的目录下面为所有应用创建数据目录
用户切换流程:ActivityManager.switchUser--> ActivityManagerNative.getDefault().switchUser-->ActivityManagerService.switchUser
,代码如下:
--->ActivityManager.java:
public boolean switchUser(int userid) {
try {
return ActivityManagerNative.getDefault().switchUser(userid);
} catch (RemoteException e) {
return false;
}
}
--->ActivityManagerProxy.switchUser
public boolean switchUser(int userid) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userid);
mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
reply.recycle();
data.recycle();
return result;
}
-->AMS中的switchUser:
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;
}
// 该user只是一个user的profile,无法切换
if (userInfo.isManagedProfile()) {
Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
return false;
}
userName = userInfo.name;
// 把该用户Id记录到mTargetUserId变量
mTargetUserId = userId;
}
// 发送切换用户的消息
mUiHandler.removeMessages(START_USER_SWITCH_MSG);
mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
return true;
}
===>Handler
收到START_USER_SWITCH_MSG
消息后,会调用showUserSwitchDialog()
来弹出一个确认的对话框, 代码如下:
case START_USER_SWITCH_MSG: {
showUserSwitchDialog(msg.arg1, (String) msg.obj);
break;
---> showUserSwitchDialog代码如下:
private void showUserSwitchDialog(int userId, String userName) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
Dialog d = new UserSwitchingDialog(this, mContext, userId, userName,
true /* above system */);
d.show();
}
===>点击确定后最终会调用到startUser()
来执行切换用户的动作
--->UserSwitchingDialog.java
public void onWindowShown() {
// Slog.v(TAG, "onWindowShown called");
startUser();
}
--->
private boolean startUser(final int userId, final boolean foreground) {
//切换用话需要INTERACT_ACROSS_USERS_FULL权限
if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(msg);
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
// 如果当前用户已经是需要切换的用户,退出当前流程
final int oldUserId = mCurrentUserId;
if (oldUserId == userId) {
return true;
}
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
"startUser", false);
// 如果没有需要启动的用户的信息,则直接退出
final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
if (userInfo == null) {
return false;
}
// 如果是前台启动且是一份profile的话,则直接退出
if (foreground && userInfo.isManagedProfile()) {
return false;
}
// 如果是前台启动,则需要将屏幕冻结
if (foreground) {
mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
R.anim.screen_user_enter);
}
boolean needStart = false;
// 如果当前已经启动过的用户列表中没有该用户,则需要先启动该用户
if (mStartedUsers.get(userId) == null) {
mStartedUsers.put(userId, new UserState(new UserHandle(userId), false));
updateStartedUserArrayLocked();
needStart = true;
}
// 调整该用户在mUserLru列表中的位置,当前用户放到最后位置
final Integer userIdInt = Integer.valueOf(userId);
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
if (foreground) {
// 如果是前台切换,直接切换到需要启动的用户
mCurrentUserId = userId;
// 重置mTargetUserId,因为userId已经赋值给mCurrentUserId了
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
// 更新与该用户相关的profile列表
updateCurrentProfileIdsLocked();
// 在WindowManagerService中设置要启动的用户为当前用户
mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
// 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);
// 更新与该用户相关的profile列表
updateCurrentProfileIdsLocked();
mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
// 重新调整mUserLru
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
final UserState uss = mStartedUsers.get(userId);
if (uss.mState == UserState.STATE_STOPPING) {
// 如果该用户是正在停止,这个时候还没有发送ACTION_SHUTDOWN广播,则切换为正在运行
uss.mState = UserState.STATE_RUNNING;
// 更新mStartedUserArray列表
updateStartedUserArrayLocked();
needStart = true;
} else if (uss.mState == UserState.STATE_SHUTDOWN) {
// 如果该用户是正在停止,这个时候已经发送ACTION_SHUTDOWN广播,则切换为正在启动状态
uss.mState = UserState.STATE_BOOTING;
updateStartedUserArrayLocked();
needStart = true;
}
if (uss.mState == UserState.STATE_BOOTING) {
// 如果用户的状态是正在启动,则发送一个SYSTEM_USER_START_MSG消息,该消息的处理下面会介绍
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
}
if (foreground) {
// 发送SYSTEM_USER_CURRENT_MSG消息
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
oldUserId));
// 分别发送REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG,防止切换时间过长,后面会介绍
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);
}
if (needStart) {
// 发送一个 USER_STARTED 广播
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);
}
if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
// 当该用户还没有初始化,如果是普通用户则会发送ACTION_USER_INITIALIZE广播,如果是机主用户,则直接标记为已初始化
if (userId != UserHandle.USER_OWNER) {
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null,
new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
onUserInitialized(uss, foreground, oldUserId, userId);
}
}, 0, null, null, null, AppOpsManager.OP_NONE,
null, true, false, MY_PID, Process.SYSTEM_UID, userId);
// 标记为正在初始化,该变量会在completeSwitchAndInitialize()重置
uss.initializing = true;
} else {
getUserManagerLocked().makeInitialized(userInfo.id);
}
}
if (foreground) {
// 如果用户已经初始化过了,则设置为前台用户,后面会介绍这一部分
if (!uss.initializing) {
moveUserToForeground(uss, oldUserId, userId);
}
} else {
// 如果是后台启动用户,先加入到mStartingBackgroundUsers列表中去
mStackSupervisor.startBackgroundUserLocked(userId, uss);
}
if (needStart) {
Intent intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
broadcastIntentLocked(null, null, intent,
null, new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered, boolean sticky,
int sendingUser) throws RemoteException {
}
}, 0, null, null,
new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
===>下面来分析一下moveUserToForeground()
函数
void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
//从mStackSupervisor获取newUserId用户在切换之前的stack状态,以便将原来在前台的应用推到前台
boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss);
if (homeInFront) {
// 如果原来是从桌面切换的用户,则启动桌面
startHomeActivityLocked(newUserId, "moveUserToFroreground");
} else {
// 如果是其他应用,则将此应用推到前台
mStackSupervisor.resumeTopActivitiesLocked();
}
EventLogTags.writeAmSwitchUser(newUserId);
getUserManagerLocked().onUserForeground(newUserId);
// 发送ACTION_USER_BACKGROUND广播,通知和oldUserId相关的用户(包括profile)进入后台的消息
// 发送ACTION_USER_FOREGROUND广播,通知和newUserId相关的用户(包括profile)进入前台的消息
// 发送ACTION_USER_SWITCHED广播,通知用户切换
sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
}
UserManagerService
中删除用户是在removeUser()
方法中实现的:
public boolean removeUser(int userHandle) {
// 检查该进程是否具有删除用户的权限
checkManageUsersPermission("Only the system can remove users");
// 检查一下该用户是否被限制删除用户
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
UserManager.DISALLOW_REMOVE_USER, false)) {
return false;
}
long ident = Binder.clearCallingIdentity();
try {
final UserInfo user;
synchronized (mPackagesLock) {
user = mUsers.get(userHandle);
// 检查被删除的用户是不是管理员用户userHandle=0,检查用户列表中是否有该用户,以及该用户是否是正在被删除的用户
if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
return false;
}
// 将该用户放入mRemovingUserIds列表中,防止重复删除
// mRemovingUserIds中的数据会一直保存直到系统重启,防止Id被重复使用
mRemovingUserIds.put(userHandle, true);
try {
mAppOpsService.removeUser(userHandle);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
}
// 将partial设置为true,这样如果后面的过程意外终止导致此次删除失败,系统重启后还是会继续删除工作的
user.partial = true;
// 设置FLAG_DISABLED,禁止该用户
user.flags |= UserInfo.FLAG_DISABLED;
// 将上面更新的用户文件信息写入到xml文件中去
writeUserLocked(user);
}
// 如果该user是一个user的一份profile,则发出一个ACTION_MANAGED_PROFILE_REMOVED广播
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& user.isManagedProfile()) {
sendProfileRemovedBroadcast(user.profileGroupId, user.id);
}
// 调用AMS停止当前的用户,这部分后面会详细介绍
int res;
try {
res = ActivityManagerNative.getDefault().stopUser(userHandle,
// 设置回调函数,调用finishRemoveUser继续后面的删除工作
new IStopUserCallback.Stub() {
@Override
public void userStopped(int userId) {
finishRemoveUser(userId);
}
@Override
public void userStopAborted(int userId) {
}
});
} catch (RemoteException e) {
return false;
}
return res == ActivityManager.USER_OP_SUCCESS;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
ActivityManagerNative.getDefault().stopUser
执行完后 UMS
会继续执行删除工作。
void finishRemoveUser(final int userHandle) {
// Let other services shutdown any activity and clean up their state before completely
// wiping the user's system directory and removing from the user list
long ident = Binder.clearCallingIdentity();
try {
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
android.Manifest.permission.MANAGE_USERS,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
new Thread() {
public void run() {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
removeUserStateLocked(userHandle);
}
}
}
}.start();
}
},
null, Activity.RESULT_OK, null, null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
根据代码可以看到finishRemoveUser
方法只是发送了一个有序广播ACTION_USER_REMOVED
,同时注册了一个广播接收器,这个广播接收器是最后一个接收到该广播的接收器,这样做的目的是让关心该广播的其他接收器处理完之后, UMS
才会进行删除用户的收尾工作,即调用removeUserStateLocked
来删除用户的相关文件。
private void removeUserStateLocked(final int userHandle) {
// 调用mPm.cleanUpUserLILPw来删除用户目录/data/user/<用户id>/下面的应用数据,后面会详细介绍
mPm.cleanUpUserLILPw(this, userHandle);
// 从mUsers列表中移除该用户信息
mUsers.remove(userHandle);
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
// 删除用户信息文件 <用户id>.xml
userFile.delete();
// 更新userlist.xml文件,将删除调用的用户从中移除
writeUserListLocked();
// 更新mUserIds列表
updateUserIdsLocked();
// 删除用户目录以及该用户的所有文件
removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
UMS
的removeUser()
会调用AMS
的stopUser()
来处理停止用户的一些工作,在AMS
内部也会调用stopUser()
。该方法在进行了权限检查之后,主要的工作都是由stopUserLocked()
来完成的。
void finishUserStop(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
boolean stopped;
ArrayList<IStopUserCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
if (mStartedUsers.get(userId) != uss) {
stopped = false;
} else if (uss.mState != UserState.STATE_SHUTDOWN) {
stopped = false;
} else {
stopped = true;
// User can no longer run.
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLocked();
// 杀掉和该用户相关的所有进程
forceStopUserLocked(userId, "finish user");
}
// 清除用户相关的Recent Task列表
mRecentTasks.removeTasksForUserLocked(userId);
}
//调用在stopUserLocked中添加的回调函数
for (int i=0; i<callbacks.size(); i++) {
try {
if (stopped) callbacks.get(i).userStopped(userId);
else callbacks.get(i).userStopAborted(userId);
} catch (RemoteException e) {
}
}
if (stopped) {
// 回调各个SystemServer的onCleanupUser方法
mSystemServiceManager.cleanupUser(userId);
synchronized (this) {
mStackSupervisor.removeUserLocked(userId);
}
}
}
finishUserStop()
方法从mStartedUsers
和mUserLru
列表中删除该用户,更新mStartedUserArray
列表,清理和该用户有关的进程,发送Intent.ACTION_USER_STOPPED
广播来通知该用户已经停止,接下来清除用户相关的Recent Task
列表以及从mStackSupervisor
中删除用户的信息。
private void forceStopUserLocked(int userId, String reason) {
// 杀掉该用户相关的所有进程,具体流程会在ActivityManager相关文章中介绍
forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason);
// 发出Intent.ACTION_USER_STOPPED广播
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
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, UserHandle.USER_ALL);
}
删除用户数据cleanUpUserLILPw()
void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
mDirtyUsers.remove(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
if (mInstaller != null) {
// Technically, we shouldn't be doing this with the package lock
// held. However, this is very rare, and there is already so much
// other disk I/O going on, that we'll let it slide for now.
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
mInstaller.removeUserDataDirs(volumeUuid, userHandle);
}
}
mUserNeedsBadging.delete(userHandle);
removeUnusedPackagesLILPw(userManager, userHandle);
}
删除用户的工作比较简单,删除用户的数据。同时调用mSettings.removeUserLPw(userHandle)
来删除和 PMS 中和用户相关的信息。
void removeUserLPw(int userId) {
// 删除每个应用中的该用户的信息
Set<Entry<String, PackageSetting>> entries = mPackages.entrySet();
for (Entry<String, PackageSetting> entry : entries) {
entry.getValue().removeUser(userId);
}
mPreferredActivities.remove(userId);
File file = getUserPackagesStateFile(userId);
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemoved(userId);
// 更新/data/system/packages.list文件
writePackageListLPr();
}
VUserHandle
基本全参考了UserHandle
,相同的方法接口
// packageName<-->uid映射
private final HashMap<String, Integer> mSharedUserIdMap = new HashMap<>();
private int mFreeUid = FIRST_APPLICATION_UID; // 10000
uid
的管理类
mSharedUserIdMap
记录了包名---uid
的映射uid
从10001(FIRST_APPLICATION_UID
+1)开始依次递增,uid
后,都把mSharedUserIdMap
+ mFreeUid
写入到/data/data/packageName/virtual/data/app/system/uid-list.ini
中,同时把先前的uid-list.ini
保存在/data/data/packageName/virtual/data/app/system/uid-list.ini.bak
,关键方法如下: public int getOrCreateUid(VPackage pkg) {
synchronized (mSharedUserIdMap) {
String sharedUserId = pkg.mSharedUserId;
if (sharedUserId == null) {
sharedUserId = pkg.packageName; // eg: com.hgy.ndk
}
Integer uid = mSharedUserIdMap.get(sharedUserId);
if (uid != null) {
return uid;// 此包名对应的uid已创建
}
int newUid = ++mFreeUid;
mSharedUserIdMap.put(sharedUserId, newUid);
save();
return newUid;
}
}
类似于系统的UserInfo
,
类似于系统的UMS
,基于IUserManager.aidl
,IUserManager.aidl
只提供了部分接口,如setUserRestrictions
等限制权限的函数是没有的。
它的构造流程基本抄了系统的UMS
,可以参考前面的流程
==>同样在PMS
的systemReady
初始化,但它没有作为成员变量保存到PMS
中
public static void systemReady() {
new VUserManagerService(VirtualCore.get().getContext(), get(), new char[0], get().mPackages);
}
==>构造函数和系统的UMS
基本一样,这个方法部分功能为:
PMS
对象到mPm
成员/data/data/ xxx /virtual/data/system/users/0
/data/data/ xxx /virtual/data/system/users/userlist.xml
,这个文件保存了系统中所有用户的id信息userlist.xml
文件,将用户的信息解析成UserInfo
,并保存在mUsers
列表中userlist.xml
文件移除所有部分创建或部分删除的用户记录代码如下:
private VUserManagerService(Context context, VPackageManagerService pm,
Object installLock, Object packagesLock,
File dataDir, File baseUserPath) { // 109
// dataDir=/data/data/io.busniess.vatools/virtual/data
// baseUserPath=/data/data/io.busniess.vatools/virtual/data/user
mContext = context;
mPm = pm;
mInstallLock = installLock;
mPackagesLock = packagesLock;
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
// mUsersDir=/data/data/io.busniess.vatools/virtual/data/system/users
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
// 创建第一个用户的目录/data/data/io.busniess.vatools/virtual/data/system/users/0
File userZeroDir = new File(mUsersDir, "0");
userZeroDir.mkdirs();
mBaseUserPath = baseUserPath;
// FileUtils.setPermissions(mUsersDir.toString(),
// FileUtils.S_IRWXU|FileUtils.S_IRWXG
// |FileUtils.S_IROTH|FileUtils.S_IXOTH,
// -1, -1);
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
// 读取userlist.xml文件,将用户的信息保存在mUsers列表中
readUserListLocked();
// 去掉任何部分创建/部分删除的用户。
ArrayList<VUserInfo> partials = new ArrayList<VUserInfo>();
for (int i = 0; i < mUsers.size(); i++) {
VUserInfo ui = mUsers.valueAt(i);
if (ui.partial & i != 0) {
partials.add(ui);
}
}
for (int i = 0; i < partials.size(); i++) {
VUserInfo ui = partials.get(i);
VLog.w(LOG_TAG, "Removing partially created user #" + i
+ " (name=" + ui.name + ")");
removeUserStateLocked(ui.id);
}
sInstance = this;
}
}
}
===>writeUserListLocked
写入/data/data/ xxx/virtual/data/system/users/userlist.xml
===>writeUserLocked
把VUserInfo
写入到data/data/io.busniess.vatools/virtual/data/system/users
目录下
===>fallbackToSingleUserLocked
创建第一个user并调用writeUserListLocked
写入userlist.xml
,并调用writeUserLocked
写入
参考UserManagerStub.java
, 主要实现:
1.将 setApplicationRestrictions
,getApplicationRestrictions
,getApplicationRestrictionsForUser
的包名换成io.busniess.va
2.将 getProfileParent
,getUserIcon
,getDefaultGuestRestrictions
,setDefaultGuestRestrictions
,removeRestrictions
,createUser,等都返回null