Android 4.4中apk包的安装过程

时间:2025-01-27 11:12:59
1231                for ( int i= 0 ; i<; ...= "" 1243 = "" 1244 = "" if = "" pre= "" >接下来,监控/system/framework目录并扫描该目录。<PRE class =brush:java;> 1276            // Find base frameworks (resource packages without code).
1277            mFrameworkInstallObserver = new AppDirObserver(
1278                (), OBSERVER_EVENTS, true , false );</PRE>
这里用Observer模式来监视目录变动。它依赖于Linux kernel提供的Inotify机制。实现主要位于/frameworks/base/core/java/android/os/和/frameworks/base/core/jni/android_util_FileObserver.cpp。对于它的继承类(如AppDirObserver),只要实现onEvent()方法来处理文件或目录变动即可。onEvent()的实现比如/frameworks/base/services/java/com/android/server/pm/中:<BR>
<PRE class =brush:java;> 6384        public void onEvent( int event, String path) {
...
6450                        p = scanPackageLI(fullPath, flags,
6451                                SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
6452                                (), );
...
6460                            synchronized (mPackages) {
6461                                updatePermissionsLPw(, p,
6462                                        () > 0 ? UPDATE_PERMISSIONS_ALL : 0 );
6463                            }
...
6471                synchronized (mPackages) {
6472                    ();
6473                }</PRE>
扫描该目录的目的是要安装里边的apk包。主要实现函数是scanDirLI():<BR>
<PRE class =brush:java;> 1280            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
1281                    | PackageParser.PARSE_IS_SYSTEM_DIR
1282                    | PackageParser.PARSE_IS_PRIVILEGED,
1283                    scanMode | SCAN_NO_DEX, 0 );</PRE>
对于其它几个目录(/system/priv-app,/system/app,/vendor/app, /data/app, /data/app- private ),也是一样的:<BR>
<PRE class =brush:java;> 1380                mAppInstallObserver = new AppDirObserver(
1381                    (), OBSERVER_EVENTS, false , false );
1382                ();
1383                scanDirLI(mAppInstallDir, 0 , scanMode, 0 );
…</PRE>
全安装好了就可以更新权限信息并且写回安装信息了。<BR>
<PRE class =brush:java;> 1446            updatePermissionsLPw( null , null , UPDATE_PERMISSIONS_ALL
1447                    | (regrantPermissions
1448                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
1449                            : 0 ));
...
1457            // can downgrade to reader
1458            ();</PRE>
这样启动时安装主要工作就差不多完成了。下面回头看一下重头戏 - 目录的扫描和安装,也就是scanDirLI()函数:<BR>
<PRE class =brush:java;>scanDirLI()
     scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null );
         PackageParser pp = new PackageParser(scanPath);
         final pkg = (scanFile, scanPath, mMetrics, parseFlags);
             assmgr = new AssetManager();
             parser = (cookie, ANDROID_MANIFEST_FILENAME);
             pkg = parsePackage(res, parser, flags, errorText); // parse
         ...
         scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);</PRE>
可以看到scanPackageLI()和parsePackage()皆有重载版本。基本上内层的版本才是做事的。内层的parsePackage(res, ...)函数用于解析文件。实现在/frameworks/base/core/java/android/content/pm/。至于是apk中必不可少的配置文件。详见http: ///guide/topics/manifest/,没有的话Android压根不让你装。<BR>
<PRE class =brush:java;> 1034            String tagName = ();
1035            if ((application)) {
1036                if (foundApp) {
1037                    if (RIGID_PARSER) {
1038                        outError[ 0 ] = <MANIFEST> has more than one ;
1039                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1040                        return null ;
1041                    } else {
1042                        (TAG, <MANIFEST> has more than one );
1043                        (parser);
1044                        continue ;
1045                    }
1046                }
1047
1048                foundApp = true ;
1049                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
1050                    return null ;
1051                }
1052            } else if ((keys)) {
1053                if (!parseKeys(pkg, res, parser, attrs, outError)) {
1054                    return null ;
1055                }
1056            } else if ((permission-group)) {
1057                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null ) {
1058                    return null ;
1059                }
1060            } else if ((permission)) {
1061                if (parsePermission(pkg, res, parser, attrs, outError) == null ) {
1062                    return null ;
1063                }
1064            } else if ((permission-tree)) {
1065                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null ) {
1066                    return null ;
1067                }
1068            } else if ((uses-permission)) {
1069                if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
1070                    return null ;
1071                }
</APPLICATION></MANIFEST></APPLICATION></MANIFEST></PRE>
比较重要的如parseApplication()是解析application标签里的东西。application标签里包含Android四大组件(Activity, Receiver, Service, Content Provider)信息和库等信息。一顿解析后,返回结果对象pkg,这个类基本上就包含了里的信息。接下来scanPackageLI(pkg, ...)被调用,前面返回的解析结果pkg被当作参数传入。scanPackageLI(pkg, ...)干的事还挺多的。如处理共享用户,注册包信息,调用NativeLibraryHelper和Installer的相关函数进行安装等等。<BR>
<BR>
如果该app使用了共享用户,则调用getSharedUserLPw()函数获取该共享uid的SharedUserSetting,没有的话就新建,然后分配uid:<BR>
<P> </P>
<PRE class =brush:java;> 245    SharedUserSetting getSharedUserLPw(String name,
246            int pkgFlags, boolean create) {
247        SharedUserSetting s = (name);
248        if (s == null ) {
249            if (!create) {
250                return null ;
251            }
252            s = new SharedUserSetting(name, pkgFlags);
253            = newUserIdLPw(s);
254            (, New shared user  + name + : id= + );
255            // < 0 means we couldn't assign a userid; fall out and return
256            // s, which is currently null
257            if ( >= 0 ) {
258                (name, s);
259            }
260        }
261
262        return s;
263    }</PRE>
在scanPackageLI(pkg, ...)函数中,下面调用getPackageLPw()得到该apk包的PackageSetting对象,有些在前面readPackageLPw()时就已经恢复好了,还没有的那些这儿就会新建:<BR>
<P> </P>
<PRE class =brush:java;> 4302            // Just create the setting, don't add it yet. For already existing packages
4303            // the PkgSetting exists already and doesn't have to be created.
4304            pkgSetting = (pkg, origPackage, realName, suid, destCodeFile,
4305                    destResourceFile, ,
4306                    , user, false );</PRE>
<P> </P>
<P>getPackageLPw()实现在/frameworks/base/services/java/com/android/server/pm/中:</P>
<P> </P>
<PRE class =brush:java;> 392    private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
393            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
394            String nativeLibraryPathString, int vc, int pkgFlags,
395            UserHandle installUser, boolean add, boolean allowInstall) {
457                p = new PackageSetting(name, realName, codePath, resourcePath,
458                        nativeLibraryPathString, vc, pkgFlags);
...
520                        // Assign new user id
521                        = newUserIdLPw(p);</PRE>
<P> </P>
newUserIdLPw()函数为app分配uid。这样,该应用对应的uid就设置好了。<BR>
<P> </P>
<P>回到scanPackageLI()中,下面设置进程名,进程名默认就是包名。所以我们在ps里看到的都是包名。</P>
<P> </P>
<PRE class =brush:java;> 4422        = fixProcessName(
4423                ,
4424                ,
4425                );</PRE>
对于大部分全新安装的一般应用而言,接下来为应用创建数据目录:
<P> </P>
<P> </P>
<PRE class =brush:java;> 3987    private int createDataDirsLI(String packageName, int uid, String seinfo) {
3988        int [] users = ();
3989        int res = (packageName, uid, uid, seinfo);
...
3993        for ( int user : users) {
3994            if (user != 0 ) {
3995                res = (packageName,
3996                        (user, uid), user);</PRE>
Installer是一个代理类,它会和后台的installd通信并让installd完成具体工作。installd的实现位于/frameworks/ native /cmds/installd/,install()会创建app目录(/data/data/<PACKAGE-NAME>),并作lib目录的软链接,如/data/data/xxx/lib -> /data/app-lib/xxx。
<P> </P>
<P> </P>
<P>下面设置原生库目录为/data/data/<PACKAGE- name= "" >/lib,它指向/data/app-lib。再把原生库文件(如有)解压到该目录下。</PACKAGE-></P>
<P> </P>
<PRE class =brush:java;> 4556                if ( == null ) {
4557                    setInternalAppNativeLibraryPath(pkg, pkgSetting);
4558                } else {
4559                    = ;
4560                }
...
4577        if ( != null ) {
4578            try {
4579                File nativeLibraryDir = new File();
...
4605                        try {
4606                            if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) { 
4607                                (TAG, Unable to copy native libraries);
4608                                mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
4609                                return null ;
</PRE>
这里调用copyNativeLibrariesForInternalApp(),它会调用()把apk里的原生库解压出来放到/data/app-lib的对应目录下。<BR>
<BR>
接下来PMS调用performDexOptLI()优化Java的bytecode,即dex文件。<BR>
<PRE class =brush:java;> 4638        if ((scanMode&SCAN_NO_DEX) == 0 ) {
4639            if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0 , false )
4640                    == DEX_OPT_FAILED) {
4641                mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
4642                return null ;
4643            }
4644        }</PRE>
真正的工作还是在后台installd进程中完成, installd中的dexopt()函数会根据当前是运行dalvik还是art虚拟机来选择调用run_dexopt()或run_dex2oat()。<BR>
<P> </P>
<P> </P>
<PRE class =brush:java;> 741        if (strncmp(persist_sys_dalvik_vm_lib, libdvm, 6 ) == 0 ) {
742            run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
743        } else if (strncmp(persist_sys_dalvik_vm_lib, libart, 6 ) == 0 ) {
744            run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
745        } else {
746            exit( 69 );   /* Unexpected value */
747        }</PRE>
前者适用于dalvik,将dex优化成odex文件。后者适用于art,将dex直接一步到位编译成oat文件(也就是可执行代码)了。由于下层是执行了/system/bin/dexopt或/system/bin/dex2oat文件,dexopt()将之放到子进程去做,自己作为父进程等待它结束。以art为例,/system/bin/dex2oat被调用(实现位于/art/dex2oat/)。输出的elf文件放在/data/dalvik-cache/data @app @<PACKAGE-NAME> @classes .dex。注意对于dalvik和art,这个文件名称相同但性质截然不同。dalvik下,该文件为优化后的bytecode。而art下,这个就是可执行文件了。<BR>
<BR>
<P> </P>
<P>如果是更新已有的app,还要让ActivityManager调用killApplication()把进程杀掉。app都更新了,老的还留内存里跑,这不合适。</P>
<P> </P>
<PRE class =brush:java;> 4743            killApplication(,
4744                        , update pkg);</PRE>
之后将安装的app注册到PMS的mPackages中,mPackges是个Hash表,保存了从包名到的映射。注意Settings里也有个mPackages,那里保存的是包名到PackageSetting的映射。前者主要是app配置文件中的信息,而后者是安装过程中的信息。可以粗略理解为一个是静态信息,一个是动态信息。<BR>
<PRE class =brush:java;> 4763            (pkgSetting, pkg);
     // PackageSetting <=
     addPackageSettingLPw(p, , )
         (name, p);
4764            // Add the new setting to mPackages
4765            (, pkg);</PRE>
下面把app中的组件信息(content provider, service, receiver, activity)记录到系统中。另外根据前面app配置文件中的权限信息进行初始化。
<P> </P>
<P> </P>
<P> </P>
<PRE class =brush:java;> 4811            int N = ();
4812            StringBuilder r = null ;
4813            int i;
4814            for (i= 0 ; i<N; = "" = "fixProcessName(," p= "(i);" n= "();" 4965 = "" 4931 = "" 4911 = "" 4891 = "" 4871 = "" 4818 = "" 4817 = "" 4816 = "" 4815 = "" ...= "" ><P> </P>
回到PMS构造函数中。下面就是收尾工作了。主要包括更新共享库信息,更新权限信息,以及写回安装信息。所有包都解析完了,意味着所有共享库信息都已解析,这儿就可以调用updateAllSharedLibrariesLPw()为那些使用动态库的app绑定动态库信息了。下面updatePermissionsLPw()函数用于赋予app相应权限:<P> </P><PRE class =brush:java;> 5365    private void updatePermissionsLPw(String changingPkg,
5366            pkgInfo, int flags) {
...
5430        // Now update the permissions for all packages, in particular
5431        // replace the granted permissions of the system packages.
5432        if ((flags&UPDATE_PERMISSIONS_ALL) != 0 ) {
5433            for ( pkg : ()) {
5434                if (pkg != pkgInfo) {
5435                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0 );
5436                }
5437            }
5438        }</PRE>
在中app会申请一些权限,比如读取位置信息,读取联系人,操作摄像头等等。中的格式如:<BR>
<USES-PERMISSION android:name= "permission_name" ><BR>
这里的permission_name被放到requestedPermissions,代表该app申请该权限。经过grantPermissionsLPw()里判断能否给予相应权限,如果允许则授予权限(即把权限对应的gid加到app的gid列表中,因为权限在Linux中对应物就是group,由gid表示),并把该权限加到grantedPermissions中,代表已授予该权限。<BR>
<PRE class =brush:java;> 5445    private void grantPermissionsLPw( pkg, boolean replace) {
...
5467        final int N = ();
5468        for ( int i= 0 ; i<N; ...= "" pre= "" if = "" = "appendInts(," else = "" changedpermission= "true;" 5534 = "" 5533 = "" 5532 = "" 5531 = "" 5530 = "" 5529 = "" 5528 = "" >最后,writeLPr()将安装信息写回文件,这也是一开始readLPw()读的那个文件。这样下次启动时就可以按照这里边的信息重新安装了。<PRE class =brush:java;> 1261    void writeLPr() {
...
1315            ( null , permission-trees);
1316            for (BasePermission bp : ()) {
1317                writePermissionLPr(serializer, bp);
1318            }
1319            ( null , permission-trees);
1320
1321            ( null , permissions);
1322            for (BasePermission bp : ()) {
1323                writePermissionLPr(serializer, bp);
1324            }
1325            ( null , permissions);
1326
1327            for ( final PackageSetting pkg : ()) {
1328                writePackageLPr(serializer, pkg);
1329            }</PRE>
总结下几个主要类的用途:<BR>
PackageManagerService: apk包安装服务<BR>
Settings: 管理app的安装信息<BR>
PackageSetting: app的动态安装信息<BR>
SharedUserSetting: 共享Linux用户<BR>
: app的静态配置信息。<BR>
Pm: pm命令实现类<BR>
Installer: installd daemon代理类<BR>
HandlerThread: PMS工作线程<BR>
它们之间的大致关系:<BR>
<IMG style= "WIDTH: 630px; HEIGHT: 418px" alt=\ src= "http:///uploadfile/Collfiles/20140512/" ><BR>
<BR>
<STRONG>二、adb install安装</STRONG>
<P> </P>
<P> </P>
<P>通过adb install命令安装时流程略有不同,主要是scanPackageLI()之前的流程不同。host机上的adb从/system/core/adb/中的main()开始:main()->adb_commandline()->install_app()->pm_command()->send_shellcommand(),其中会把安装包从host传到guest上的临时目录。<BR>
接下来guest里的pm命令(/frameworks/base/cmds/pm/src/com/android/commands/pm/)接手,大体流程如下:</P>
<P> </P>
<PRE class =brush:java;>runInstall()
     installPackageWithVerificationAndEncryption()
     doHandleMessage() // INIT_COPY
     doHandleMessage() // MCS_BOUND
         startCopy()
             ()
                 InstallArgs args = createInstallArgs( this )
                 () // FileInstallArgs或AsecInstallArgs,取决于是否是forward-lock或装在sd card上。
                     createCopyFile() // 拷贝生成类似于/data/app/这样的临时文件,因为这时候包都没解析,不知道包名。
             handleReturnCode()
                 processPendingInstall() //异步方式安装,因为安装过程可能较长。
                     installPackageLI()</PRE>
这里用到了一开始提到的PMS工作线程,doHandleMessage()就是工作线程用于处理丢给它的消息的。<BR>
<P> </P>
<P><IMG style= "WIDTH: 584px; HEIGHT: 354px" alt=\ src= "http:///uploadfile/Collfiles/20140512/" ></P>
<P>这里有几个设计模式值得学习的。首先,Pm中得到PMS的代理类,然后调用installPackageWithVerificationAndEncryption()进行安装。</P>
<P> </P>
<PRE class =brush:java;> 90        mPm = (( package ));
...
957            (apkURI, obs, installFlags,
958                    installerPackageName, verificationParams, encryptionParams);</PRE>
由于安装时间一般较长,这里的obs用了Observer模式来监视安装完成事件。
<P> </P>
<P> </P>
<P>其次,InstallArgs用了Strategy模式,而createInstallArgs()使用了简单工厂模式。在handleStartCopy()中,只要根据安装类型生成相应的InstallArgs对象,然后调用统一接口copyApk()等就行了。</P>
<P><IMG style= "WIDTH: 349px; HEIGHT: 240px" alt=\ src= "http:///uploadfile/Collfiles/20140512/" ><BR>
另外,HandlerParams和其继承类采用了Template method模式。其中基类中的startCopy()是模板函数,继承类实现handleStartCopy(),handleServiceError()和handleReturnCode()来完成不同工作。HandlerParams包含了要工作线程做的工作内容,工作线程只要取出HandlerParams对象,调用其startCopy()接口,因此这里也用了Command模式的思想。</P>
<P><IMG style= "WIDTH: 508px; HEIGHT: 273px" alt=\ src= "http:///uploadfile/Collfiles/20140512/" ></P>
<P>这里的installPackageLI()做了很多前一种安装方式中scanPackageLI(file, ...)的工作,接着它会根据该app是否是全新安装调用replacePackageLI()或installNewPackageLI()。</P>
<P> </P>
<PRE class =brush:java;> 9061        if (!(, pkgName, oldCodePath)) { // 前面拷贝apk时是随机取了临时名字的,这里用doRename()函数为其“正名”。
...
9068        if (replace) {
9069            replacePackageLI(pkg, parseFlags, scanMode, ,
9070                    installerPackageName, res);
9071        } else {
9072            installNewPackageLI(pkg, parseFlags, scanMode, ,
9073                    installerPackageName, res);
9074        }</PRE>
如果是全新的apk,调用installNewPackageLI()进行安装。它调用scanPackageLI()完成主要安装工作。<BR>
<PRE class =brush:java;> 8601    private void installNewPackageLI( pkg,
8602            int parseFlags, int scanMode, UserHandle user,
8603            String installerPackageName, PackageInstalledInfo res) {
...
8630        newPackage = scanPackageLI(pkg, parseFlags, scanMode,
8631                (), user);</PRE>
从这开始就熟悉了吧,和启动时安装的流程差不多了,最后调用updateSettingsLI()来更新安装信息。
<P> </P>
<P> </P>
<P><STRONG>三、Google Play网络下载安装 </STRONG></P>
<P><BR>
Google Play的包名为。由于是闭源的,看不了源码。不过从反汇编粗略地看,应该是先把apk包下载到:<BR>
/data/data//cache/<BR>
然后调用installPackage()安装,类似于:</P>
<PRE class =brush:java;>PackageManager pm = ();
(packageURI, observer, flags, null );</PRE>
接着就和上面一样了:installPackage()->installPackageWithVerification()->installPackageWithVerificationAndEncryption()。<BR>
<P> </P>
<P><BR>
<STRONG>四、点选apk文件安装</STRONG><BR>
<BR>
这种情况下,会通过PackageInstaller安装app。在/packages/apps/PackageInstaller/src/com/android/packageinstaller/中:</P>
<P> </P>
<PRE class =brush:java;> 284            (mPackageURI, observer, installFlags,
285                    installerPackageName, verificationParams, null );</PRE>
后面的故事又都熟悉了吧。<BR>
<BR>
<P> </P>
</N;></PRE>
</USES-PERMISSION></N;></PRE>
</PACKAGE-NAME></PACKAGE-NAME></;>