DBMNG数据库管理与应用

书籍是全世界的营养品。生活里没有书籍,就好像没有阳光;智慧里没有书籍,就好像鸟儿没有翅膀。
当前位置:首页 > 移动应用 > Android

Android的打包文件apk的安装和优化原理

0x00

    apk安装的方式有:

    1、开机启动时安装

    2、通过adb install 或者在手机中点击apk,进行界面安装。


    0x01

    开机启动后在system_server中调用PackageManagerService.main,随着调用的深入,循环对每个apk都调用scanPackageLI方法,这个函数提取apk的AndroidManifest.xml里面的内容放在PackagemanagerService中,并且安装了apk,还有优化了dex。

    安装apk的代码:

  1. int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,  
  2.                            pkg.applicationInfo.uid);  


    优化dex的代码:

  1. if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) {  
  2.                     mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;  
  3.                     return null;  
  4.                 }  
  1. private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) {  
  2.     boolean performed = false;  
  3.     if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) {  
  4.         String path = pkg.mScanPath;  
  5.         int ret = 0;  
  6.         try {  
  7.             if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {  
  8.                 ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,  
  9.                         !isForwardLocked(pkg));  
  10.                 pkg.mDidDexOpt = true;  
  11.                 performed = true;  
  12.             }  
  13.         } catch (FileNotFoundException e) {  
  14.             Slog.w(TAG, "Apk not found for dexopt: " + path);  
  15.             ret = -1;  
  16.         } catch (IOException e) {  
  17.             Slog.w(TAG, "IOException reading apk: " + path, e);  
  18.             ret = -1;  
  19.         } catch (dalvik.system.StaleDexCacheError e) {  
  20.             Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);  
  21.             ret = -1;  
  22.         } catch (Exception e) {  
  23.             Slog.w(TAG, "Exception when doing dexopt : ", e);  
  24.             ret = -1;  
  25.         }  
  26.         if (ret < 0) {  
  27.             //error from installer  
  28.             return DEX_OPT_FAILED;  
  29.         }  
  30.     }  
  31.   
  32.     return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;  
  33. }  
    mInstaller.dexopt 通过socket通信 让installd 进程(由init进程起来了)执行do_dexopt-->dexopt-->fork出子进程去执行run_dexopt,安装和优化的调用流程请参考Android安装服务installd源码分析


    run_dexopt代码如下:

  1. static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,  
  2.     const char* dexopt_flags)  
  3. {  
  4.     //input_file_name为apk的路径  
  5.     static const char* DEX_OPT_BIN = "/system/bin/dexopt";  
  6.     static const int MAX_INT_LEN = 12;        
  7.     char zip_num[MAX_INT_LEN];  
  8.     char odex_num[MAX_INT_LEN];  
  9.     sprintf(zip_num, "%d", zip_fd);//apk文件句柄  
  10.     sprintf(odex_num, "%d", odex_fd);//dex文件句柄  
  11.     //调用/system/bin/dexopt工具来优化apk文件  
  12.     execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,  
  13.         dexopt_flags, (char*) NULL);  
  14.     ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));  
  15. }  


    fork出的子线程执行的是/system/bin/dexopt,代码位于dalvik\dexopt\OptMain.c


    0x02

    执行的是/system/bin/dexopt,实际上就是OptMain.c的main函数。

  1. /* 
  2.  * Main entry point.  Decide where to go. 
  3.  */  
  4. int main(int argc, charconst argv[])  
  5. {  
  6.     set_process_name("dexopt");  
  7.   
  8.     setvbuf(stdout, NULL, _IONBF, 0);  
  9.   
  10.     if (argc > 1) {  
  11.         if (strcmp(argv[1], "--zip") == 0)  
  12.             return fromZip(argc, argv);  
  13.         else if (strcmp(argv[1], "--dex") == 0)  
  14.             return fromDex(argc, argv);  
  15.         else if (strcmp(argv[1], "--preopt") == 0)  
  16.             return preopt(argc, argv);  
  17.     }  
  18.     ......  
  19.     return 1;  
  20. }  
    代码位于dalvik\dexopt\OptMain.c。    


    由于执行时传入的参数是--zip,所以这里执行fromZip。

  1. static int fromZip(int argc, charconst argv[])  
  2. {  
  3.     ......  
  4.   
  5.     result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);  
  6.   
  7. bail:  
  8.     return result;  
  9. }  
    代码位于dalvik\dexopt\OptMain.c。    
    然后经过processZipFile->extractAndProcessZip->dvmContinueOptimization。


  1. bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,  
  2.     const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)  
  3. {  
  4.     DexClassLookup* pClassLookup = NULL;  
  5.     RegisterMapBuilder* pRegMapBuilder = NULL;  
  6.     u4 headerFlags = 0;  
  7.   
  8.     ......  
  9.   
  10.     {  
  11.         /* 
  12.          * Map the entire file (so we don't have to worry about page 
  13.          * alignment).  The expectation is that the output file contains 
  14.          * our DEX data plus room for a small header. 
  15.          */  
  16.         bool success;  
  17.         void* mapAddr;  
  18.         mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,  
  19.                     MAP_SHARED, fd, 0);  
  20.         if (mapAddr == MAP_FAILED) {  
  21.             LOGE("unable to mmap DEX cache: %s\n", strerror(errno));  
  22.             goto bail;  
  23.         }  
  24.   
  25.         ......  
  26.         success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,  
  27.                     &headerFlags, &pClassLookup);  
  28.   
  29.         if (success) {  
  30.             DvmDex* pDvmDex = NULL;  
  31.             u1* dexAddr = ((u1*) mapAddr) + dexOffset;  
  32.   
  33.             if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {  
  34.                 LOGE("Unable to create DexFile\n");  
  35.                 success = false;  
  36.             } else {  
  37.                 ......  
  38.             }  
  39.         }  
  40.   
  41.         ......  
  42.   
  43.         if (!success)  
  44.             goto bail;  
  45.     }  
  46.   
  47.     ......  
  48.   
  49.     if (writeDependencies(fd, modWhen, crc) != 0) {  
  50.         LOGW("Failed writing dependencies\n");  
  51.         goto bail;  
  52.     }  
  53.   
  54.     ......  
  55.     if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {  
  56.         LOGW("Failed writing opt data\n");  
  57.         goto bail;  
  58.     }  
  59.   
  60.     ......  
  61.     DexOptHeader optHdr;  
  62.     memset(&optHdr, 0xff, sizeof(optHdr));  
  63.     memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);  
  64.     memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);  
  65.     optHdr.dexOffset = (u4) dexOffset;  
  66.     optHdr.dexLength = (u4) dexLength;  
  67.     optHdr.depsOffset = (u4) depsOffset;  
  68.     optHdr.depsLength = (u4) depsLength;  
  69.     optHdr.optOffset = (u4) optOffset;  
  70.     optHdr.optLength = (u4) optLength;  
  71.   
  72.     optHdr.flags = headerFlags;  
  73.     optHdr.checksum = optChecksum;  
  74.   
  75.     fsync(fd);      /* ensure previous writes go before header is written */  
  76.   
  77.     lseek(fd, 0, SEEK_SET);  
  78.     if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)  
  79.         goto bail;  
  80.   
  81.     LOGV("Successfully wrote DEX header\n");  
  82.     result = true;  
  83.   
  84.     //dvmRegisterMapDumpStats();  
  85.   
  86. bail:  
  87.     dvmFreeRegisterMapBuilder(pRegMapBuilder);  
  88.     free(pClassLookup);  
  89.     return result;  
  90. }  
    dexOffset为odex文件头部大小,dexLength为dex文件长度。首先调用mmap把要优化的dex加载到内存虚拟地址mapAddr,这个dex其实就是位于/data/dalvik-cache/xxx@classes.dex。


    然后调用rewriteDex函数对目标文件进行优化验证,其主要内容包括:字符顺序调整、字节码替换、字节码验证以及文件结构重新对齐。

    然后通过writeDependencies写入依赖库信息,writeOptData写入其他优化信息,包括类索引信息以及寄存器映射关系。

    最后修改odex文件的头部内容。

    生成odex更为详细的流程请参考Android系统ODEX文件格式解析。

    此时生成的odex其实就是位于/data/dalvik-cache/xxx@classes.dex。

    odex结构图如下:


    
    0x03

    adb install的安装流程请参考深入理解PackageManagerService。整个安装流程,首先把apk拷贝到/data/local/tmp目录下,在安装的过程中把apk拷贝到/data/app中,最后调用了PackageManagerService的InstallPackagtLI,这个函数调用了installNewPackageLI,installNewPackageLI调用了scanPackageLI,在这个函数里面完成了apk的优化和安装,优化和安装的流程和上面一样。


    0x04

    本文中讲解了用于PathClassLoader加载/data/dalvik-cache/xxx@classes.dex的生成流程。

    那么DexClassLoader加载apk的流程是什么呢?

    注意PathClassLoader和DexClassLoader的构造函数有不同:

    PathClassLoader:

  1. public PathClassLoader(String path, String libPath, ClassLoader parent) {  
  2.     super(parent);  
  3.   
  4.     if (path == null)  
  5.         throw new NullPointerException();  
  6.   
  7.     this.path = path;  
  8.     this.libPath = libPath;  
  9.   
  10.     mPaths = path.split(":");  
  11.     int length = mPaths.length;  
  12.   
  13.     //System.out.println("PathClassLoader: " + mPaths);  
  14.     mFiles = new File[length];  
  15.     mZips = new ZipFile[length];  
  16.     mDexs = new DexFile[length];  
  17.   
  18.     boolean wantDex =  
  19.         System.getProperty("android.vm.dexfile""").equals("true");  
  20.   
  21.     /* open all Zip and DEX files up front */  
  22.     for (int i = 0; i < length; i++) {  
  23.         //System.out.println("My path is: " + mPaths[i]);  
  24.         File pathFile = new File(mPaths[i]);  
  25.         mFiles[i] = pathFile;  
  26.   
  27.         if (pathFile.isFile()) {  
  28.             try {  
  29.                 mZips[i] = new ZipFile(pathFile);  
  30.             }  
  31.             catch (IOException ioex) {  
  32.                 // expecting IOException and ZipException  
  33.                 //System.out.println("Failed opening '" + pathFile + "': " + ioex);  
  34.                 //ioex.printStackTrace();  
  35.             }  
  36.             if (wantDex) {  
  37.                 /* we need both DEX and Zip, because dex has no resources */  
  38.                 try {  
  39.                     mDexs[i] = new DexFile(pathFile);  
  40.                 }  
  41.                 catch (IOException ioex) {}  
  42.             }  
  43.         }  
  44.     }  
  45.     ......  
  46. }  


    最终调用的是new DexFile(pathFile)。

    而DexClassLoader:

  1. public DexClassLoader(String dexPath, String dexOutputDir, String libPath,  
  2.     ClassLoader parent) {  
  3.   
  4.     super(parent);  
  5.   
  6.     if (dexPath == null || dexOutputDir == null)  
  7.         throw new NullPointerException();  
  8.   
  9.     mRawDexPath = dexPath;  
  10.     mDexOutputPath = dexOutputDir;  
  11.     mRawLibPath = libPath;  
  12.   
  13.     String[] dexPathList = mRawDexPath.split(":");  
  14.     int length = dexPathList.length;  
  15.   
  16.     //System.out.println("DexClassLoader: " + dexPathList);  
  17.     mFiles = new File[length];  
  18.     mZips = new ZipFile[length];  
  19.     mDexs = new DexFile[length];  
  20.   
  21.     /* open all Zip and DEX files up front */  
  22.     for (int i = 0; i < length; i++) {  
  23.         //System.out.println("My path is: " + dexPathList[i]);  
  24.         File pathFile = new File(dexPathList[i]);  
  25.         mFiles[i] = pathFile;  
  26.   
  27.         if (pathFile.isFile()) {  
  28.             try {  
  29.                 mZips[i] = new ZipFile(pathFile);  
  30.             } catch (IOException ioex) {  
  31.                 // expecting IOException and ZipException  
  32.                 System.out.println("Failed opening '" + pathFile  
  33.                     + "': " + ioex);  
  34.                 //ioex.printStackTrace();  
  35.             }  
  36.   
  37.             /* we need both DEX and Zip, because dex has no resources */  
  38.             try {  
  39.                 String outputName =  
  40.                     generateOutputName(dexPathList[i], mDexOutputPath);  
  41.                 mDexs[i] = DexFile.loadDex(dexPathList[i], outputName, 0);  
  42.             } catch (IOException ioex) {  
  43.                 // might be a resource-only zip  
  44.                 System.out.println("Failed loadDex '" + pathFile  
  45.                     + "': " + ioex);  
  46.             }  
  47.         } else {  
  48.             if (VERBOSE_DEBUG)  
  49.                 System.out.println("Not found: " + pathFile.getPath());  
  50.         }  
  51.     }  
  52.   
  53.     .......  
  54. }  
    最终调用的是DexFile.loadDex(dexPathList[i], outputName, 0)。

    说明DexClassLoader还需要指定一个生成优化后的apk的路径。而PathClassLoader则不需要,因为在安装阶段已经生成了/data/dalvik-cache/xxx@classes.dex。

本站文章内容,部分来自于互联网,若侵犯了您的权益,请致邮件chuanghui423#sohu.com(请将#换为@)联系,我们会尽快核实后删除。
Copyright © 2006-2023 DBMNG.COM All Rights Reserved. Powered by DEVSOARTECH            豫ICP备11002312号-2

豫公网安备 41010502002439号