添加用户的流程分析
添加用户的流程分析
Settings的上层接口
settings中的文件:
/android/packages/apps/Settings/src/com/android/settings/users/UserSettings.java
/android/vendor/samsung/packages/apps/Settings/res/xml/user_settings.xml
添加用户item的key值为Preference key="user_add"
点击item之后,根据型号的不同分为两个不同的选择:
mCanAddRestrictedProfile == true 可以对不同的权限进行添加
mCanAddRestrictedProfile != true 一般型号是不能进行更深层次的管理的
->
弹出DIALOG_ADD_USER的对话框之后,对于有通话功能的平板,会有一个选择是否同意数据的过程
选择完就是dialog的本体了,layout在前面会有个简单的设置
其中的userType的值也是根据之前mCanAddRestrictedProfile来的,两者相互交叉,统一在这个地方就行用户的添加,一般型号的是走USER_TYPE_USER的流程。addUserNow(final int userType)方法是一个较长的方法,其中主要是包含一个新建用户的线程,在线程中会针对userType的不同新建不同级别的用户。
咱走的是USER_TYPE_USER, 在createTrustedUser方法中完成用户的新建,这里我们能看到新用户命名的过程以及新建用户的真正接口
新建完用户之后立即会起一个dialog,这个dialog是询问用户是否要立即切换至新用户(这个动作就会起用户的Setup),我们得搞明白这个流程,到底switchUserNow的过程中做了些什么
可以看到切换用户的接口是ActivityManagerNative的方法
切换用户流程
切换用户接口文件:
/android/frameworks/base/core/java/android/app/ActivityManagerNative.java
/android/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
/android/frameworks/base/services/core/java/com/android/server/am/UserSwitchingDialog.java
/android/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
/android/vendor/samsung/packages/apps/Settings/src/com/android/settings/ManagedProfileSetup.java
/android/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityManagerNative类的getDefault 方法返回的是ActivityManagerService这个类的一个对象,ActivityManagerService的switchUser方法较为复杂,但也分为了很多小模块。
1、拿到切换到的userId对应的UserInfo,然后启动一个切换用户的dialog
2、在这个dialog里面对切换用户的信息有一些简单的设置用来作为显示,最主要的是回调了ActivityManagerService中的startUserInForeground方法,通过这个来进行用户的切换。
3、startUserInForeground直接转到startUser(final int userId, final boolean foreground)方法,在startUser中管理用户的启动,过程比较复杂,涉及到各个状态的处理。我们把这些过程也分为几个流程块:
A、启动新用户的准备工作,主要包括windowManagerService的窗口处理,userid的添加,启动过程对于window的冻结三部分,由于foreground值为true,我们只需要看前台启动的部分就行了。
B、获取user的状态,对于不同状态的处理方式,主要包括四种状态:STATE_STOPPING、STATE_SHUTDOWN、STATE_BOOTING和STATE_RUNNING,若是前两种状态则要让状态回到STATE_BOOTING,以启动user,启动完之后发一条消息:SYSTEM_USER_START_MSG,这条消息由SystemServiceManager来处理,启动新用户
C、第三部分在systemService结束启动用户之后,会连续地发几个广播:SYSTEM_USER_CURRENT_MSG、REPORT_USER_SWITCH_MSG、USER_SWITCH_TIMEOUT_MSG,这几个广播处理较复杂,稍等
D、接下来是检测User是否被初始化,若未初始化则发广播给接收处理用户初始化的文件就行初始化,intent为Intent.ACTION_USER_INITIALIZE= android.intent.action.USER_INITIALIZE,这一初始化是在settings中完成的: ManagedProfileSetup.java
这里面会对新用户的某些数据库进行设置
重要的方法是onUserInitialized(uss, foreground, oldUserId, userId)方法,这个方法就能能起Setupwizard的方法
onUserInitialized(uss, foreground, oldUserId, userId)方法调用moveUserToForeground方法引出startHomeActivityLocked(newUserId)方法进行Home级别的Activity启动
E、startHomeActivityLocked方法主要是根据userId判断启动Home级以及优先级最高的app(SetupWizard优先级比Launcher2更高),由getHomeIntent方法进行遍历。遍历完之后取到home级别的app,由startHomeActivity方法来启动。startHomeActivity方法是在下面这个文件中写的:
/android/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityManagerNative中也有设置switchUser的接口,其对switchUser方法的写法如下,把需要切换到的user的id传到mRemote中用transact方法进行进一步的解析工作。那么mRemote是个shen me gui 呢?
PS:关于Parcel,Parcel其实是一个存放读取数据的容器, Android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel来交互的。在Java空间和C++都实现了Parcel,由于它在C/C++中,直接使用了内存来读取数据,因此,它更有效率。
Parcel的方法:
l obtain() 获得一个新的parcel ,相当于new一个Parcel对象
l writeInterfaceToken(String interfaceName) 写入接口句柄,相当于把接口的文件路径给此Parcel
l writeInt(int) 写入一个整数
l readException() 在Parcel队头读取,若读取值为异常,则抛出该异常;否则,程序正常运行
l recyle() 清空、回收parcel对象的内存
其中String descriptor = "android.app.IActivityManager";
由此我们可以看到switchUser方法new了两个内存区data、reply,data中存放了Binder机制的客户请求的服务端路径、需要切换到的用户id。mRemote其实是Ibinder 的一个对象,它把1、客户端请求的动作类型SWITCH_USER_TRANSACTION(int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129);2、服务端源、用户id;3、一段返回内存区reply传到Binder里进行处理。
新建用户流程
新建用户接口文件:
/android/frameworks/base/core/java/android/os/UserManager.java
/android/frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
其中mService是(UserManager) context.getSystemService(Context.USER_SERVICE);用户的新建由这个服务来完成
我们拿到UserManagerService文件中关于createUser(String name, int flags)的写法:
可以看到新建用户的主要工作一、是在存储区里面开辟一块用户区域二、是发一个已经创建了新用户的广播。