最近将uiautomator的工作转移出去了,等待下一个任务的到来间隙,学习学习framework的知识,找来《深入理解android II》开始学,都是一些笔记。方便自己理解。
PKMS
System.Server中调用PackageManagerService.main()方法,该方法中创建PackageManagerService对象,调用PackageMangerService的构造函数,而且还将该对象加入到ServiceManager对象中。
PackageManagerService构造方法中会首先判断sdk的版本是否小于等于0,然后判断是否是英文版本,创建DisplayMetrics对象.创建Settings类,在存储运行过程中设置一些参数。然后调用Settings.addSharedUserLPw方法。
addSharedUserLPw方法传过来的参数为(字符串,uid值,flag),uid代表了进程的权限问题。方法中做了哪些处理?首先在系统已有的SharedUserSettings表中检索一下是否已经存在符合传入参数的SharedUserSettings对象。如果不存在,则以name,flag创建一个对象,设置该对象userId值为uid,然后将该SharedUserSettings对象保存到对应的数组中。
然后分析SharedUserSettings类。保存具有相同sharedUserId的包名。继承自GrantedPermissions权限
做完以后创建SharedUserSettings对象后,回到PackageManagerService构造函数中。后续的工作就是创建Installer,WM,Display,然后创建用户程序的安装和卸载的线程。然后创建各种目录。data/data,data/user,data/app-private.然后到readPermissions()和Settings.readLPw();
readPermissions()方法为解析etc/permissions目录下的所有xml文件,里面包括各种各样的权限信息。实际调用readPermissionsFromXml()
platform.xml文件中有4种标签:
<一>linux层gid和android层permission映射关系
<permission>
<group/>
</permission>
<二>用于向指定uid赋予相应的权限
<assgin-permission/>
<三>指定系统库
<library/>
readPermissionsFromXml()方法
int[] mGlobalGids保存<group>标签中gid的值
HashMap<String,String> mshareLibrarie保存library标签中库名,库文件的位置
SparseArray<HashSet<String>> mSystemPermissions<assgin-permission>标签里值,uid,权限字符串为里面的HashSet值。以uid指向很多权限。
HashMap<String,FeatureInfo> mAvailableFeatures解析<feature>标签
HashMap mSettings.mPermissions解析<permission>标签
Settings构造方法中会在data/system文件夹下生成5个文件
package.xml:package相关的信息,当系统进行程序安装,卸载和更新时都会更新该文件
package-backup.xml:
package-stop.xml:从系统自带的设置程序中进入到应用程序页面,然后强制关闭某个应用,这些应用的信息保存在该文件中
package-stop-backup.xml:先在该文件中写,然后完成后复制改名为package-stop.xml
package.list:系统中所有非系统自带的apk信息。
Settings.readLPw就是解析上面的xml文件,然后保存到对应的数据结构中。
扫描package开始:
/system/frameworks
/data/dalvik-cache
获得java启动类库,判断启动库里的jar文件是否需要做优化和mshareLibrarie是否需要,如果需要做优化,则删除dalivk-cache到的缓存信息,优化后重新得到。
进入第二阶段:
linux中inotify机制:文件系统变化通知机制,对每个目录都是先创建文件夹监控对象,然后启动监控,然后scanDirLI扫描开始。
/system/framworks/:扫描里面的framework-res.apk
/system/app :browser.apk、settingsProvider.apk等系统应用
/vendor/app:第三方应用
以上三个目录为系统package。
scanDirLI()方法分析:PKMS中的方法,只扫描apk文件,扫描的时候调用scanPackageLI返回PackageParser.Package对象,代表APK的数据结构。扫描过程中遇到扫描失败的情况还得判断它是否属于系统的APK,如果是非系统的apk就地删除。
scanPackageLI()方法分析:PKMS中的方法。调用PackageParser.parsePackage()真正解析apk文件,返回的是PackageParser.Package对象,将文件里的内容映射到数据结构中,还有package升级的工作。然后再调第二个scanPackageLI,传入的参数为Package,第一个scanPackageLI传入的是File。
PackageParser类分析:解析AndroidManifest.xml文件。首先调用parsePackage()获得该xml配置文件,然后再调用同名函数解析xml里的各个节点,这就设计到xml解析方法的知识。PP子类中Package保存整个AndroidManifest.xml里所有信息,以内部变量的信息保存其他子类对象,以数组信息保存在该类中。PP中的子类继承自Component<ActivityIntentInfo>的原因是我们需要通过intent查询到相应的activity这个需求。通过这样的继承关系就能做到这点,如果单纯的就建一个Activity裸类达不到这样的效果。PP中的PackageLite让我想到了SQLite,它也是一个轻量级的数据结构,保存package一些简单的信息用的。理解为SQLite也不过分。
第二个PKMS.scanPackageLI()分析:完成的工作是将package加入到系统中。首先单独处理packageName为“android”的framework-res.apk。framework-res.apk中配置档文件包含的信息如下:(都是一些与系统相关的信息)
ChooserActivity:当多个activity符合同一个intent会有一个选择框弹出
RingtonePickerActivity:铃声选择activity
ShutdownActivity:关机前弹出的选择对话框
PKMS专门设置成员变量来保存该package中的信息:
mPlatformPackage:保存该package信息
mAndroidAppliaction:保存ApplicationInfo信息
mResolveActivity:保存ChooseActivity信息的ActivityInfo信息
mResolveInfo:ResolveInfo类型。保存由系统解析intent后剩下的信息,例如解析完intent后的activity信息。该对象的变量activityInfo指向mResolveActivity。之前所说的PP。activity就是通过过滤完intent得到ResolveInfo对象后得到生下来的activity对象。
接着scanPackageLI所要做的操作是对androidManifest.xml里的信息做一些验证,看是否符合系统的要求。然后将该package里的四大组件公有化。
扫描完系统的apk后,要对非系统的apk进行扫描,也就是/data/app、/data/app-private非系统package目录里扫描apk信息然后加入到系统中。
进行完扫描工作完成后,第二阶段的工作完成,然后将扫描的到的信息集中整理,将某些信息保存到相应的文件中。这样pkms的构造函数里的工作就全部完成啦。
构造函数的工作分3个阶段
1.扫描system/etc/permissions包下的xml信息
2.扫描包信息,最耗时,所以才会造成系统启动速度慢。
3.将扫描的到的信息整理,写到相应的文件中。
APK Installation分析
文件:commandline.c
首先在int adb_commandline()中调用install_app函数。该函数做了3件事情:
1.对apk安装进行验证
2.调用pm_command()安装
3.安装完毕后,删除/data/local/tmp里的文件,因为安装完成以后apk位于/data/app目录下。
pm_command():将要安装的参数信息发送给手机端的adbd.会用一个叫pm脚本,该脚本通过app_process进程去执行pm.jar包中的main函数。然后来看看pm.java的main函数做了哪些工作。
通过aidl接口获得PKMS的binder客户端,IPackageManager.Stub.asInterface(ServiceManager.getService("package"));然后调用runInstall()方法,该方法首先做一些参数分析,然后将Verification程序加入进来,最后调用PKMS客户端来执行installPackageWithVerification函数。而该函数只是简单的创建
InstallParams
几个对象后,发送一个message,信息内容是INIT_COPY。就交给了之前创建mHandler对象
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
也就是该消息的处理会方法mHandlerThread线程中处理。Looper信息队列中接受到信息后,会调用Handler的handlerMessage处理,也就是PKMS的方法。在该方法中调用PKMS里的doHandlerMessage()来处理。
doHandlerMessage()先从mes中获得信息的实体,也就是installParams对象。然后获得要安装的个数。最后连接DefaultContainerService.apk。该apk里DefalutContainerService提供安装的服务。所以先查看该服务是否已经启动,如果已经启动将所有要安装的apk索引保存在一个变量mPendingInstalls中。然后mHandler发送安装请求。INIT_COPY变成了MCS_BOUND,然后开始安装。
HandlerParams抽象类,含3个子类:
InstallParams:处理apk安装
moveParams处理apk移动
measureParams查询apk所占控件大小
InstallArgs抽象类,含2个子类:
FileInstallArgs:针对内部存储/data/local/tmp中apk.
SDInstallArgs:sd 卡的/sdcard/tmp中的apk
在上面的处理MCS_BOUND信息中,调用了InstallParams的startCopy()方法,该方法是其父类HandlerParams里的方法,该方法就是安装apk的方法,会尝试4次安装,如果提示不成功,则提示安装失败。在该方法中使用2个handlerParams的子类的实现方法详细解析这2个方法。
了解startCopy()中handleStartCopy()和handleReturnCode()
handleStartCopy()方法相当复杂,总结如下:
1.调用DefaultContainerService.getMinimalPackageInfo(),得到PackageInfoLite,描述apk结构,里面有重要信息推荐安装路径recommendedInstallLocation,该对象继承Parcelable,所以可通过Binder在进程间传递.
DefaultContainerService.getMinimalPackageInfo()方法里也会创建PackageLite对象和PackageInfoLite对象,将PackageLite里的一些信息赋值给PackageInfoLite。所以感觉上来说PackageInfoLite是PackageLite的简洁版,可能还含有其他信息。然后调用recommendAppInstallLocation函数来获得一个合理的安装位置赋值给PackageInfoLiete里的recommendedInstallLocation变量。该方法做了大量工作,就是各种验证路径,检查推荐的路径是否合法,内部控件和sd卡是否有足够控件,最后做一个合理的判断,返回。
2.调用installLocationPolicy检查推荐的路径是否合法。系统的package是不允许安装在sd卡上。
3.createInstallArgs会根据不同的安装位置创建不同的InstallArgs,如果是内部存储返回FileInstallArgs,否则是SDInstallArgs。
4.在正式安装前,对apk进行检查
5.调用InstallArgs的copyApk来安装应用。
copyApk中有createCopyFile()该方法为在/data/app目录下创建一个临时文件,临时文件名为tmp结尾的随机文件,因为inotify的机制会扫描apk结尾的文件,触发。所以为了不让它触发,才会生成这样一个文件。然后调用IMediaContainerService中的copyResource方法。将/data/local/tmp里的apk复制到/data/app下的之前生成的tmp文件中。
handleReturnCode()方法:
1.调用InstallArgs的doPreInstall函数。内部存储的就调用FileInstallArgs,否则就是SDInstallArgs,调用其doPreInstall函数
2.调用pkms的installPackageLI()进行安装,在该函数内部调用InstallArgs的doRename对文件进行改名。然后扫描该apk文件,至此就把apk的所有内容都登记到PKMS的内部中啦。
----doRename:vmdl-随机数字.tmp变为包名-索引.apk。
3.调用InstallArgs的doPostInstall()
4.然后向mHandler中发送消息,提示安装完成,信息内容为POST_INSTALL。携带token,通过它可从mRunningInstalls数组中找到PostInstallData对象。
然后mHandler又要开始处理POST_INSTALL消息。向pm发送一个安装的结果信息。
APK安装流程总结。