理解包安装中的OAT流程

600 阅读24分钟

在 APK 中,Java 和 Kotlin 代码被编译为 dex 字节码,但由于存储和加载完全编译的App的成本,并未完全编译为机器码。 应用程序中经常使用的类和方法,以及用于应用程序启动的类和方法,都记录在Profiles中。 一旦设备进入空闲模式,ART 就会根据这些Profiles编译应用程序。 这加快了后续应用程序的启动。

它的原理是尽可能的将启动需要加载的dex 进行 oat 化从而来达到启动优化的目的。那么,为什么 oat 后就能提升运行速度呢?

关于OAT 文件

1. OAT 文件格式

与 OAT 相关的文件后缀有如下几种:

  1. art

这个文件在android系统中被称为 image ,是由 dex2oat工具生成的。它的内部包含了很多 Dex 文件,Zygote 在启动的过程中会加载 boot.art.

在 android 5.0 版本中,.art 文件只有一个,即 boot.art,存储路径在 /system/framework/oat 或者 data/dalvik-cache/ 中。

从 android 7.0 开始,application 也可以有 image 了。

  1. oat

OAT 是由 dex2oat 产生的。相较于 .oat, .art 包含了很多已经预初始化了的类和对象,运行速度会快一些,同时体积也会更大一些。

  1. odex

在 Dalvik 中,odex 标识杯优化后的 Dex 文件;Art 虚拟机中同样也存在 odex 文件,和 Dalvik 中不同的是,Art 虚拟机中的 odex 文件实际上也是 .oat 文件。

  1. vdex

vdex 是在android 8.0 引入的机制,目的是为了降低dex2oat时间。关于 vdex 的介绍可以看这篇文章 Android 8.0 VDEX机制简介

2. OAT 文件是什么

OAT 文件是android 基于 linux 中的可执行文件格式 ELF 所做的拓展

oat 文件和elf文件最大的区别在于oat文件多了两个重要的区段,即 ‘oat data section’和‘oat exec section’。其中,‘data section’保存的是原 dex 文件中的字节码数据,而 ‘exec section’则是 dex 经过 dex2oat 翻译后生成的机器码的存储区段。并且我们可以通过一定的对应关系可以迅速找到某个 class/function 在 exec section 中的机器码。

与 data 和 exec section 有关的有3个重要变量,即 oatdata、oatexec 和 oatlastword,其中oatdata指向的是 oatdata section 的起始地址,oatexec指向的是 oatexec section的起始地址,oatlastword指向的是 oatexec section 的结束地址。

因为 data 和 exec 两个区域是紧挨着的,所以[oatdata,oatexec]就是dex的存储区域,[oatexec,oatlastword]是机器指令的存储区域。

3. oat 文件的加载

OatFile* OatFile::Open(int zip_fd,

                       const std::string& oat_filename,

                       const std::string& oat_location,

                       bool executable,

                       bool low_4gb,

                       const char* abs_dex_location,

                       /*inout*/MemMap* reservation,

                       /*out*/std::string* error_msg) {

  ScopedTrace trace("Open oat file " + oat_location);

  CHECK(!oat_filename.empty()) << oat_location;

  CheckLocation(oat_location);



  std::string vdex_filename = GetVdexFilename(oat_filename);



  // Check that the files even exist, fast-fail.

  if (!OS::FileExists(vdex_filename.c_str())) {

    *error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str());

    return nullptr;

  } else if (!OS::FileExists(oat_filename.c_str())) {

    *error_msg = StringPrintf("File %s does not exist.", oat_filename.c_str());

    return nullptr;

  }



  // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is

  // disabled.

  OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(zip_fd,

                                                                 vdex_filename,

                                                                 oat_filename,

                                                                 oat_location,

                                                                 /*writable=*/ false,

                                                                 executable,

                                                                 low_4gb,

                                                                 abs_dex_location,

                                                                 reservation,

                                                                 error_msg);

  if (with_dlopen != nullptr) {

    return with_dlopen;

  }

  if (kPrintDlOpenErrorMessage) {

    LOG(ERROR) << "Failed to dlopen: " << oat_filename << " with error " << *error_msg;

  }

  // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:

  //

  // On target, dlopen may fail when compiling due to selinux restrictions on installd.

  //

  // We use our own ELF loader for Quick to deal with legacy apps that

  // open a generated dex file by name, remove the file, then open

  // another generated dex file with the same name. http://b/10614658

  //

  // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile.

  //

  //

  // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually

  // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN.

  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(zip_fd,

                                                                vdex_filename,

                                                                oat_filename,

                                                                oat_location,

                                                                /*writable=*/ false,

                                                                executable,

                                                                low_4gb,

                                                                abs_dex_location,

                                                                reservation,

                                                                error_msg);

  return with_internal;

}

art有两套可选方案加载oat文件:dlopen加载器 DlOpenOatFile 和 elf 加载器 ElfOatFile。dlopen加载器主要使用系统函数dlopen来加载,elf加载器主要使用art内部实现的方式来加载。art会首先尝试dlopen加载器加载,加载不成功就换用elf加载器。

OpenOatFile 步骤包含以上5步

  • 第一步主要是进行一些准备工作
  • Load主要作用是将oat文件程序头表的PT_LOAD段映射到参数reservation的预留内存中
  • ComputeFields会获取oat文件的各个动态符号在内存中的地址,保存在成员变量中。
  • PreSetup的主要作用是创建多个Dummy MemMap来跟踪每个加载的PT_LOAD段。
  • Setup的作用是读取oat文件中记录的OatDexFile的二进制信息,计算出该oat文件对应的dex文件在内存中的位置,构建出一个启动运行时的OatDexFile。

先看 ComputeFields

bool OatFileBase::ComputeFields(const std::string& file_path, std::string* error_msg) {

  std::string symbol_error_msg;

  begin_ = FindDynamicSymbolAddress("oatdata", &symbol_error_msg);

 

  end_ = FindDynamicSymbolAddress("oatlastword", &symbol_error_msg);

 

  // Readjust to be non-inclusive upper bound.

  end_ += sizeof(uint32_t);



  data_bimg_rel_ro_begin_ = FindDynamicSymbolAddress("oatdatabimgrelro", &symbol_error_msg);

  if (data_bimg_rel_ro_begin_ != nullptr) {

    data_bimg_rel_ro_end_ =

        FindDynamicSymbolAddress("oatdatabimgrelrolastword", &symbol_error_msg);

    if (data_bimg_rel_ro_end_ == nullptr) {

      *error_msg =

          StringPrintf("Failed to find oatdatabimgrelrolastword symbol in '%s'", file_path.c_str());

      return false;

    }

    // Readjust to be non-inclusive upper bound.

    data_bimg_rel_ro_end_ += sizeof(uint32_t);

  }



  bss_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbss", &symbol_error_msg));

  if (bss_begin_ == nullptr) {

    // No .bss section.

    bss_end_ = nullptr;

  } else {

    bss_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbsslastword", &symbol_error_msg));

    if (bss_end_ == nullptr) {

      *error_msg = StringPrintf("Failed to find oatbsslastword symbol in '%s'", file_path.c_str());

      return false;

    }

    // Readjust to be non-inclusive upper bound.

    bss_end_ += sizeof(uint32_t);

    // Find bss methods if present.

    bss_methods_ =

        const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssmethods", &symbol_error_msg));

    // Find bss roots if present.

    bss_roots_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssroots", &symbol_error_msg));

  }



  vdex_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdex", &symbol_error_msg));

  if (vdex_begin_ == nullptr) {

    // No .vdex section.

    vdex_end_ = nullptr;

  } else {

    vdex_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdexlastword", &symbol_error_msg));

    if (vdex_end_ == nullptr) {

      *error_msg = StringPrintf("Failed to find oatdexlastword symbol in '%s'", file_path.c_str());

      return false;

    }

    // Readjust to be non-inclusive upper bound.

    vdex_end_ += sizeof(uint32_t);

  }



  return true;

}

ComputeFields 主要通过 FindDynamicSymbolAddress 查找 Dynamic Symbol,并将它们保持到全局变量中,这样就可以跟进这些字段确定各区段到起始地址了;

bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) {



  //..... 省略一些校验代码

  size_t oat_dex_files_offset = GetOatHeader().GetOatDexFilesOffset();

 

  const uint8_t* oat = Begin() + oat_dex_files_offset;  // Jump to the OatDexFile records.



  uint32_t dex_file_count = GetOatHeader().GetDexFileCount();

  oat_dex_files_storage_.reserve(dex_file_count);

  for (size_t i = 0; i < dex_file_count; i++) {

    uint32_t dex_file_location_size;

 

    const char* dex_file_location_data = reinterpret_cast<const char*>(oat);

    oat += dex_file_location_size;



    // Location encoded in the oat file. We will use this for multidex naming,

    // see ResolveRelativeEncodedDexLocation.

    std::string oat_dex_file_location(dex_file_location_data, dex_file_location_size);

    // If `oat_dex_file_location` is relative (so that the oat file can be moved to

    // a different folder), resolve to absolute location. Also resolve the file name

    // in case dex files need to be opened from disk. The file name and location

    // differ when cross-compiling on host for target.

    std::string dex_file_name;

    std::string dex_file_location;

    ResolveRelativeEncodedDexLocation(abs_dex_location,

                                      oat_dex_file_location,

                                      &dex_file_location,

                                      &dex_file_name);



    uint32_t dex_file_checksum;

    uint32_t dex_file_offset;

    const uint8_t* dex_file_pointer = nullptr;

    if (UNLIKELY(dex_file_offset == 0U)) {

      if (uncompressed_dex_files_ == nullptr) {

        // Do not support mixed-mode oat files.

        uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>());

        // No dex files, load it from location.

        const ArtDexFileLoader dex_file_loader;

        bool loaded = false;

        if (zip_fd != -1) {

          loaded = dex_file_loader.OpenZip(zip_fd,

                                           dex_file_location,

                                           /*verify=*/ false,

                                           /*verify_checksum=*/ false,

                                           error_msg,

                                           uncompressed_dex_files_.get());

        } else {

          loaded = dex_file_loader.Open(dex_file_name.c_str(),

                                        dex_file_location,

                                        /*verify=*/ false,

                                        /*verify_checksum=*/ false,

                                        error_msg,

                                        uncompressed_dex_files_.get());

        }

      ...

      dex_file_pointer = (*uncompressed_dex_files_)[i]->Begin();

    } else {

      // Do not support mixed-mode oat files.

      ...

      dex_file_pointer = DexBegin() + dex_file_offset;

    }



    const bool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer);

    //dex file magic 校验

    const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);

    //dex file Header 校验

    uint32_t class_offsets_offset;

    // ...



    const uint32_t* class_offsets_pointer =

        reinterpret_cast<const uint32_t*>(Begin() + class_offsets_offset);



    uint32_t lookup_table_offset;

    // ...

    const uint8_t* lookup_table_data = lookup_table_offset != 0u

        ? Begin() + lookup_table_offset

        : nullptr;



    uint32_t dex_layout_sections_offset;

 

    const DexLayoutSections* const dex_layout_sections = dex_layout_sections_offset != 0

        ? reinterpret_cast<const DexLayoutSections*>(Begin() + dex_layout_sections_offset)

        : nullptr;



    const IndexBssMapping* method_bss_mapping;

    const IndexBssMapping* type_bss_mapping;

    const IndexBssMapping* string_bss_mapping;





    // Create the OatDexFile and add it to the owning container.

    OatDexFile* oat_dex_file = new OatDexFile(

        this,

        dex_file_location,

        DexFileLoader::GetDexCanonicalLocation(dex_file_name.c_str()),

        dex_file_checksum,

        dex_file_pointer,

        lookup_table_data,

        method_bss_mapping,

        type_bss_mapping,

        string_bss_mapping,

        class_offsets_pointer,

        dex_layout_sections);

    oat_dex_files_storage_.push_back(oat_dex_file);



    // Add the location and canonical location (if different) to the oat_dex_files_ table.

    // Note: we use the dex_file_location_data storage for the view, as oat_dex_file_location

    // is just a temporary string.

    std::string_view key(dex_file_location_data, dex_file_location_size);

    std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());

    oat_dex_files_.Put(key, oat_dex_file);

    if (canonical_key != key) {

      oat_dex_files_.Put(canonical_key, oat_dex_file);

    }

  }



  ...



  return true;

}

OatDexFile 保存了 dex 文件的所有属性和内容数据。一个oat文件中包含n个dex文件,而每个dex文件又包含n个class,每个class又包含n个函数,那么问题来了,既然art已经将dex提前编译成了oat文件,为何oat文件还要保留原始dex呢?这不是浪费体积么?

Art 的这种设计有以下几点考虑

  1. 提供便利的class到native code的对应关系,从而加速代码执行速度
  2. 为开发调试提供更准确的源码定位
  3. 还是有很多情况下需要解释器来对dex文件进行解释执行

回到最初的问题,为什么 oat 后就能提升运行速度呢?

  1. 我们的app存在大量的直接或者间接对系统framework层代码的调用,对于系统应用来说,其运行环境在编译期间是可以确定的,那么系统层的这些大量代码在设备上已经是尽可能的 .art 化了的,并且 frameworks image 和 frameworks code 是可以直接提供给oat中的cpmpiled method 直接调用和访问的,而不需要在程序启动的时候动态创建,这样无疑能很大程度提升程序运行速度。
  2. Oat 文件包含了不少对 dex 文件进行 preload 的数据,省去了大量内存开辟和赋值的指令

app安装过程

android系统中处理包安装过程分为系统app和非系统app,这里仅仅介绍非系统app的流程。同时我们重点关注 oat 相关的部分,由于篇幅限制,其它中间流程将会简单带过,其中的许多细节也将不再展开。

apk安装请求发起

对非系统应用来说,apk 应用程序的安装过程中就会 dex2oat。承载这一功能的服务是 Package Installer,Package Installer 本身也是一个系统级的应用程序。这里我们按照时间轴,对从发起安装请求到安装结束这期间的过程进行介绍。

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.setDataAndType(apkUri, "application/vnd.android.package-archive");

startActivity(intent);

Apk 安装请求的发起是通过 startActivity 唤醒的,系统中响应上诉 intent ****的是一个叫做 InstallStart 的 Activity。

app层面上的一系列ui展示与跳转

/*com.android.packageinstaller.InstallStart*/

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

  

    ...

    final boolean isSessionInstall =

            PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());

    ...



    final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);

    final int originatingUid = getOriginatingUid(sourceInfo);

    ...

    Intent nextActivity = new Intent(intent);

    nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);



    // The the installation source as the nextActivity thinks this activity is the source, hence

    // set the originating UID and sourceInfo explicitly

    nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);

    nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);

    nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);



     // 如果设置了ACTION_CONFIRM_PERMISSIONS,则调用PackageInstallerActivity

    if (isSessionInstall) {

        nextActivity.setClass(this, PackageInstallerActivity.class);

    } else {

        Uri packageUri = intent.getData();



        if (packageUri != null && packageUri.getScheme().equals(

                ContentResolver.SCHEME_CONTENT)) {

            // 调用InstallStaging来拷贝file/content,防止被修改

            nextActivity.setClass(this, InstallStaging.class);

        } else if (packageUri != null && packageUri.getScheme().equals(

                PackageInstallerActivity.SCHEME_PACKAGE)) {

            // 如果Uri中包含package,则调用PackageInstallerActivity

            nextActivity.setClass(this, PackageInstallerActivity.class);

        } else {

            // Uri不合法 

            Intent result = new Intent();

            result.putExtra(Intent.EXTRA_INSTALL_RESULT,

                    PackageManager.INSTALL_FAILED_INVALID_URI);

            setResult(RESULT_FIRST_USER, result);



            nextActivity = null;

        }

    }



    if (nextActivity != null) {

        startActivity(nextActivity);

    }

    finish();

}

InstallStart 根据 Uri 的 Scheme 协议,若是 content 则调用 InstallStaging。InstallStaging 创建临时文件 mStagedFile 用来存储数据, 开启了一个StagingAsyncTask,在 StagingAsyncTask 的 doInBackground 方法中将 packageUri(content 协议的 Uri)的内容写入到 mStagedFile 中,如果写入成功,调用 DeleteStagedFileOnResult 的 OnCreate 方法,再次跳转到PackageInstallerActivity。

所以,InstallStaging 的作用就相当于一个中转站,进行一下协议数据转换,最终的实现还是要交由PackageInstallerActivity来执行。而这个 PackageInstallerActivity 就是要显示的安装界面

/*com.android.packageinstaller.PackageInstallerActivity*/

@Override

protected void onCreate(Bundle icicle) {

    getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);

    super.onCreate(null);

    mPm = getPackageManager(); //

    mIpm = AppGlobals.getPackageManager();

    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);

    mInstaller = mPm.getPackageInstaller();

    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);



    // ... 执行一些 Scheme 处理 校验

    

 

    boolean wasSetUp = processPackageUri(packageUri);

    if (!wasSetUp) {

        return;

    }



    // load dummy layout with OK button disabled until we override this layout in

    // startInstallConfirm

    bindUi();

    checkIfAllowedAndInitiateInstall();

}

getPackageManager 最终实现是在 ContextImpl 中,得到的是一个ApplicationPackageManager 对象;AppGlobals.getPackageManager 获取到的则是 PackageManagerService 的代理。当数据一切正常,用户点击确定后最终会执行到 startInstall()方法上, startInstall() 也是一个中转,通过 startActivity 跳转到了 InstallInstalling 的 Activity 上。

/*com.android.packageinstaller.InstallInstalling*/

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);



    ApplicationInfo appInfo = getIntent()

            .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);

    mPackageURI = getIntent().getData();



     // 判断安装的应用是否已经存在

    if ("package".equals(mPackageURI.getScheme())) {

        try {

            getPackageManager().installExistingPackage(appInfo.packageName);

            launchSuccess();

        } catch (PackageManager.NameNotFoundException e) {

            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);

        }

    } else {

        final File sourceFile = new File(mPackageURI.getPath());

        ...



        if (savedInstanceState != null) {

            // 如果savedInstanceState 不为空,获取已经存在mSessionId 和mInstallId 重新注册

            mSessionId = savedInstanceState.getInt(SESSION_ID);

            mInstallId = savedInstanceState.getInt(INSTALL_ID);



            // Reregister for result; might instantly call back if result was delivered while

            // activity was destroyed

            try {

                InstallEventReceiver.addObserver(this, mInstallId,

                        this::launchFinishBasedOnResult);

            } catch (EventResultPersister.OutOfIdsException e) {

                // Does not happen

            }

        } else {

            // 如果为空创建SessionParams,解析APK, 并将解析的参数赋值给SessionParams

            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(

                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);

            ...

           

            try {

               // 注册InstallEventReceiver,并在launchFinishBasedOnResult会接收到安装事件的回调

                mInstallId = InstallEventReceiver

                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,

                                this::launchFinishBasedOnResult);

            } catch (EventResultPersister.OutOfIdsException e) {

                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);

            }



            try {

                mSessionId = getPackageManager().getPackageInstaller().createSession(params);

            } catch (IOException e) {

                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);

            }

        }



        mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);



        mSessionCallback = new InstallSessionCallback();

    }

}

非首次安装

如果安装的应用是否已经存在,直接通过 ApplicationPackageManager 的 installExistingPackage 进行安装;installExistingPackage 最终调用到了 installExistingPackageAsUser 方法上

/*android.app.ApplicationPackageManager*/

private int installExistingPackageAsUser(String packageName, int installReason, int userId)

        throws NameNotFoundException {

    try {

        int res = mPM.installExistingPackageAsUser(packageName, userId,

                INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, installReason, null);

        if (res == INSTALL_FAILED_INVALID_URI) {

            throw new NameNotFoundException("Package " + packageName + " doesn't exist");

        }

        return res;

    } catch (RemoteException e) {

        throw e.rethrowFromSystemServer();

    }

}
/*com.android.server.pm.PackageManagerService*/



final Installer mInstaller



int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,

        @PackageManager.InstallFlags int installFlags,

        @PackageManager.InstallReason int installReason,

        @Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) {

        ...

        if (installed) {

            ...

            // start async restore with no post-install since we finish install here

            PackageInstalledInfo res =

                    createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);

            res.pkg = pkgSetting.pkg;

            res.newUsers = new int[]{ userId };

            PostInstallData postInstallData = intentSender == null ? null :

                    new PostInstallData(null, res, () -> onRestoreComplete(res.returnCode,

                          mContext, intentSender));

            restoreAndPostInstall(userId, res, postInstallData);

        }



    return PackageManager.INSTALL_SUCCEEDED;

}

installExistingPackageAsUser 根据app类型是即时应用还是完整安装包有不同的处理,但都会根据各种情况更新 Settings 配置等相关信息,最后通过 restoreAndPostInstall 来处理apk 的安装或者备份,如果需要备份,则会交由 BackupManagerService 实现;如果不需要备份或者备份失败则会发送一个POST_INSTALL 的 Message 出去执行安装。

非首次安装要处理和判断的干扰逻辑比较多,从app安装流程来说看首次安装的逻辑会更清晰一些,接下来的安装流程我们将以首次安装的来进行介绍。

首次安装

在进入流程之前,先做一个简单的铺垫,介绍一下 Installer,先有个初步的印象。

在 PackageManagerService 中有一个成员变量 mInstaller,这个 Installer 继承自 SystemService,是一个系统服务,该服务是用来与在 init.rc 中启动的一个 installd 系统服务进行通信的。mInstaller 这个变量是 SystemServer 在构造 PackageManagerService 时传递给后者的。

SystemServer 负责启动 Installer 服务,后者则通过 connect 与 installd 建立通信连接。installd 即 Install Deamon, 不难猜到 installd 服务端应该是真正用来执行安装程序的地方。

接回上文,如果非 package 类型的 Scheme,则会注册一个观察者 InstallEventReceiver,并在 launchFinishBasedOnResult 会接收到安装事件的回调,接着会通过 onResume 中的InstallingAsyncTask 用来执行 APK 的安装。InstallingAsyncTask 做了两件事情,一个是在doInBackground 中通过io流的形式将apk 写入 PackageInstallerSession,然后通过在 onPostExecute 中发送广播通知InstallEventReceiver处理

/*com.android.packageinstaller.InstallInstalling*/

protected PackageInstaller.Session doInBackground(Void... params) {

    PackageInstaller.Session session;

    try {

        session = getPackageManager().getPackageInstaller().openSession(mSessionId);

    } catch (IOException e) {

        return null;

    }



    session.setStagingProgress(0);



    try {

        File file = new File(mPackageURI.getPath());



        try (InputStream in = new FileInputStream(file)) {

            long sizeBytes = file.length();

            // 将apk数据写入PackageInstallerSession

            try (OutputStream out = session

                    .openWrite("PackageInstaller", 0, sizeBytes)) {

                byte[] buffer = new byte[1024 * 1024];

                while (true) {

                    int numRead = in.read(buffer);



                    if (numRead == -1) {

                        session.fsync(out);

                        break;

                    }



                    if (isCancelled()) {

                        session.close();

                        break;

                    }



                    out.write(buffer, 0, numRead);

                    if (sizeBytes > 0) {

                        float fraction = ((float) numRead / (float) sizeBytes);

                        session.addProgress(fraction);

                    }

                }

            }

        }



        return session;

    } 

}
/* com.android.server.pm.PackageManagerService */

@Override

public PackageInstaller getPackageInstaller() {

    synchronized (mLock) {

        if (mInstaller == null) {

            try {

                mInstaller = new PackageInstaller(mPM.getPackageInstaller(),

                        mContext.getPackageName(), getUserId());

            } catch (RemoteException e) {

                throw e.rethrowFromSystemServer();

            }

        }

        return mInstaller;

    }

}

这里的 mPM.getPackageInstaller() 中的 mPM 对应binder 服务端的 PackageManagerService, mPM.getPackageInstaller() 则是 PackageInstallerService; PackageInstaller.openSession 得到的是一个 PackageInstallerSession 对象。

apk 写入 session 后便会通过 commit 提交。PackageInstallerSession 的 commit 进行一些验证通过后又通过一个 Handler 将消息转发了出去,handler 收到消息后交由 handleCommit 方法处理,handleCommit 又调用到 commitNonStagedLocked,commitNonStagedLocked 最终交由 mPm.installStage 去执行,这里的 mPm 则是 PackageManagerService。

PackageManagerService 的 installStage 将数据封装成一个 InstallParams 对象,然后就又通过handler 发出去了,之后会回调到 PackageManagerService 的 startCopy 方法上去

/*com.android.server.pm.PackageManagerService*/

final void startCopy() {

    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

    handleStartCopy();

    handleReturnCode();

}

handleStartCopy 会对包进行解析,进行一些校验,真正执行安装的流程则是在handleReturnCode 方法中,具体则是 processPendingInstall 方法

/*com.android.server.pm.PackageManagerService*/

private void processPendingInstall(final InstallArgs args, final int currentStatus) {

    if (args.mMultiPackageInstallParams != null) {

        args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);

    } else {

        PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);

        processInstallRequestsAsync(

                res.returnCode == PackageManager.INSTALL_SUCCEEDED,

                Collections.singletonList(new InstallRequest(args, res)));

    }

}



private void processInstallRequestsAsync(boolean success,

        List<InstallRequest> installRequests) {

    mHandler.post(() -> {

        if (success) {

      

            synchronized (mInstallLock) {

                 //安装apk

                installPackagesTracedLI(installRequests);

            }

       

        }

        for (InstallRequest request : installRequests) {

            // 安装后续:备份、可能的回滚、发送广播

            restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,

                    new PostInstallData(request.args, request.installResult, null));

        }

    });

}





private void installPackagesTracedLI(List<InstallRequest> requests) {

    try {

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");

        installPackagesLI(requests);

    } finally {

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

    }

}







private void installPackagesLI(List<InstallRequest> requests) {

 

       for (InstallRequest request : requests) {

       

        

            // 分析当前安装状态,解析包 并初始校验

            prepareResult = preparePackageLI(request.args, request.installResult);

        

        

                // 进一步解析 校验

                final List<ScanResult> scanResults = scanPackageTracedLI(

                        prepareResult.packageToScan, prepareResult.parseFlags,

                        prepareResult.scanFlags, System.currentTimeMillis(),

                        request.args.user);

              

   

        }

        ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,

                installResults,

                prepareResults,

                mSharedLibraries,

                Collections.unmodifiableMap(mPackages), versionInfos,

                lastStaticSharedLibSettings);

        CommitRequest commitRequest = null;

        synchronized (mPackages) {

          

                // 签名验证等 还是校验

                reconciledPackages = reconcilePackagesLocked(

                        reconcileRequest, mSettings.mKeySetManagerService);

    

   

                // 提交扫描的包、更新系统状态,PMS、PermissionManager、ComponentResolver等

                commitRequest = new CommitRequest(reconciledPackages,

                        sUserManager.getUserIds());

                commitPackagesLocked(commitRequest);   

        }

        

        // 准备app数据、执行dex优化

        executePostCommitSteps(commitRequest);

    } 
  1. 分析当前包的状态,解析包,对包进行权限、签名等一系列安全性和完整性校验
/*com.android.server.pm.PackageManagerService*/

private void executePostCommitSteps(CommitRequest commitRequest) {

    for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {

          

          ....

        // Prepare the application profiles for the new code paths.

        // This needs to be done before invoking dexopt so that any install-time profile

        // can be used for optimizations.

        mArtManagerService.prepareAppProfiles(

                pkg,

                resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),

                /* updateReferenceProfileContent= */ true);



        final boolean performDexopt =

                (!instantApp || Global.getInt(mContext.getContentResolver(),

                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)

                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);



        if (performDexopt) {

            // Compile the layout resources.

            if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {

                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");

                mViewCompiler.compileLayouts(pkg);

                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

            }



            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");

            // Do not run PackageDexOptimizer through the local performDexOpt

            // method because `pkg` may not be in `mPackages` yet.

            //

            // Also, don't fail application installs if the dexopt step fails.

            DexoptOptions dexoptOptions = new DexoptOptions(packageName,

                    REASON_INSTALL,

                    DexoptOptions.DEXOPT_BOOT_COMPLETE

                            | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);

            mPackageDexOptimizer.performDexOpt(pkg,

                    null /* instructionSets */,

                    getOrCreateCompilerPackageStats(pkg),

                    mDexManager.getPackageUseInfoOrDefault(packageName),

                    dexoptOptions);

            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        }



        // Notify BackgroundDexOptService that the package has been changed.

        // If this is an update of a package which used to fail to compile,

        // BackgroundDexOptService will remove it from its blacklist.

        // TODO: Layering violation

        BackgroundDexOptService.notifyPackageChanged(packageName);

    }

}

对 AppProfiles 文件的处理

调用 ArtManagerService.prepareAppProfiles 解析包里面的 profiles 文件,将解析出的内容 交由 mInstaller.prepareAppProfile 进行处理,mInstaller 即之前提到过的 Installer 服务,Installer 服务仅仅只是 java 层对native 层的 IInstalld 一层封装代理。故而ArtManagerService.prepareAppProfiles最终是由 mInstalld.prepareAppProfile 处理的

/*com.android.server.pm.dex.ArtManagerService*/

public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,

        boolean updateReferenceProfileContent) {

    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);

    ...

    try {

        ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);

        for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {

            String codePath = codePathsProfileNames.keyAt(i);

            String profileName = codePathsProfileNames.valueAt(i);

            String dexMetadataPath = null;

            // Passing the dex metadata file to the prepare method will update the reference

            // profile content. As such, we look for the dex metadata file only if we need to

            // perform an update.

            if (updateReferenceProfileContent) {

                File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));

                dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();

            }

            synchronized (mInstaller) {

                boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,

                        profileName, codePath, dexMetadataPath);

                if (!result) {

                    Slog.e(TAG, "Failed to prepare profile for " +

                            pkg.packageName + ":" + codePath);

                }

            }

        }

    } catch (InstallerException e) {

        Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);

    }

}

dex2oat

如果需要开启 Dexopt 优化,通过 PackageDexOptimizer.performDexOpt 执行 dex2oat,该方法最终会执行到 dexOptPath 上交由 mInstaller.dexopt ->mInstalld.dexopt。

Native 层 installd 服务的启动

installd 服务是由 installd.rc 文件中配置,由系统开机启动的

/* rameworks/native/cmds/installd/installd.rc */

service installd /system/bin/installd

    class main

main 函数为入口函数,首先还是先进行一系列环境校验判断,然后调用到 InstalldNativeService::start 开启服务,服务启动后通过 IPCThreadState::self()->joinThreadPool() 让其加入 binder 线程进入到循环中,等待命令,解析命令,给出响应

static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {

    int ret;

    int selinux_enabled = (is_selinux_enabled() > 0);



    setenv("ANDROID_LOG_TAGS", "*:v", 1);

    android::base::InitLogging(argv);



    SLOGI("installd firing up");



    union selinux_callback cb;

    cb.func_log = log_callback;

    selinux_set_callback(SELINUX_CB_LOG, cb);



    if (!initialize_globals()) {

        SLOGE("Could not initialize globals; exiting.\n");

        exit(1);

    }



    if (initialize_directories() < 0) {

        SLOGE("Could not create directories; exiting.\n");

        exit(1);

    }



    if (selinux_enabled && selinux_status_open(true) < 0) {

        SLOGE("Could not open selinux status; exiting.\n");

        exit(1);

    }



    if ((ret = InstalldNativeService::start()) != android::OK) {

        SLOGE("Unable to start InstalldNativeService: %d", ret);

        exit(1);

    }



    IPCThreadState::self()->joinThreadPool();



    LOG(INFO) << "installd shutting down";



    return 0;

}

不难看出,InstalldNativeService 则是 installd 在 native 层的 binder 服务端。

Installd 执行 prepareAppProfile 与 dexopt

/*frameworks/native/cmds/installd/InstalldNativeService.cpp*/

binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,

        int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,

        const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) {

     ...

    *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath,

        dexMetadata);

    return ok();

}
/*frameworks/native/cmds/installd/dexopt.cpp*/

bool prepare_app_profile(const std::string& package_name,

                         userid_t user_id,

                         appid_t app_id,

                         const std::string& profile_name,

                         const std::string& code_path,

                         const std::unique_ptr<std::string>& dex_metadata) {

    // Prepare the current profile.

    std::string cur_profile  = create_current_profile_path(user_id, package_name, profile_name,

            /*is_secondary_dex*/ false);



    // We have a dex metdata. Merge the profile into the reference profile.

    unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name,

            /*read_write*/ true, /*is_secondary_dex*/ false);

    unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY(

            open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW)));

    unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)));

    if (apk_fd < 0) {

        PLOG(ERROR) << "Could not open code path " << code_path;

        return false;

    }



    RunProfman args;

    args.SetupCopyAndUpdate(std::move(dex_metadata_fd),

                            std::move(ref_profile_fd),

                            std::move(apk_fd),

                            code_path);

    pid_t pid = fork();

    if (pid == 0) {

        /* child -- drop privileges before continuing */

        gid_t app_shared_gid = multiuser_get_shared_gid(user_id, app_id);

        drop_capabilities(app_shared_gid);



        // The copy and update takes ownership over the fds.

        args.Exec();

    }



    /* parent */

    int return_code = wait_child(pid);

    if (!WIFEXITED(return_code)) {

        PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;

        return false;

    }

    return true;

}



}  // namespace installd

}  // namespace android

prepare_app_profile 首先会根据 create_current_profile_path 确定从oat目录还是data目录查找当前的profile文件,然后根据情况对profile文件执行merge操作,之后凋用 SetupCopyAndUpdate 同步和更新数据,确定使用 dex2oatd 或者 dex2oat 类型的二进制文件等准备工作。

/*frameworks/native/cmds/installd/InstalldNativeService.cpp*/

binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,

        const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,

        int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,

        const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,

        const std::unique_ptr<std::string>& classLoaderContext,

        const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,

        const std::unique_ptr<std::string>& profileName,

        const std::unique_ptr<std::string>& dexMetadataPath,

        const std::unique_ptr<std::string>& compilationReason) {

 

    std::lock_guard<std::recursive_mutex> lock(mLock);

    ...

    int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,

            oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,

            downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg);

    return res ? error(res, error_msg) : ok();

}
/*frameworks/native/cmds/installd/dexopt.cpp*/

int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,

        int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,

        const char* volume_uuid, const char* class_loader_context, const char* se_info,

        bool downgrade, int target_sdk_version, const char* profile_name,

        const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) {



     ...



    // Open the input file.

    unique_fd input_fd(open(dex_path, O_RDONLY, 0));

   



    // Open class loader context dex files.

    std::vector<unique_fd> context_input_fds;

    if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) {

        LOG(ERROR) << *error_msg;

        return -1;

    }

  



    // Create the output OAT file.

    char out_oat_path[PKG_PATH_MAX];

    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,

            instruction_set, is_secondary_dex, out_oat_path);





    // Open vdex files.

    Dex2oatFileWrapper in_vdex_fd;

    Dex2oatFileWrapper out_vdex_fd;

    if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,

            is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {

        *error_msg = "Could not open vdex files.";

        return -1;

    }



    // Create a swap file if necessary.

    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);



    // Create the app image file if needed.

    Dex2oatFileWrapper image_fd = maybe_open_app_image(

            out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);



    // Open the reference profile if needed.

    Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(

            pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);





    RunDex2Oat runner(input_fd.get(),

                      out_oat_fd.get(),

                      in_vdex_fd.get(),

                      out_vdex_fd.get(),

                      image_fd.get(),

                      dex_path,

                      out_oat_path,

                      swap_fd.get(),

                      instruction_set,

                      compiler_filter,

                      debuggable,

                      boot_complete,

                      background_job_compile,

                      reference_profile_fd.get(),

                      class_loader_context,

                      join_fds(context_input_fds),

                      target_sdk_version,

                      enable_hidden_api_checks,

                      generate_compact_dex,

                      dex_metadata_fd.get(),

                      compilation_reason);



    pid_t pid = fork();

    if (pid == 0) {

        /* child -- drop privileges before continuing */

        drop_capabilities(uid);



        SetDex2OatScheduling(boot_complete);

        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {

            PLOG(ERROR) << "flock(" << out_oat_path << ") failed";

            _exit(DexoptReturnCodes::kFlock);

        }



        runner.Exec(DexoptReturnCodes::kDex2oatExec);

    } else {

        int res = wait_child(pid);

        if (res == 0) {

            LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---";

        } else {

            LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"

                         << std::hex << std::setw(4) << res << ", process failed";

            *error_msg = format_dexopt_error(res, dex_path);

            return res;

        }

    }



    update_out_oat_access_times(dex_path, out_oat_path);



    // We've been successful, don't delete output.

    out_oat_fd.SetCleanup(false);

    out_vdex_fd.SetCleanup(false);

    image_fd.SetCleanup(false);

    reference_profile_fd.SetCleanup(false);



    return 0;

}
  • 打开输入文件,即apk
  • loader dex 文件
  • 创建输出的 OAT 文件
  • 打开 vdex 文件
  • 根据情况对swap、app_image、profile 文件选择性处理
  • 构建一个 RunDex2Oat runner对象,fork 一个新的进程,执行任务
   /*frameworks/native/cmds/installd/dexopt.cpp*/ 

    

    RunDex2Oat(int zip_fd,

               int oat_fd,

               int input_vdex_fd,

               int output_vdex_fd,

               int image_fd,

               const char* input_file_name,

               const char* output_file_name,

               int swap_fd,

               const char* instruction_set,

               const char* compiler_filter,

               bool debuggable,

               bool post_bootcomplete,

               bool background_job_compile,

               int profile_fd,

               const char* class_loader_context,

               const std::string& class_loader_context_fds,

               int target_sdk_version,

               bool enable_hidden_api_checks,

               bool generate_compact_dex,

               int dex_metadata_fd,

               const char* compilation_reason) {

        // Get the relative path to the input file.

        const char* relative_input_file_name = get_location_from_path(input_file_name);



        std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");

        std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");



        const char* threads_property = post_bootcomplete

                ? "dalvik.vm.dex2oat-threads"

                : "dalvik.vm.boot-dex2oat-threads";

        std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");



        std::string bootclasspath;

        char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");

        if (dex2oat_bootclasspath != nullptr) {

            bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);

        }

        // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query

        // BOOTCLASSPATH.



        const std::string dex2oat_isa_features_key =

                StringPrintf("dalvik.vm.isa.%s.features", instruction_set);

        std::string instruction_set_features_arg =

            MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");



        const std::string dex2oat_isa_variant_key =

                StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);

        std::string instruction_set_variant_arg =

            MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");



        const char* dex2oat_norelocation = "-Xnorelocate";



        const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");

        std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);

        ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());



        // If we are booting without the real /data, don't spend time compiling.

        std::string vold_decrypt = GetProperty("vold.decrypt", "");

        bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||

                                vold_decrypt == "1";



        std::string resolve_startup_string_arg =

                MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",

                                 "--resolve-startup-const-strings=%s");

        if (resolve_startup_string_arg.empty()) {

          // If empty, fall back to system property.

          resolve_startup_string_arg =

                MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",

                                 "--resolve-startup-const-strings=%s");

        }



        const std::string image_block_size_arg =

                MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",

                                 "--max-image-block-size=%s");



        const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);



        std::string image_format_arg;

        if (image_fd >= 0) {

            image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");

        }



        std::string dex2oat_large_app_threshold_arg =

            MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");





        const char* dex2oat_bin = select_execution_binary(

            kDex2oatPath, kDex2oatDebugPath, background_job_compile);



        bool generate_minidebug_info = kEnableMinidebugInfo &&

                GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);



        std::string boot_image;

        std::string use_apex_image =

            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,

                                                                 ENABLE_APEX_IMAGE,

                                                                 /*default_value=*/ "");

        if (use_apex_image == "true") {

          boot_image = StringPrintf("-Ximage:%s", kApexImage);

        } else {

          boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");

        }



        // clang FORTIFY doesn't let us use strlen in constant array bounds, so we

        // use arraysize instead.

        std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);

        std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);

        std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);

        std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);

        std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);

        std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);

        std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);

        std::string dex2oat_compiler_filter_arg;

        std::string dex2oat_swap_fd;

        std::string dex2oat_image_fd;

        std::string target_sdk_version_arg;

        if (target_sdk_version != 0) {

            target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);

        }

        std::string class_loader_context_arg;

        std::string class_loader_context_fds_arg;

        if (class_loader_context != nullptr) {

            class_loader_context_arg = StringPrintf("--class-loader-context=%s",

                                                    class_loader_context);

            if (!class_loader_context_fds.empty()) {

                class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",

                                                            class_loader_context_fds.c_str());

            }

        }



        if (swap_fd >= 0) {

            dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);

        }

        if (image_fd >= 0) {

            dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);

        }



        // Compute compiler filter.

        bool have_dex2oat_relocation_skip_flag = false;

        if (skip_compilation) {

            dex2oat_compiler_filter_arg = "--compiler-filter=extract";

            have_dex2oat_relocation_skip_flag = true;

        } else if (compiler_filter != nullptr) {

            dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);

        }



        if (dex2oat_compiler_filter_arg.empty()) {

            dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",

                                                           "--compiler-filter=%s");

        }



        // Check whether all apps should be compiled debuggable.

        if (!debuggable) {

            debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";

        }

        std::string profile_arg;

        if (profile_fd != -1) {

            profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);

        }



        // Get the directory of the apk to pass as a base classpath directory.

        std::string base_dir;

        std::string apk_dir(input_file_name);

        unsigned long dir_index = apk_dir.rfind('/');

        bool has_base_dir = dir_index != std::string::npos;

        if (has_base_dir) {

            apk_dir = apk_dir.substr(0, dir_index);

            base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());

        }



        std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);



        std::string compilation_reason_arg = compilation_reason == nullptr

                ? ""

                : std::string("--compilation-reason=") + compilation_reason;



        ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);



        // Disable cdex if update input vdex is true since this combination of options is not

        // supported.

        const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);



        AddArg(zip_fd_arg);

        AddArg(zip_location_arg);

        AddArg(input_vdex_fd_arg);

        AddArg(output_vdex_fd_arg);

        AddArg(oat_fd_arg);

        AddArg(oat_location_arg);

        AddArg(instruction_set_arg);



        AddArg(instruction_set_variant_arg);

        AddArg(instruction_set_features_arg);



        AddRuntimeArg(boot_image);

        AddRuntimeArg(bootclasspath);

        AddRuntimeArg(dex2oat_Xms_arg);

        AddRuntimeArg(dex2oat_Xmx_arg);



        AddArg(resolve_startup_string_arg);

        AddArg(image_block_size_arg);

        AddArg(dex2oat_compiler_filter_arg);

        AddArg(dex2oat_threads_arg);

        AddArg(dex2oat_swap_fd);

        AddArg(dex2oat_image_fd);



        if (generate_debug_info) {

            AddArg("--generate-debug-info");

        }

        if (debuggable) {

            AddArg("--debuggable");

        }

        AddArg(image_format_arg);

        AddArg(dex2oat_large_app_threshold_arg);



        if (have_dex2oat_relocation_skip_flag) {

            AddRuntimeArg(dex2oat_norelocation);

        }

        AddArg(profile_arg);

        AddArg(base_dir);

        AddArg(class_loader_context_arg);

        AddArg(class_loader_context_fds_arg);

        if (generate_minidebug_info) {

            AddArg(kMinidebugDex2oatFlag);

        }

        if (disable_cdex) {

            AddArg(kDisableCompactDexFlag);

        }

        AddRuntimeArg(target_sdk_version_arg);

        if (enable_hidden_api_checks) {

            AddRuntimeArg("-Xhidden-api-policy:enabled");

        }



        if (dex_metadata_fd > -1) {

            AddArg(dex_metadata_fd_arg);

        }



        AddArg(compilation_reason_arg);



        // Do not add args after dex2oat_flags, they should override others for debugging.

        args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());



        PrepareArgs(dex2oat_bin);

    }

};

RunDex2Oat 是真正执行dex2oat的地方,里面逻辑比较长,细节较多,功能和 android 提供的 Dex2oat 工具一致。具体的细节实现和原理笔者也没完全搞懂,也不是本文的重点,这里就不班门弄斧了。大家如果对 dex2oat 具体细节感兴趣可以去查阅下其他大神的文章。

至此,从apk发起安装到执行生成 OAT 文件的流程算是打通了,整体由于链路比较长,代码量较多,很多细节都是一笔带过。通过这篇文章,如果能对 oat 和 apk 安装流程有一个相对清晰的认知其实也就达到目的了。