Android_framework-odex优化
代码路径
/frameworks/base/services/core/java/com/android/server/pm/
逻辑执行
通过
adb shell cmd package compile -m speed-profile -f com.tencent.mm
我们可以对com.tencent.mm进行odex优化, 通过cmd命令执行章节, 已经描述了通过cmd最终会将参数传递给PackageManagerShellCommand.java中的onCommand执行;
在BasicShellCommandHandler.java中已经记录例参数mArgs, 以及 目标mTarget;
ARM_SERVICE_COMMANDS中即为ART中涉及的一些指令,
runArtServiceCommand函数如下, 通过该函数即调用到ArtManagerLocal类对应的handleShellCommand函数中;
private int runArtServiceCommand() {try (var in = ParcelFileDescriptor.dup(getInFileDescriptor());var out = ParcelFileDescriptor.dup(getOutFileDescriptor());var err = ParcelFileDescriptor.dup(getErrFileDescriptor())) {return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class)// getTarget()和getAllArgs()即获得对应的服务以及所有的参数.handleShellCommand(getTarget(), in, out, err, getAllArgs());} catch (IOException e) {throw new IllegalStateException(e);} catch (ManagerNotFoundException e) {PrintWriter epw = getErrPrintWriter();epw.println("ART Service is not ready. Please try again later");return -1;}}
handleShellCommand和我们之前讲过的cmd调用逻辑再次对上, 该类的路径为
art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
然后创建一个新的类ArtShellCommand, 该类继承自BasicShellCommandHandler, 其中的exec函数会调用到onCommand函;
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)public int handleShellCommand(@NonNull Binder target, @NonNull ParcelFileDescriptor in,@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,@NonNull String[] args) {return new ArtShellCommand(this, mInjector.getPackageManagerLocal(), mInjector.getContext()).exec(target, in.getFileDescriptor(), out.getFileDescriptor(),err.getFileDescriptor(), args);}
在onCommand函数中, ArtShellCommand重新对传入的参数进行解析;
核心解析参数函数handleCompile,
private int handleCompile(@NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) {@DexoptFlags int scopeFlags = 0;String reason = null;String compilerFilter = null;@PriorityClassApi int priorityClass = ArtFlags.PRIORITY_NONE;String splitArg = null;boolean force = false;boolean reset = false;boolean forAllPackages = false;boolean legacyClearProfile = false;boolean verbose = false;boolean forceMergeProfile = false;boolean forceCompilerFilter = false;String opt;// 仅仅解析带- 和 --的选项函数while ((opt = getNextOption()) != null) {switch (opt) {// 对当前系统中所有的包都进行优化case "-a":forAllPackages = true;break;// 可以通过参数指定显示的reason, 这样可以看到我们的编译有没有成功case "-r":reason = getNextArgRequired();break;case "-m":// 获得编译参数 speed-profilecompilerFilter = getNextArgRequired();forceCompilerFilter = true;break;case "-p":priorityClass = parsePriorityClass(getNextArgRequired());break;case "-f":force = true;break;case "--primary-dex":scopeFlags |= ArtFlags.FLAG_FOR_PRIMARY_DEX;break;case "--secondary-dex":scopeFlags |= ArtFlags.FLAG_FOR_SECONDARY_DEX;break;case "--include-dependencies":scopeFlags |= ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES;break;case "--full":scopeFlags |= ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX| ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES;break;case "--split":splitArg = getNextArgRequired();break;case "--reset":reset = true;break;case "-c":pw.println("Warning: Flag '-c' is deprecated and usually produces undesired "+ "results. Please use one of the following commands instead.");pw.println("- To clear the local profiles only, use "+ "'pm art clear-app-profiles PACKAGE_NAME'. (The existing dexopt "+ "artifacts will be kept, even if they are derived from the "+ "profiles.)");pw.println("- To clear the local profiles and also clear the dexopt artifacts "+ "that are derived from them, use 'pm compile --reset PACKAGE_NAME'. "+ "(The package will be reset to the initial state as if it's newly "+ "installed, which means the package will be re-dexopted if "+ "necessary, and cloud profiles will be used if exist.)");pw.println("- To re-dexopt the package with no profile, use "+ "'pm compile -m verify -f PACKAGE_NAME'. (The local profiles "+ "will be kept but not used during the dexopt. The dexopt artifacts "+ "are guaranteed to have no compiled code.)");legacyClearProfile = true;break;case "--check-prof":getNextArgRequired();pw.println("Warning: Ignoring obsolete flag '--check-prof'. It is "+ "unconditionally enabled now");break;case "-v":verbose = true;break;case "--force-merge-profile":forceMergeProfile = true;break;default:pw.println("Error: Unknown option: " + opt);return 1;}}// 如果参数不是-a, 即不是对所有的package都做的话, 就解析下一个参数,获得包名List<String> packageNames = forAllPackages? List.copyOf(snapshot.getPackageStates().keySet()): List.of(getNextArgRequired());var paramsBuilder = new DexoptParams.Builder(ReasonMapping.REASON_CMDLINE);if (reason != null) {if (reason.equals(ReasonMapping.REASON_INACTIVE)) {pw.println("Warning: '-r inactive' produces undesired results.");}if (compilerFilter == null) {paramsBuilder.setCompilerFilter(ReasonMapping.getCompilerFilterForReason(reason));}if (priorityClass == ArtFlags.PRIORITY_NONE) {paramsBuilder.setPriorityClass(ReasonMapping.getPriorityClassForReason(reason));}}if (compilerFilter != null) {paramsBuilder.setCompilerFilter(compilerFilter);}if (priorityClass != ArtFlags.PRIORITY_NONE) {paramsBuilder.setPriorityClass(priorityClass);}if (force) {paramsBuilder.setFlags(ArtFlags.FLAG_FORCE, ArtFlags.FLAG_FORCE);}if (forceMergeProfile) {paramsBuilder.setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE, ArtFlags.FLAG_FORCE_MERGE_PROFILE);}if (forceCompilerFilter) {paramsBuilder.setFlags(ArtFlags.FLAG_FORCE_COMPILER_FILTER, ArtFlags.FLAG_FORCE_COMPILER_FILTER);}if (splitArg != null) {if (scopeFlags != 0) {pw.println("Error: '--primary-dex', '--secondary-dex', "+ "'--include-dependencies', or '--full' must not be set when '--split' "+ "is set.");return 1;}if (forAllPackages) {pw.println("Error: '-a' cannot be specified together with '--split'");return 1;}scopeFlags = ArtFlags.FLAG_FOR_PRIMARY_DEX;paramsBuilder.setFlags(ArtFlags.FLAG_FOR_SINGLE_SPLIT, ArtFlags.FLAG_FOR_SINGLE_SPLIT).setSplitName(getSplitName(pw, snapshot, packageNames.get(0), splitArg));}if (scopeFlags != 0) {paramsBuilder.setFlags(scopeFlags,ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX| ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);} else {paramsBuilder.setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES,ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX| ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);}if (forAllPackages) {// We'll iterate over all packages anyway.paramsBuilder.setFlags(0, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);}if (reset) {return resetPackages(pw, snapshot, packageNames, verbose);} else {if (legacyClearProfile) {// For compat only. Combining this with dexopt usually produces in undesired// results.for (String packageName : packageNames) {mArtManagerLocal.clearAppProfiles(snapshot, packageName);}}return dexoptPackages(pw, snapshot, packageNames, paramsBuilder.build(), verbose);}}private int dexoptPackages(@NonNull PrintWriter pw,@NonNull PackageManagerLocal.FilteredSnapshot snapshot,@NonNull List<String> packageNames, @NonNull DexoptParams params, boolean verbose) {try (var signal = new WithCancellationSignal(pw, verbose)) {for (String packageName : packageNames) {DexoptResult result =mArtManagerLocal.dexoptPackage(snapshot, packageName, params, signal.get());printDexoptResult(pw, result, verbose, packageNames.size() > 1);}}return 0;}
通过上面的dexoptPackages调用回到ArtManagerLocal.java
public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,@NonNull String packageName, @NonNull DexoptParams params,@NonNull CancellationSignal cancellationSignal) {mCleanupLock.readLock().lock();try (var pin = mInjector.createArtdPin()) {return mInjector.getDexoptHelper().dexopt(snapshot, List.of(packageName), params, cancellationSignal, Runnable::run);} finally {mCleanupLock.readLock().unlock();}}
然后调用到DexoptHelper.java中的dexopt函数, 最终调用dexoptPacakges函数; 在继续往下之前,现在看下传递来的参数都是怎样获得的;
- snapshot: 已安装包的视图快照, 用来减少索的使用, 优化性能;
- packageNames: 需要进行odex优化的包名
- params: 参数
@NonNullpublic DexoptResult dexopt(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,@NonNull List<String> packageNames, @NonNull DexoptParams params,@NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor) {return dexopt(snapshot, packageNames, params, cancellationSignal, dexoptExecutor,null /* progressCallbackExecutor */, null /* progressCallback */);}@NonNullpublic DexoptResult dexopt(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,@NonNull List<String> packageNames, @NonNull DexoptParams params,@NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor,@Nullable Executor progressCallbackExecutor,@Nullable Consumer<OperationProgress> progressCallback) {return dexoptPackages(getPackageStates(snapshot, packageNames,(params.getFlags() & ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES) != 0),params, cancellationSignal, dexoptExecutor, progressCallbackExecutor,progressCallback);}
getPackageStates定义在DexoptHelper.java中定义, 通过之前传递过来的packageName,通过关联的库文件, 获得了需要进行dex优化的packageState的列表;
private List<PackageState> getPackageStates(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,@NonNull List<String> packageNames, boolean includeDependencies) {var pkgStates = new LinkedHashMap<String, PackageState>();Set<String> visitedLibraries = new HashSet<>();Queue<SharedLibrary> queue = new LinkedList<>();Consumer<SharedLibrary> maybeEnqueue = library -> {// The package name is not null if the library is an APK.// TODO(jiakaiz): Support JAR libraries.if (library.getPackageName() != null && !library.isNative()&& !visitedLibraries.contains(library.getName())) {visitedLibraries.add(library.getName());queue.add(library);}};for (String packageName : packageNames) {PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);Utils.getPackageOrThrow(pkgState);pkgStates.put(packageName, pkgState);if (includeDependencies && canDexoptPackage(pkgState)) {for (SharedLibrary library : pkgState.getSharedLibraryDependencies()) {maybeEnqueue.accept(library);}}}// 通过上面的可以获得apk直接写明的依赖库,但是依赖库的库并不能获得, 因此,需要继续遍历依赖库,从而获得完整的依赖;SharedLibrary library;while ((library = queue.poll()) != null) {String packageName = library.getPackageName();PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);// canDexoptPackage主要考虑包本身的状态以及包的休眠状态来确定是否if (canDexoptPackage(pkgState)) {pkgStates.put(packageName, pkgState);// Note that `library.getDependencies()` is different from// `pkgState.getUsesLibraries()`. Different libraries can belong to the same// package. `pkgState.getUsesLibraries()` returns a union of dependencies of// libraries that belong to the same package, which is not what we want here.// Therefore, this loop cannot be unified with the one above.for (SharedLibrary dep : library.getDependencies()) {maybeEnqueue.accept(dep);}}}// `LinkedHashMap` guarantees deterministic order.return new ArrayList<>(pkgStates.values());}
接下来继续调用dexoptPackages函数, 进行opt优化, 该函数通过future创建子线程给每个package做dex优化,然后收集结果, 其中AutoValue_是一种自动格式化数值的方式, 后续可以再了解;
/*** DO NOT use this method directly. Use {@link ArtManagerLocal#dexoptPackage} or {@link* ArtManagerLocal#dexoptPackages}.*/@NonNullprivate DexoptResult dexoptPackages(@NonNull List<PackageState> pkgStates,@NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal,@NonNull Executor dexoptExecutor, @Nullable Executor progressCallbackExecutor,@Nullable Consumer<OperationProgress> progressCallback) {// TODO(jiakaiz): Find out whether this is still needed.long identityToken = Binder.clearCallingIdentity();try {List<CompletableFuture<PackageDexoptResult>> futures = new ArrayList<>();// Child threads will set their own listeners on the cancellation signal, so we must// create a separate cancellation signal for each of them so that the listeners don't// overwrite each other.List<CancellationSignal> childCancellationSignals =pkgStates.stream().map(pkgState -> new CancellationSignal()).collect(Collectors.toList());cancellationSignal.setOnCancelListener(() -> {for (CancellationSignal childCancellationSignal : childCancellationSignals) {childCancellationSignal.cancel();}});for (int i = 0; i < pkgStates.size(); i++) {PackageState pkgState = pkgStates.get(i);CancellationSignal childCancellationSignal = childCancellationSignals.get(i);//futures用来收集子线程结果futures.add(CompletableFuture.supplyAsync(() -> {AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);if (canDexoptPackage(pkgState)&& (params.getFlags() & ArtFlags.FLAG_FOR_SINGLE_SPLIT) != 0) {// Throws if the split is not found.PrimaryDexUtils.getDexInfoBySplitName(pkg, params.getSplitName());}try {// 执行优化函数return dexoptPackage(pkgState, pkg, params, childCancellationSignal);} catch (RuntimeException e) {AsLog.wtf("Unexpected package-level exception during dexopt", e);return PackageDexoptResult.create(pkgState.getPackageName(),new ArrayList<>() /* dexContainerFileDexoptResults */,DexoptResult.DEXOPT_FAILED);}}, dexoptExecutor));}if (progressCallback != null) {CompletableFuture.runAsync(() -> {progressCallback.accept(OperationProgress.create(0 /* current */, futures.size(), null /* packageDexoptResult */));}, progressCallbackExecutor);AtomicInteger current = new AtomicInteger(0);for (CompletableFuture<PackageDexoptResult> future : futures) {future.thenAcceptAsync(result -> {progressCallback.accept(OperationProgress.create(current.incrementAndGet(), futures.size(), result));}, progressCallbackExecutor).exceptionally(t -> {AsLog.e("Failed to update progress", t);return null;});}}// 获得子线程结果List<PackageDexoptResult> results =futures.stream().map(Utils::getFuture).collect(Collectors.toList());var result =DexoptResult.create(params.getCompilerFilter(), params.getReason(), results);for (Callback<DexoptDoneCallback, Boolean> doneCallback :mInjector.getConfig().getDexoptDoneCallbacks()) {boolean onlyIncludeUpdates = doneCallback.extra();if (onlyIncludeUpdates) {List<PackageDexoptResult> filteredResults =results.stream().filter(PackageDexoptResult::hasUpdatedArtifacts).collect(Collectors.toList());if (!filteredResults.isEmpty()) {var resultForCallback = DexoptResult.create(params.getCompilerFilter(), params.getReason(), filteredResults);CompletableFuture.runAsync(() -> {doneCallback.get().onDexoptDone(resultForCallback);}, doneCallback.executor());}} else {CompletableFuture.runAsync(() -> {doneCallback.get().onDexoptDone(result);}, doneCallback.executor());}}return result;} finally {Binder.restoreCallingIdentity(identityToken);// Make sure nothing leaks even if the caller holds `cancellationSignal` forever.cancellationSignal.setOnCancelListener(null);}}