Android Framework重要服务之PackageManagerService(三) dex优化

809 阅读2分钟

上一章分析了PMS的APP安装流程,APK经过复制、创建对应包文件夹、安装之后,还剩一个比较重要的点需要分析,那就是dex编译,本章将结合Android 11继续探究dex的编译流程。

在executePostCommitSteps中,存在如下代码:

private void executePostCommitSteps(CommitRequest commitRequest) {
    ...
    
    // 判断是否需要dex优化
    final boolean performDexopt =
            (!instantApp || Global.getInt(mContext.getContentResolver(),
            Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
            && !pkg.isDebuggable()
            && (!onIncremental)
            && dexoptOptions.isCompilationEnabled();

    // 如有必要,执行dex优化
    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");
        ScanResult result = reconciledPkg.scanResult;
    
        PackageSetting realPkgSetting = result.existingSettingCopied
                ? result.request.pkgSetting : result.pkgSetting;
        if (realPkgSetting == null) {
            realPkgSetting = reconciledPkg.pkgSetting;
        }

        // Unfortunately, the updated system app flag is only tracked on this PackageSetting
        boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
                .isUpdatedSystemApp();

        realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);

        // 开始执行dex优化
        mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                null /* instructionSets */,
                getOrCreateCompilerPackageStats(pkg),
                mDexManager.getPackageUseInfoOrDefault(packageName),
                dexoptOptions);
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    ...
    
}

在这其中存在dex优化方法performDexOpt(),下面继续深入分析该方法。

performDexOpt

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java

int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
        String[] instructionSets, CompilerStats.PackageStats packageStats,
        PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        ...
        
            // 开始执行dex优化
            synchronized (mInstallLock) {
                final long acquireTime = acquireWakeLockLI(pkg.getUid());
                try {
                    return performDexOptLI(pkg, pkgSetting, instructionSets,
                           packageStats, packageUseInfo, options);
                } finally {
                    releaseWakeLockLI(acquireTime);
            }
        }
        ...
        
    }

可以看到,该方法调用了performDexOptLI()方法

performDexOptLI

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java

private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
        String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
        PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        ...
        
    for (String dexCodeIsa : dexCodeInstructionSets) {
        // 执行dexOptPath
        int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
            profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
            packageStats, options.isDowngrade(), profileName, dexMetadataPath,
            options.getCompilationReason());
        ...

        // The end result is:
        //  - FAILED if any path failed,
        //  - PERFORMED if at least one path needed compilation,
        //  - SKIPPED when all paths are up to date
        if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
            result = newResult;
        }
    }
    ...
}

dexOptPath

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java

@GuardedBy("mInstallLock")
private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path,
        String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext,
        int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
        String profileName, String dexMetadataPath, int compilationReason) {
        ...
        // 
        mInstaller.dexopt(path, uid, pkg.getPackageName(), isa, dexoptNeeded, oatDir,
                dexoptFlags, compilerFilter, pkg.getVolumeUuid(), classLoaderContext,
                seInfo, false /* downgrade*/, pkg.getTargetSdkVersion(),
                profileName, dexMetadataPath,
                getAugmentedReasonName(compilationReason, dexMetadataPath != null));
                
        ...
   }
   
// [frameworks/base/services/core/java/com/android/server/pm/Installer.java]
// dexopt优化方法, 该方法通过binder与 frameworks/native/cmds/installd/installd.cpp 进行通信
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
        int dexoptNeeded, @Nullable String outputPath, int dexFlags,
        String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
        @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
        @Nullable String profileName, @Nullable String dexMetadataPath,
        @Nullable String compilationReason) throws InstallerException {
        assertValidInstructionSet(instructionSet);
        BlockGuard.getVmPolicy().onPathAccess(apkPath);
        BlockGuard.getVmPolicy().onPathAccess(outputPath);
        BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
        if (!checkBeforeRemote()) return;
        try {
            mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
                    targetSdkVersion, profileName, dexMetadataPath, compilationReason);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

在这里可以看出,dexopt方法通过binder与frameworks/native/cmds/installd/installd.cpp进行通信,调用了installd.cpp的dexopt方法。

dexopt

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,
        /* out */ bool* completed) {
        ...
        
        // 使用dex2opt进行dex优化
        RunDex2Oat runner(dex2oat_bin, execv_helper.get());
        runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(),
                  in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
                  join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
                  debuggable, boot_complete, for_restore, target_sdk_version,
                  enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
                  compilation_reason);
                  
         ...              
}

这个方法最终调用了dex2opt对dex文件进行优化,值得注意的时,dex2opttargetSdkVersion>=29Android 10+的系统上不再允许在应用进程上触发dex2oat编译。其原因是系统在targetSdkVersion=29的时候,对此做了限制,不允许应用进程上触发dex2oat编译,通过应用进程触发dex2oat编译来弥补系统触发dex2oat编译的不足的方式,已经在andorid 10+的系统上不再适用,这点需要特别注意。