从Android 4.0开始,Google就开始在Android上布局多用户,UserManager因此而诞生,然而此时还没有对应的Binder服务。真正支持多用户是从Android 4.2 开始,即便如此,系统中也依然存在各种Bug和兼容性问题。直到Android 6.0,Android多用户才比较完善,国内外的厂家也纷纷开始针对多用户这个噱头来做各种 “花里胡哨” 的操作,“手机分身”、“分身应用”、“应用双开” 应运而生,不得不说,国内的厂家在多用户这方面定制化到如今已经非常稳定和完善了。
对于Android中的每个进程,都有一个单独的Uid、Gid以及Gids集合(这个地方有点疑惑,Android Framework中的权限控制不依赖这个Gids,这个是Linux上有的东西,有待考证),通过这三者,Android系统实现了一套文件和数据访问权限规则系统。如:
这里只截取了部分权限,我们发现每个我们常见的权限都可能对应一个或多个group gid,而我们上面说的gids就是由这个group gid生成的集合。具体的权限读取和gids生成流程如下:
Android在创建每个用户时,都会分配一个整型的userId。对于主用户(正常下的默认用户)来说,userId为0,之后创建的userId将从10开始计算,每增加一个userId加1:
为了多用户下的数据安全性,在每个新用户创建之初,不管是外部存储(External Storage)还是app data目录,Android都为其准备了独立的文件存储。
新用户创建时,Android在 “/storage/emulated” 目录下为每个用户都创建了名为用户id的目录,当我们在代码中使用 “Environment.getExternalStorageDirectory().absolutePath” 获取外部存储路径时,返回的就是当前用户下的对应目录(如:userId = 11, 则返回为 “/storage/emulated/11”)。
与External Storage相同,新用户创建时,Android也会在 “data/user” 目录下创建了名为userId的目录,用于存储该用户中所有App的隐私数据,如果在代码中使用 “Context.getFilesDir()” 来获取应用的data目录,不同User下也会有不同。
注:在Android中,应用的uid是和当前的用户有关的,同一个应用具有相同的appId,其uid的计算方式为: uid = userId * 1000000 + appId
,在主用户中,uid = appId。
3.3 独立的权限控制
不同用户具有的权限不同,如:访客用户的默认权限限制就有:
perseus: / $ dumpsys user
. . .
Guest restrictions:
no_sms
no_install_unknown_sources
no_config_wifi
no_outgoing_calls
(注:使用 “adb shell dumpsys user” 可以查看所有的用户信息,如userId、name、restrictions等)
这些权限可以在创建用户时规定,也可以后期由系统动态设置。
不同用户下App的应用权限是独立的
前面说到,uid与userId存在一种计算关系(uid = userId * 1000000 + appId),而在系统中对于权限控制也是根据uid和对应的userId来判定的,因此不同用户下相同应用可以具有不同的权限。
3.4 App安装的唯一性
虽然前面说到,App的文件存储和数据目录在不同用户下都是独立的,但是对于App的安装,多个用户下同一个App却保持着同一个安装目录,即:
普通三方app:/data/app/ 普通系统应用:/system/app/ 特权系统应用:/system/priv-app/
root@virgo : / # ls / data/ app/ com. ulangch. multiuser- 1 /
base. apk
lib
oat
拓展:权限在声明时安全等级(protectionLevel)分为3类:
< permission android: name= "android.permission.READ_EXTERNAL_STORAGE"
android: permissionGroup= "android.permission-group.STORAGE"
android: label= "@string/permlab_sdcardRead"
android: description= "@string/permdesc_sdcardRead"
android: protectionLevel= "dangerous" / >
< ! -- Allows applications to access information about Wi- Fi networks.
< p> Protection level: normal
-- >
< permission android: name= "android.permission.ACCESS_WIFI_STATE"
android: description= "@string/permdesc_accessWifiState"
android: label= "@string/permlab_accessWifiState"
android: protectionLevel= "normal" / >
< ! -- @SystemApi Allows applications to set the system time.
< p> Not for use by third- party applications. -- >
< permission android: name= "android.permission.SET_TIME"
android: protectionLevel= "signature|privileged" / >
normal:普通权限,在AndroidManifest.xml中声明就可以获取的权限,如INTERNET权限 dangerous:敏感权限,需要动态申请告知用户才能获取 signature|privileged:具有系统签名的系统应用才可以获取的权限,对应上方的 “/system/priv-app”
因此,多用户下的应用其实只安装一次,不同用户下同一个应用的版本和签名都应该相同,不同用户下相同App能够独立运行是因为系统为他们创造了不同的运行环境和权限。
3.5 kernel及系统进程的不变性
在不同用户下,虽然能够看到不同的桌面,不同的运行环境,一切都感觉是新的,但是我们系统本身并没有发生改变,kernel进程、system_server进程以及所有daemon进程依然是同一个,并不会重启。
而如果我们在不同用户中开启相同的app,我们可以看到可以有多个app进程,而他们的父进程都是同一个,即 zygote:
root@virgo : / # ps | grep multiuser
u11_a110 9805 357 788188 54628 sys_epoll_ b6d2c99c S com. ulangch. multiuser
u10_a110 13335 357 816516 54588 sys_epoll_ b6d2c99c S com. ulangch. multiuser
u0_a110 13746 357 788448 54056 sys_epoll_ b6d2c99c S com. ulangch. multiuser
root@virgo : / # ps | grep 357
root 357 1 1542716 65560 poll_sched b6d2cb64 S zygote
四、流程分析
多用户的创建、启动、停止等行为是系统级的,因此只有具有root、system权限的进程才能操作。
3.1 多用户的创建
adb shell pm create-user [–profileOf USER_ID] [–managed] USER_NAME
多用户的创建流程主要在UserManagerService.createUserInternalUnchecked()方法中,方法太长,截取部分分析:
private UserInfo createUserInternalUnchecked ( String name, int flags, int parentId, String[ } {
final boolean isGuest = ( flags & UserInfo. FLAG_GUEST) != 0 ;
final boolean isManagedProfile = ( flags & UserInfo. FLAG_MANAGED_PROFILE) != 0 ;
final boolean isRestricted = ( flags & UserInfo. FLAG_RESTRICTED) != 0 ;
final boolean isDemo = ( flags & UserInfo. FLAG_DEMO) != 0 ; ] disallowedPackages) ;
if ( isManagedProfile && ! canAddMoreManagedProfiles ( parentId, false ) ) {
Log. e ( LOG_TAG, "Cannot add more managed profiles for user " + parentId) ;
return null;
}
if ( ! isGuest && ! isManagedProfile && ! isDemo && isUserLimitReached ( ) ) {
return null;
}
if ( isGuest && findCurrentGuestUser ( ) != null) {
return null;
}
userId = getNextAvailableId ( ) ;
Environment. getUserSystemDirectory ( userId) . mkdirs ( ) ;
synchronized ( mUsersLock) {
userInfo = new UserInfo ( userId, name, null, flags) ;
userInfo. serialNumber = mNextSerialNumber++ ;
long now = System. currentTimeMillis ( ) ;
userInfo. creationTime = ( now > EPOCH_PLUS_30_YEARS) ? now : 0 ;
userInfo. partial = true ;
userInfo. lastLoggedInFingerprint = Build. FINGERPRINT;
if ( isManagedProfile && parentId != UserHandle. USER_NULL) {
userInfo. profileBadge = getFreeProfileBadgeLU ( parentId) ;
}
userData = new UserData ( ) ;
userData. info = userInfo;
mUsers. put ( userId, userData) ;
}
writeUserLP ( userData) ;
writeUserListLP ( ) ;
final StorageManager storage = mContext. getSystemService ( StorageManager. class ) ;
storage. createUserKey ( userId, userInfo. serialNumber, userInfo. isEphemeral ( ) ) ;
mUserDataPreparer. prepareUserData ( userId, userInfo. serialNumber,
StorageManager. FLAG_STORAGE_DE | StorageManager. FLAG_STORAGE_CE) ;
mPm. createNewUser ( userId, disallowedPackages) ;
userInfo. partial = false ;
synchronized ( mPackagesLock) {
writeUserLP ( userData) ;
}
updateUserIds ( ) ;
mPm. onNewUserCreated ( userId) ;
Intent addedIntent = new Intent ( Intent. ACTION_USER_ADDED) ;
addedIntent. putExtra ( Intent. EXTRA_USER_HANDLE, userId) ;
mContext. sendBroadcastAsUser ( addedIntent, UserHandle. ALL,
android. Manifest. permission. MANAGE_USERS) ;
}
从上面的代码分析可以看出,用户创建的过程主要是应用运行环境(文件系统、权限等)的准备过程,主要可以分为以下几个关键的步骤:(忽略访客用户相关的操作)
1. 为新用户创建一个新的userId (新用户的userId从10开始递增 )
2. 固化新用户信息和创建状态
构造包含新用户信息的UserData,并固化到 “/data/system/users/${userId}.xml”
root@virgo : / # cat data/ system/ users/ 10. xml
< ? xml version= '1.0' encoding= 'utf-8' standalone= 'yes' ? >
< user id= "10" serialNumber= "10" flags= "17" created= "1561361447098" lastLoggedIn= "1561460313625" >
< name> security space< / name>
< restrictions no_install_unknown_sources= "false" no_usb_file_transfer= "false" no_debugging_features= "false" / >
< / user>
将新创建新userId固化到 “/data/system/users/userlist.xml”
root@virgo:/ # cat data/system/users/userlist.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<users nextSerialNumber="12" version="5">
<guestRestrictions>
<restrictions no_config_wifi="true" no_outgoing_calls="true" no_sms="true" />
</guestRestrictions>
<user id="0" />
<user id="10" />
<user id="11" />
</users>
3. 准备文件系统:
通过vold(Android存储守护进程)为新用户进行文件系统加密 创建"/data/system/users/${userId}" 并设置 “0700” 权限 创建 “/data/misc/users/${userId}” 并设置 “0750” 权限
4. 为已安装应用准备数据目录并记录其组件和默认权限配置:
在 “/data/user/${userId}/” 下创建各个已安装应用的package目录 在 “data/system/users/${userId}/package-restrictions.xml” 中写入非默认启动组件的信息
root@virgo : / # cat data/ system/ users/ 10 / package - restrictions. xml
< ? xml version= '1.0' encoding= 'utf-8' standalone= 'yes' ? >
< package - restrictions>
< pkg name= "com.ss.android.ugc.aweme" stopped= "true" >
< enabled- components>
< item name= "com.bytedance.performance.doctorx.leakcanary.internal.DisplayLeakActivity" / >
< item name= "com.bytedance.ttnet.hostmonitor.ConnectivityReceiver" / >
< item name= "com.bytedance.performance.doctorx.leakcanary.internal.RequestStoragePermissionActivity" / >
< item name= "com.huawei.android.pushagent.PushBootReceiver" / >
< / enabled- components>
< / pkg>
. . .
更新"data/system/packages.list",主要是最后一串gids可能会改变。(这个改变的可能性是根据permUser的配置来决定,目前使用6.0的小米Note是没有改变的)
root@virgo : / # cat data/ system/ packages. list
com. miui. screenrecorder 1000 0 / data/ data/ com. miui. screenrecorder platform 2001 , 3002 , 1023 , 1015 , 3003 , 3001 , 1021 , 3004 , 3005 , 1000 , 2002 , 3009 , 1010 , 1007 , 3006 , 3007
com. ss. android. ugc. aweme 10132 1 / data/ data/ com. ss. android. ugc. aweme default 3002 , 3003 , 3001
com. ulangch. multiuser 10110 1 / data/ data/ com. ulangch. multiuser default none
5. 固化新用户创建完成的状态、通知PMS为新用户和应用赋予默认的权限
6. 发送 “ACTION_USER_ADDED” 广播,新用户创建完成
3.2 多用户的切换
adb shell am start-user: start USER_ID in background if it is currently stopped, use switch-user if you want to start the user in foreground. adb shell am switch-user: switch to put USER_ID in the foreground, starting execution of that user if it is currently stopped.
Android多用户的切换函数入口ActivityManagerService.switchUser方法:
@Override
public boolean switchUser ( final int targetUserId) {
enforceShellRestriction ( UserManager. DISALLOW_DEBUGGING_FEATURES, targetUserId) ;
int currentUserId;
UserInfo targetUserInfo;
synchronized ( this ) {
currentUserId = mUserController. getCurrentUserIdLocked ( ) ;
targetUserInfo = mUserController. getUserInfo ( targetUserId) ;
mUserController. setTargetUserIdLocked ( targetUserId) ;
}
if ( mUserController. mUserSwitchUiEnabled) {
UserInfo currentUserInfo = mUserController. getUserInfo ( currentUserId) ;
Pair< UserInfo, UserInfo> userNames = new Pair < > ( currentUserInfo, targetUserInfo) ;
mUiHandler. removeMessages ( START_USER_SWITCH_UI_MSG) ;
mUiHandler. sendMessage ( mHandler. obtainMessage (
START_USER_SWITCH_UI_MSG, userNames) ) ;
} else {
mHandler. removeMessages ( START_USER_SWITCH_FG_MSG) ;
mHandler. sendMessage ( mHandler. obtainMessage (
START_USER_SWITCH_FG_MSG, targetUserId, 0 ) ) ;
}
return true ;
}
AMS的startUser方法只是判断了是否展示切换用户的Dialog,最终都会调用到UserController.startUser方法中:
boolean startUser ( final int userId, final boolean foreground) {
if ( mInjector. 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) ;
}
Slog. i ( TAG, "Starting userid:" + userId + " fg:" + foreground) ;
final long ident = Binder. clearCallingIdentity ( ) ;
try {
synchronized ( mLock) {
final int oldUserId = mCurrentUserId;
if ( oldUserId == userId) {
return true ;
}
if ( foreground) {
mInjector. getActivityStackSupervisor ( ) . setLockTaskModeLocked (
null, ActivityManager. LOCK_TASK_MODE_NONE, "startUser" , false ) ;
}
final UserInfo userInfo = getUserInfo ( userId) ;
if ( userInfo == null) {
Slog. w ( TAG, "No user info for user #" + userId) ;
return false ;
}
if ( foreground && userInfo. isManagedProfile ( ) ) {
Slog. w ( TAG, "Cannot switch to User #" + userId + ": not a full user" ) ;
return false ;
}
if ( foreground && mUserSwitchUiEnabled) {
mInjector. getWindowManager ( ) . startFreezingScreen (
R. anim. screen_user_exit, R. anim. screen_user_enter) ;
}
boolean needStart = false ;
if ( mStartedUsers. get ( userId) == null) {
UserState userState = new UserState ( UserHandle. of ( userId) ) ;
mStartedUsers. put ( userId, userState) ;
mInjector. getUserManagerInternal ( ) . setUserState ( userId, userState. state) ;
updateStartedUserArrayLocked ( ) ;
needStart = true ;
}
final UserState uss = mStartedUsers. get ( userId) ;
final Integer userIdInt = userId;
mUserLru. remove ( userIdInt) ;
mUserLru. add ( userIdInt) ;
if ( foreground) {
mCurrentUserId = userId;
mInjector. updateUserConfigurationLocked ( ) ;
mTargetUserId = UserHandle. USER_NULL;
updateCurrentProfileIdsLocked ( ) ;
mInjector. getWindowManager ( ) . setCurrentUser ( userId, mCurrentProfileIds) ;
if ( mUserSwitchUiEnabled) {
mInjector. getWindowManager ( ) . setSwitchingUser ( true ) ;
mInjector. getWindowManager ( ) . lockNow ( null) ;
}
} else {
final Integer currentUserIdInt = mCurrentUserId;
updateCurrentProfileIdsLocked ( ) ;
mInjector. getWindowManager ( ) . setCurrentProfileIds ( mCurrentProfileIds) ;
mUserLru. remove ( currentUserIdInt) ;
mUserLru. add ( currentUserIdInt) ;
}
if ( uss. state == UserState. STATE_STOPPING) {
uss. setState ( uss. lastState) ;
mInjector. getUserManagerInternal ( ) . setUserState ( userId, uss. state) ;
updateStartedUserArrayLocked ( ) ;
needStart = true ;
} else if ( uss. state == UserState. STATE_SHUTDOWN) {
uss. setState ( UserState. STATE_BOOTING) ;
mInjector. getUserManagerInternal ( ) . setUserState ( userId, uss. state) ;
updateStartedUserArrayLocked ( ) ;
needStart = true ;
}
if ( uss. state == UserState. STATE_BOOTING) {
mInjector. getUserManager ( ) . onBeforeStartUser ( userId) ;
mHandler. sendMessage ( mHandler. obtainMessage ( SYSTEM_USER_START_MSG, userId, 0 ) ) ;
}
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) ;
}
if ( needStart) {
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) ;
mInjector. broadcastIntentLocked ( intent,
null, null, 0 , null, null, null, AppOpsManager. OP_NONE,
null, false , false , MY_PID, SYSTEM_UID, userId) ;
}
if ( foreground) {
moveUserToForegroundLocked ( uss, oldUserId, userId) ;
} else {
finishUserBoot ( 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) ;
mInjector. broadcastIntentLocked ( 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, SYSTEM_UID, UserHandle. USER_ALL) ;
}
}
} finally {
Binder. restoreCallingIdentity ( ident) ;
}
return true ;
}
方法很长,涉及到AMS和WMS的方法分支也很多。切换分为前台切换和后台切换,这里从前台切换侧并且对用户未启动的情况总结下关键的切换过程:
1. 切换前冻结屏幕,禁止一切输入操作
void startFreezingDisplayLocked ( boolean inTransaction, int exitAnim, int enterAnim, DisplayContent displayContent) {
mInputMonitor. freezeInputDispatchingLw ( ) ;
mPolicy. setLastInputMethodWindowLw ( null, null) ;
if ( mAppTransition. isTransitionSet ( ) ) {
mAppTransition. freeze ( ) ;
}
displayContent. updateDisplayInfo ( ) ;
screenRotationAnimation = new ScreenRotationAnimation ( mContext, displayContent, mFxSession, inTransaction, mPolicy. isDefaultOrientationForced ( ) , isSecure, this ) ;
mAnimator. setScreenRotationAnimationLocked ( mFrozenDisplayId, screenRotationAnimation) ;
}
上述这个过程在屏幕旋转的过程中也会执行,因此截取屏幕并展示也是采用和横竖屏切换一样的方式(ScreenRotationAnimation):
int flags = SurfaceControl. HIDDEN;
if ( isSecure) {
flags |= SurfaceControl. SECURE;
}
if ( DEBUG_SURFACE_TRACE) {
mSurfaceControl = new SurfaceTrace ( session, "ScreenshotSurface" ,
mWidth, mHeight,
PixelFormat. OPAQUE, flags) ;
Slog. w ( TAG, "ScreenRotationAnimation ctor: displayOffset="
+ mOriginalDisplayRect. toShortString ( ) ) ;
} else {
mSurfaceControl = new SurfaceControl ( session, "ScreenshotSurface" ,
mWidth, mHeight,
PixelFormat. OPAQUE, flags) ;
}
Surface sur = new Surface ( ) ;
sur. copyFrom ( mSurfaceControl) ;
SurfaceControl. screenshot ( SurfaceControl. getBuiltInDisplay (
SurfaceControl. BUILT_IN_DISPLAY_ID_MAIN) , sur) ;
mSurfaceControl. setLayerStack ( display. getLayerStack ( ) ) ;
mSurfaceControl. setLayer ( SCREEN_FREEZE_LAYER_SCREENSHOT) ;
mSurfaceControl. setAlpha ( 0 ) ;
mSurfaceControl. show ( ) ;
sur. destroy ( ) ;
Start1:如果是待启动用户,则初始化待启动用户的状态为STATE_BOOTING,
2. 为待切换用户更改系统配置,设置Keyguard
从SettingsProvider读取待切换用户的字体、语言、地区等配置并更新到系统。如果是初创用户,则字体使用默认配置,语言和地区使用当前用户的配置 为待切换用户更新资源:如Attributes、Drawable、Color、Animator、StateList等。(有兴趣可以重点看下AMS的updateGlobalConfiguration()方法)
修改当前用户下所有Window的可见性,启动Keyguard,切换过程中关闭Keyguard的指纹监听,并设置锁屏
注:在Android8.0以前,Keyguard是一个单独的System App,8.0后将其移至SystemUI中。该模块的功能主要有:1. 展示和隐藏锁屏界面;2. 认证和校验锁屏密码、指纹密码等
Start2:如果是待启动用户
为待启动用户设置权限,校验或准备待启动用户的App存储目录 通知系统所有服务新用户正在启动(如JobSchedulerService会根据Job对应的用户是否启动来决定Job的维护)
3. 并行通知系统所有服务用户开始切换:
系统所有服务及相关监听者在收到开始切换的消息后进行一系列的操作也是用户切换所要完成的核心任务。
4. 设置切换超时定时器
设置3s的延迟消息,如果3s内没有完成用户切换(取消该消息),则终止切换过程并执行UserController.continueUserSwitch()方法。(在步骤3中所有系统服务及相关监听者完成切换任务后,也会执行UserController.continueUserSwitch()方法)
5. 将待切换用户拉到前台
stop当前用户下所有的Activity 修改所有ActivityStack中TaskRecord的顺序,将切换用户或者在两个用户中都能运行的Task移动到栈顶 将最顶端Task对应的Window移动到最顶端 取出切换应用之前存在的前台Activity置于前台并resume,如果没有前台应用,则启动HomeActivity 发送用户切换广播。(如果是后台切换,则发送ACTION_USER_BACKGROUND,如果是后台切换,则发送ACTION_USER_FOREGROUND和ACTION_USER_SWITCHED)
void moveUserToForegroundLocked ( UserState uss, int oldUserId, int newUserId) {
boolean homeInFront =
mInjector. getActivityStackSupervisor ( ) . switchUserLocked ( newUserId, uss) ;
if ( homeInFront) {
mInjector. startHomeActivityLocked ( newUserId, "moveUserToForeground" ) ;
} else {
mInjector. getActivityStackSupervisor ( ) . resumeFocusedStackTopActivityLocked ( ) ;
}
EventLogTags. writeAmSwitchUser ( newUserId) ;
sendUserSwitchBroadcastsLocked ( oldUserId, newUserId) ;
}
ActivityStackSupervisor.switchUserLocked():
boolean switchUserLocked ( int userId, UserState uss) {
final int focusStackId = mFocusedStack. getStackId ( ) ;
moveTasksToFullscreenStackLocked ( DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID) ;
removeStackLocked ( PINNED_STACK_ID) ;
mUserStackInFront. put ( mCurrentUser, focusStackId) ;
final int restoreStackId = mUserStackInFront. get ( userId, HOME_STACK_ID) ;
mCurrentUser = userId;
mStartingUsers. add ( uss) ;
for ( int displayNdx = mActivityDisplays. size ( ) - 1 ; displayNdx >= 0 ; -- displayNdx) {
final ArrayList< ActivityStack> stacks = mActivityDisplays. valueAt ( displayNdx) . mStacks;
for ( int stackNdx = stacks. size ( ) - 1 ; stackNdx >= 0 ; -- stackNdx) {
final ActivityStack stack = stacks. get ( stackNdx) ;
stack. switchUserLocked ( userId) ;
TaskRecord task = stack. topTask ( ) ;
if ( task != null) {
stack. positionChildWindowContainerAtTop ( task) ;
}
}
}
ActivityStack stack = getStack ( restoreStackId) ;
if ( stack == null) {
stack = mHomeStack;
}
final boolean homeInFront = stack. isHomeStack ( ) ;
if ( stack. isOnHomeDisplay ( ) ) {
stack. moveToFront ( "switchUserOnHomeDisplay" ) ;
} else {
resumeHomeStackTask ( null, "switchUserOnOtherDisplay" ) ;
}
return homeInFront;
}
6. 步骤3完成或者4中切换超时消息到达时需要继续进行的切换操作(continueUserSwitch)
解冻屏幕和输入 设置Keyguard,如果切换用户设置了指纹,则需要开始监听指纹信息 通知监听者用户已经完成了切换
UserController.continueUserSwitch()方法执行流程:
7. 完成切换用户
如果是后台切换,则直接调用UserController.finishUserBoot()方法 如果是前台切换,ActivityThread会在handleResumeActivity时设置Main线程MessageQueue的mIdleHandlers,在MessageQueue执行next()方法会检查该列表并最终调用到AMS的activityIdle()方法中,此时会检查正在切换的用户列表并调用最终调用到UserController.finishUserBoot()方法 设置切换用户的状态为STATE_RUNNING_LOCKED
前台切换情况下finishUserBoot()方法的调用流程:
Start3:如果是新启动的用户,则通知系统所有用户监听者用户已经启动,并发送ACTION_LOCKED_BOOT_COMPLETED广播,在Keyguard第一次解锁时,会发送ACTION_BOOT_COMPLETED广播
3.3 多用户的删除
adb shell pm remove-user ${userId}
入口是UserManagerService.removeUser(),这里不详细分析,与创建于切换用户的流程类似。
3.4 多用户的真面目
从上面对Android多用户的创建和切换流程来看,我们可以总结出:
多用户其实是系统为应用的data目录和storage目录分配了一份不同且独立的存储空间,不同用户下的存储空间互不影响且没有权限访问。同时,系统中的AMS、PMS、WMS等各大服务都会针对userId/UserHandle进行多用户适配,并在用户启动、切换、停止、删除等生命周期时做出相应策略的改变。通过以上两点,Android创造出来一个虚拟的多用户运行环境。
五、多用户下的四大组件和数据共享
5.1 获取当前用户userId的方式
UserHandle中提供myUserId()方法,但是被hide的,可以反射获取:(或者直接根据uid / 100000计算)
private fun readUserIdByReflect ( ) : Int {
var userId = 0
try {
val clz = UserHandle: : class . java
val myUserIdMethod = clz. getDeclaredMethod ( "myUserId" )
userId = myUserIdMethod. invoke ( null) as Int
Log. i ( "ulangch-r" , "userId=$userId" )
} catch ( e : Exception) {
}
return userId
}
5.2 跨用户启动Activity
Activity/Context提供了startActivityAsUser() 方法,可以传入对应用户的UserHandle来达到跨用户启动Activity的目的,Context中对该方法进行了注释:
@RequiresPermission ( android. Manifest. permission. INTERACT_ACROSS_USERS_FULL)
public void startActivityAsUser ( @RequiresPermission Intent intent, UserHandle user) {
throw new RuntimeException ( "Not implemented. Must override in a subclass." ) ;
}
只有具有"android.Manifest.permission.INTERACT_ACROSS_USERS_FULL"的系统应用才可以调用该方法,经过反射测试,的确会抛出SecurityException:
07 - 01 20 : 59 : 40.445 25832 25832 W System. err: Caused by: java. lang. SecurityException: Permission Denial: startActivityAsUser asks to run as user 0 but is calling from user 13 ; this requires android. permission. INTERACT_ACROSS_USERS_FULL
07 - 01 20 : 59 : 40.445 25832 25832 W System. err: at android. os. Parcel. createException ( Parcel. java: 1953 )
07 - 01 20 : 59 : 40.445 25832 25832 W System. err: at android. os. Parcel. readException ( Parcel. java: 1921 )
07 - 01 20 : 59 : 40.445 25832 25832 W System. err: at android. os. Parcel. readException ( Parcel. java: 1871 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at android. app. IActivityManager$Stub$Proxy. startActivityAsUser ( IActivityManager. java: 6787 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at android. app. Instrumentation. execStartActivity ( Instrumentation. java: 1887 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at android. app. Activity. startActivityAsUser ( Activity. java: 4782 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: . . . 16 more
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: Caused by: android. os. RemoteException: Remote stack trace:
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at com. android. server. am. UserController. handleIncomingUser ( UserController. java: 1581 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at com. android. server. am. ActivityStartController. checkTargetUser ( ActivityStartController. java: 240 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at com. android. server. am. ActivityManagerService. startActivityAsUser ( ActivityManagerService. java: 5309 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at com. android. server. am. ActivityManagerService. startActivityAsUser ( ActivityManagerService. java: 5298 )
07 - 01 20 : 59 : 40.446 25832 25832 W System. err: at android. app. IActivityManager$Stub. onTransact$startActivityAsUser$( IActivityManager. java: 11005 )
AMS中的权限检查抛出异常,具有 “android.permission.INTERACT_ACROSS_USERS_FULL” 和 “android.permission.INTERACT_ACROSS_USERS” 权限的系统应用才可以,以startActivityAsUser为例:
@Override
public final int startActivityAsUser ( IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller ( "startActivity" ) ;
userId = mUserController. handleIncomingUser ( Binder. getCallingPid ( ) , Binder. getCallingUid ( ) , userId, false , ALLOW_FULL_ONLY, "startActivity" , null) ;
return mActivityStarter. startActivityMayWait ( caller, - 1 , callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, null, null, bOptions, false , userId, null, "startActivityAsUser" ) ;
}
最终会使用UserController.handleIncomingUser() 方法来做跨用户的权限检查:
int handleIncomingUser ( int callingPid, int callingUid, int userId, boolean allowAll, int allowMode, String name, String callerPackage) {
final int callingUserId = UserHandle. getUserId ( callingUid) ;
if ( callingUserId == userId) {
return userId;
}
int targetUserId = unsafeConvertIncomingUserLocked ( userId) ;
if ( callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
if ( mInjector. checkComponentPermission ( INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, - 1 , true ) == PackageManager. PERMISSION_GRANTED) {
allow = true ;
} else if ( allowMode == ALLOW_FULL_ONLY) {
allow = false ;
} else if ( mInjector. checkComponentPermission ( INTERACT_ACROSS_USERS, callingPid, callingUid, - 1 , true ) != PackageManager. PERMISSION_GRANTED) {
allow = false ;
} else if ( allowMode == ALLOW_NON_FULL) {
allow = true ;
} else if ( allowMode == ALLOW_NON_FULL_IN_PROFILE) {
allow = isSameProfileGroup ( callingUserId, targetUserId) ;
} else {
throw new IllegalArgumentException ( "Unknown mode: " + allowMode) ;
}
if ( ! allow) {
if ( userId == UserHandle. USER_CURRENT_OR_SELF) {
targetUserId = callingUserId;
} else {
StringBuilder builder = new StringBuilder ( 128 ) ;
builder. append ( "Permission Denial: " ) ;
throw new SecurityException ( msg) ;
}
}
}
}
5.3 跨用户启动Service
Context中提供了startServiceAsUser() 方法,经过反射测试,也是跨不过AMS的权限检查:
07 - 01 20 : 56 : 33.759 25832 25832 W System. err: Caused by: java. lang. SecurityException: Permission Denial: service asks to run as user 0 but is calling from user 13 ; this requires android. permission. INTERACT_ACROSS_USERS_FULL or android. permission. INTERACT_ACROSS_USERS
07 - 01 20 : 56 : 33.760 25832 25832 W System. err: at android. os. Parcel. createException ( Parcel. java: 1953 )
07 - 01 20 : 56 : 33.760 25832 25832 W System. err: at android. os. Parcel. readException ( Parcel. java: 1921 )
07 - 01 20 : 56 : 33.760 25832 25832 W System. err: at android. os. Parcel. readException ( Parcel. java: 1871 )
07 - 01 20 : 56 : 33.760 25832 25832 W System. err: at android. app. IActivityManager$Stub$Proxy. startService ( IActivityManager. java: 4243 )
07 - 01 20 : 56 : 33.760 25832 25832 W System. err: at android. app. ContextImpl. startServiceCommon ( ContextImpl. java: 1572 )
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: at android. app. ContextImpl. startServiceAsUser ( ContextImpl. java: 1559 )
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: at android. content. ContextWrapper. startServiceAsUser ( ContextWrapper. java: 690 )
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: . . . 16 more
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: Caused by: android. os. RemoteException: Remote stack trace:
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: at com. android. server. am. UserController. handleIncomingUser ( UserController. java: 1581 )
07 - 01 20 : 56 : 33.761 25832 25832 W System. err: at com. android. server. am. ActiveServices. retrieveServiceLocked ( ActiveServices. java: 1914 )
07 - 01 20 : 56 : 33.762 25832 25832 W System. err: at com. android. server. am. ActiveServices. startServiceLocked ( ActiveServices. java: 427 )
07 - 01 20 : 56 : 33.762 25832 25832 W System. err: at com. android. server. am. ActivityManagerService. startService ( ActivityManagerService. java: 21017 )
07 - 01 20 : 56 : 33.762 25832 25832 W System. err: at android. app. IActivityManager$Stub. onTransact$startService$( IActivityManager. java: 10318 )
5.4 跨用户发送广播
Context中提供了sendBroadcastAsUser() 方法,但与Activity 和 Service 相同,反射调用也会抛出异常。
5.5 跨用户Query
系统没有提供类似getContentResolverAsUser的方法,但ContentResolver提供了跨用户query的能力。ContentProvider中提供了hide 的 maybeAddUserId() 方法,被query的Uri中可以携带userId(如: "content://10@com.android.contacts/contacts"
,"//10@" 中的10就是userId),通过Uri中的userId,可以访问到不同用户下相同Uri的ContentProvider。
public static Uri maybeAddUserId ( Uri uri, int userId) {
if ( uri == null) return null;
if ( userId != UserHandle. USER_CURRENT
&& ContentResolver. SCHEME_CONTENT. equals ( uri. getScheme ( ) ) ) {
if ( ! uriHasUserId ( uri) ) {
Uri. Builder builder = uri. buildUpon ( ) ;
builder. encodedAuthority ( "" + userId + "@" + uri. getEncodedAuthority ( ) ) ;
return builder. build ( ) ;
fe }
}
return uri;
}
但是对于三方应用来说,仍然不可以通过这种方式实现跨用户共享数据,AMS还是会检查权限:
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: Caused by: android. os. RemoteException: Remote stack trace:
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: at com. android. server. am. UserController. handleIncomingUser ( UserController. java: 1581 )
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: at com. android. server. am. ActivityManagerService. checkContentProviderPermissionLocked ( ActivityManagerService. java: 12408 )
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: at com. android. server. am. ActivityManagerService. getContentProviderImpl ( ActivityManagerService. java: 12687 )
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: at com. android. server. am. ActivityManagerService. getContentProvider ( ActivityManagerService. java: 13137 )
07 - 01 21 : 24 : 17.555 26991 26991 E AndroidRuntime: at android. app. IActivityManager$Stub. onTransact ( IActivityManager. java: 358 )
5.6 /storage目录的跨用户访问
不可以互相访问。如用户10的app无法访问 /storage/emulated/0 下的文件
5.7 跨用户的数据共享
通过上面的介绍,对于系统和系统应用来说,实现多用户的数据共享很方便,但系统对三方应用屏蔽了这些跨用户的能力。的确,三方应用在绝大数据场景下无需关心自己处于哪个用户下,也不会涉及到跨用户的数据共享,但我们依然可以进行一些尝试,来了解如何在多用户下进行数据共享。
方式一:使用本地回环地址(127.0.0.1)socket进行跨用户通信
创建本地ServerSocket,在另一个用户使用Socket发送消息,是可以收到的,说明多用户间可以通过Socket进行通信。
另:以下方法经过尝试证明了不可行
Settings.Global (不可行,需要 “android.permission.WRITE_SECURE_SETTINGS” 权限)
“/storage/emulated/obb” (不可行,Permission denied)