1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.pm;
18 
19 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
20 
21 import static com.android.server.pm.DexOptHelper.useArtService;
22 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
23 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
24 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
25 import static com.android.server.pm.Installer.DEXOPT_FORCE;
26 import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE;
27 import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
28 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
29 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
30 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
31 import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
32 import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
33 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
34 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
35 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES;
36 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
37 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE;
38 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
39 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
40 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
41 import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
42 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
43 
44 import static dalvik.system.DexFile.getSafeModeCompilerFilter;
45 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
46 
47 import android.annotation.IntDef;
48 import android.annotation.NonNull;
49 import android.annotation.Nullable;
50 import android.content.ContentResolver;
51 import android.content.Context;
52 import android.content.pm.ApplicationInfo;
53 import android.content.pm.SharedLibraryInfo;
54 import android.content.pm.dex.ArtManager;
55 import android.content.pm.dex.DexMetadataHelper;
56 import android.os.FileUtils;
57 import android.os.PowerManager;
58 import android.os.SystemClock;
59 import android.os.SystemProperties;
60 import android.os.Trace;
61 import android.os.UserHandle;
62 import android.os.WorkSource;
63 import android.util.Log;
64 import android.util.Slog;
65 import android.util.SparseArray;
66 
67 import com.android.internal.annotations.GuardedBy;
68 import com.android.internal.annotations.VisibleForTesting;
69 import com.android.internal.content.F2fsUtils;
70 import com.android.internal.util.IndentingPrintWriter;
71 import com.android.server.LocalServices;
72 import com.android.server.apphibernation.AppHibernationManagerInternal;
73 import com.android.server.pm.Installer.InstallerException;
74 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
75 import com.android.server.pm.dex.ArtManagerService;
76 import com.android.server.pm.dex.ArtStatsLogUtils;
77 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
78 import com.android.server.pm.dex.DexoptOptions;
79 import com.android.server.pm.dex.DexoptUtils;
80 import com.android.server.pm.dex.PackageDexUsage;
81 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
82 import com.android.server.pm.pkg.AndroidPackage;
83 import com.android.server.pm.pkg.PackageState;
84 import com.android.server.pm.pkg.PackageStateInternal;
85 
86 import dalvik.system.DexFile;
87 
88 import java.io.File;
89 import java.io.IOException;
90 import java.lang.annotation.Retention;
91 import java.lang.annotation.RetentionPolicy;
92 import java.util.ArrayList;
93 import java.util.Arrays;
94 import java.util.List;
95 import java.util.Map;
96 import java.util.Random;
97 
98 /**
99  * Helper class for running dexopt command on packages.
100  */
101 public class PackageDexOptimizer {
102     private static final String TAG = "PackageDexOptimizer";
103     static final String OAT_DIR_NAME = "oat";
104     // TODO b/19550105 Remove error codes and use exceptions
105     /** No need to run dexopt and it was skipped */
106     public static final int DEX_OPT_SKIPPED = 0;
107     /** Dexopt was completed */
108     public static final int DEX_OPT_PERFORMED = 1;
109     /**
110      * Cancelled while running it. This is not an error case as cancel was requested
111      * from the client.
112      */
113     public static final int DEX_OPT_CANCELLED = 2;
114     /** Failed to run dexopt */
115     public static final int DEX_OPT_FAILED = -1;
116 
117     @IntDef(prefix = {"DEX_OPT_"}, value = {
118             DEX_OPT_SKIPPED,
119             DEX_OPT_PERFORMED,
120             DEX_OPT_CANCELLED,
121             DEX_OPT_FAILED,
122     })
123     @Retention(RetentionPolicy.SOURCE)
124     public @interface DexOptResult {
125     }
126 
127     // One minute over PM WATCHDOG_TIMEOUT
128     private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
129 
130     private final Object mInstallLock;
131 
132     /**
133      * This should be accessed only through {@link #getInstallerLI()} with {@link #mInstallLock}
134      * or {@link #getInstallerWithoutLock()} without the lock. Check both methods for further
135      * details on when to use each of them.
136      */
137     private final Installer mInstaller;
138 
139     @GuardedBy("mInstallLock")
140     private final PowerManager.WakeLock mDexoptWakeLock;
141     private volatile boolean mSystemReady;
142 
143     private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
144     private final Injector mInjector;
145 
146 
147     private final Context mContext;
148     private static final Random sRandom = new Random();
149 
PackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)150     PackageDexOptimizer(Installer installer, Object installLock, Context context,
151             String wakeLockTag) {
152         this(new Injector() {
153             @Override
154             public AppHibernationManagerInternal getAppHibernationManagerInternal() {
155                 return LocalServices.getService(AppHibernationManagerInternal.class);
156             }
157 
158             @Override
159             public PowerManager getPowerManager(Context context) {
160                 return context.getSystemService(PowerManager.class);
161             }
162         }, installer, installLock, context, wakeLockTag);
163     }
164 
PackageDexOptimizer(PackageDexOptimizer from)165     protected PackageDexOptimizer(PackageDexOptimizer from) {
166         this.mContext = from.mContext;
167         this.mInstaller = from.mInstaller;
168         this.mInstallLock = from.mInstallLock;
169         this.mDexoptWakeLock = from.mDexoptWakeLock;
170         this.mSystemReady = from.mSystemReady;
171         this.mInjector = from.mInjector;
172     }
173 
174     @VisibleForTesting
PackageDexOptimizer(@onNull Injector injector, Installer installer, Object installLock, Context context, String wakeLockTag)175     PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
176             Context context, String wakeLockTag) {
177         this.mContext = context;
178         this.mInstaller = installer;
179         this.mInstallLock = installLock;
180 
181         PowerManager powerManager = injector.getPowerManager(context);
182         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
183         mInjector = injector;
184     }
185 
canOptimizePackage(@onNull AndroidPackage pkg)186     boolean canOptimizePackage(@NonNull AndroidPackage pkg) {
187         // The system package has to be optimized during early boot by odrefresh instead.
188         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
189             return false;
190         }
191 
192         // We do not dexopt a package with no code.
193         if (!pkg.isDeclaredHavingCode()) {
194             return false;
195         }
196 
197         // We do not dexopt APEX packages.
198         if (pkg.isApex()) {
199             return false;
200         }
201 
202         // We do not dexopt unused packages.
203         // It's possible for this to be called before app hibernation service is ready due to
204         // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
205         // a hibernating app should have no artifacts to copy in the first place.
206         AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal();
207         if (ahm != null
208                 && ahm.isHibernatingGlobally(pkg.getPackageName())
209                 && ahm.isOatArtifactDeletionEnabled()) {
210             return false;
211         }
212 
213         return true;
214     }
215 
216     /**
217      * Performs dexopt on all code paths and libraries of the specified package for specified
218      * instruction sets.
219      *
220      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
221      * synchronized on {@link #mInstallLock}.
222      */
223     @DexOptResult
performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)224     int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
225             String[] instructionSets, CompilerStats.PackageStats packageStats,
226             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
227             throws LegacyDexoptDisabledException {
228         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
229             throw new IllegalArgumentException(
230                     "System server dexopting should be done via odrefresh");
231         }
232         if (pkg.getUid() == -1) {
233             throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName()
234                     + " has invalid uid.");
235         }
236         if (!canOptimizePackage(pkg)) {
237             return DEX_OPT_SKIPPED;
238         }
239         synchronized (mInstallLock) {
240             final long acquireTime = acquireWakeLockLI(pkg.getUid());
241             try {
242                 return performDexOptLI(pkg, pkgSetting, instructionSets,
243                         packageStats, packageUseInfo, options);
244             } finally {
245                 releaseWakeLockLI(acquireTime);
246             }
247         }
248     }
249 
250     /**
251      * Cancels currently running dex optimization.
252      */
controlDexOptBlocking(boolean block)253     void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
254         // This method should not hold mInstallLock as cancelling should be possible while
255         // the lock is held by other thread running performDexOpt.
256         getInstallerWithoutLock().controlDexOptBlocking(block);
257     }
258 
259     /**
260      * Performs dexopt on all code paths of the given package.
261      * It assumes the install lock is held.
262      */
263     @GuardedBy("mInstallLock")
264     @DexOptResult
performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)265     private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
266             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
267             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
268             throws LegacyDexoptDisabledException {
269         // ClassLoader only refers non-native (jar) shared libraries and must ignore
270         // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
271         final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
272                 .getNonNativeUsesLibraryInfos();
273         final String[] instructionSets = targetInstructionSets != null ?
274                 targetInstructionSets : getAppDexInstructionSets(
275                 pkgSetting.getPrimaryCpuAbi(),
276                 pkgSetting.getSecondaryCpuAbi());
277         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
278         final List<String> paths = AndroidPackageUtils.getAllCodePaths(pkg);
279 
280         int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
281         if (sharedGid == -1) {
282             Slog.wtf(TAG, "Well this is awkward; package " + pkg.getPackageName() + " had UID "
283                     + pkg.getUid(), new Throwable());
284             sharedGid = android.os.Process.NOBODY_UID;
285         }
286 
287         // Get the class loader context dependencies.
288         // For each code path in the package, this array contains the class loader context that
289         // needs to be passed to dexopt in order to ensure correct optimizations.
290         boolean[] pathsWithCode = new boolean[paths.size()];
291         pathsWithCode[0] = pkg.isDeclaredHavingCode();
292         for (int i = 1; i < paths.size(); i++) {
293             pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
294         }
295         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
296                 pkg, sharedLibraries, pathsWithCode);
297 
298         // Validity check that we do not call dexopt with inconsistent data.
299         if (paths.size() != classLoaderContexts.length) {
300             String[] splitCodePaths = pkg.getSplitCodePaths();
301             throw new IllegalStateException("Inconsistent information "
302                 + "between AndroidPackage and its ApplicationInfo. "
303                 + "pkg.getAllCodePaths=" + paths
304                 + " pkg.getBaseCodePath=" + pkg.getBaseApkPath()
305                 + " pkg.getSplitCodePaths="
306                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
307         }
308 
309         int result = DEX_OPT_SKIPPED;
310         for (int i = 0; i < paths.size(); i++) {
311             // Skip paths that have no code.
312             if (!pathsWithCode[i]) {
313                 continue;
314             }
315             if (classLoaderContexts[i] == null) {
316                 throw new IllegalStateException("Inconsistent information in the "
317                         + "package structure. A split is marked to contain code "
318                         + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
319             }
320 
321             // Append shared libraries with split dependencies for this split.
322             String path = paths.get(i);
323             if (options.getSplitName() != null) {
324                 // We are asked to compile only a specific split. Check that the current path is
325                 // what we are looking for.
326                 if (!options.getSplitName().equals(new File(path).getName())) {
327                     continue;
328                 }
329             }
330 
331             String profileName = ArtManager.getProfileName(
332                     i == 0 ? null : pkg.getSplitNames()[i - 1]);
333 
334             final boolean isUsedByOtherApps;
335             if (options.isDexoptAsSharedLibrary()) {
336                 isUsedByOtherApps = true;
337             } else if (useArtService()) {
338                 // We get here when collecting dexopt commands in OTA preopt, even when ART Service
339                 // is in use. packageUseInfo isn't useful in that case since the legacy dex use
340                 // database hasn't been updated. So we'd have to query ART Service instead, but it
341                 // doesn't provide that API. Just cop-out and bypass the cloud profile handling.
342                 // That means such apps will get preopted wrong, and we'll leave it to a later
343                 // background dexopt after reboot instead.
344                 isUsedByOtherApps = false;
345             } else {
346                 isUsedByOtherApps = packageUseInfo.isUsedByOtherApps(path);
347             }
348 
349             String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
350             // If the app is used by other apps, we must not use the existing profile because it
351             // may contain user data, unless the profile is newly created on install.
352             final boolean useCloudProfile = isProfileGuidedCompilerFilter(compilerFilter)
353                     && isUsedByOtherApps
354                     && options.getCompilationReason() != PackageManagerService.REASON_INSTALL;
355 
356             String dexMetadataPath = null;
357             if (options.isDexoptInstallWithDexMetadata() || useCloudProfile) {
358                 File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
359                 dexMetadataPath = dexMetadataFile == null
360                         ? null : dexMetadataFile.getAbsolutePath();
361             }
362 
363             // If we don't have to check for profiles updates assume
364             // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
365             // profiles.
366             int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
367             if (options.isCheckForProfileUpdates()) {
368                 profileAnalysisResult =
369                         analyseProfiles(pkg, sharedGid, profileName, compilerFilter);
370             }
371             String cloudProfileName = null;
372             try {
373                 if (useCloudProfile) {
374                     cloudProfileName = "cloud-" + profileName;
375                     if (prepareCloudProfile(pkg, cloudProfileName, path, dexMetadataPath)) {
376                         profileName = cloudProfileName;
377                     } else {
378                         // Fall back to use the shared filter.
379                         compilerFilter =
380                                 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
381                                         PackageManagerService.REASON_SHARED);
382                         profileName = null;
383                     }
384 
385                     // We still run `analyseProfiles` even if `useCloudProfile` is true because it
386                     // merges profiles into the reference profile, which a system API
387                     // `ArtManager.snapshotRuntimeProfile` takes snapshots from. However, we don't
388                     // want the result to affect the decision of whether dexopt is needed.
389                     profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
390                 }
391 
392                 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
393                 // flags.
394                 final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter,
395                         useCloudProfile, options);
396 
397                 for (String dexCodeIsa : dexCodeInstructionSets) {
398                     int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
399                             profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
400                             packageStats, options.isDowngrade(), profileName, dexMetadataPath,
401                             options.getCompilationReason());
402                     // OTAPreopt doesn't have stats so don't report in that case.
403                     if (packageStats != null) {
404                         Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dex2oat-metrics");
405                         try {
406                             long sessionId = sRandom.nextLong();
407                             ArtStatsLogUtils.writeStatsLog(
408                                     mArtStatsLogger,
409                                     sessionId,
410                                     compilerFilter,
411                                     pkg.getUid(),
412                                     packageStats.getCompileTime(path),
413                                     dexMetadataPath,
414                                     options.getCompilationReason(),
415                                     newResult,
416                                     ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(),
417                                             pkg.getSplitCodePaths()),
418                                     dexCodeIsa,
419                                     path);
420                         } finally {
421                             Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
422                         }
423                     }
424 
425                     // Should stop the operation immediately.
426                     if (newResult == DEX_OPT_CANCELLED) {
427                         // Even for the cancellation, return failed if has failed.
428                         if (result == DEX_OPT_FAILED) {
429                             return result;
430                         }
431                         return newResult;
432                     }
433                     // The end result is:
434                     //  - FAILED if any path failed,
435                     //  - PERFORMED if at least one path needed compilation,
436                     //  - SKIPPED when all paths are up to date
437                     if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
438                         result = newResult;
439                     }
440                 }
441             } finally {
442                 if (cloudProfileName != null) {
443                     try {
444                         mInstaller.deleteReferenceProfile(pkg.getPackageName(), cloudProfileName);
445                     } catch (InstallerException e) {
446                         Slog.w(TAG, "Failed to cleanup cloud profile", e);
447                     }
448                 }
449             }
450         }
451         return result;
452     }
453 
454     /**
455      * Creates a profile with the name {@code profileName} from the dex metadata file at {@code
456      * dexMetadataPath} for the dex file at {@code path} belonging to the package {@code pkg}.
457      *
458      * @return true on success, or false otherwise.
459      */
460     @GuardedBy("mInstallLock")
prepareCloudProfile(AndroidPackage pkg, String profileName, String path, @Nullable String dexMetadataPath)461     private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
462             @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
463         if (dexMetadataPath != null) {
464             if (mInstaller.isIsolated()) {
465                 // If the installer is isolated, the two calls to it below will return immediately,
466                 // so this only short-circuits that a bit. We need to do it to avoid the
467                 // LegacyDexoptDisabledException getting thrown first, when we get here during OTA
468                 // preopt and ART Service is enabled.
469                 return true;
470             }
471 
472             try {
473                 // Make sure we don't keep any existing contents.
474                 mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName);
475 
476                 final int appId = UserHandle.getAppId(pkg.getUid());
477                 mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, appId,
478                         profileName, path, dexMetadataPath);
479                 return true;
480             } catch (InstallerException e) {
481                 Slog.w(TAG, "Failed to prepare cloud profile", e);
482                 return false;
483             }
484         } else {
485             return false;
486         }
487     }
488 
489     /**
490      * Performs dexopt on the {@code path} belonging to the package {@code pkg}.
491      *
492      * @return
493      *      DEX_OPT_FAILED if there was any exception during dexopt
494      *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
495      *      DEX_OPT_SKIPPED if the path does not need to be deopt-ed.
496      */
497     @GuardedBy("mInstallLock")
498     @DexOptResult
dexOptPath(AndroidPackage pkg, @NonNull PackageStateInternal 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)499     private int dexOptPath(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
500             String path, String isa, String compilerFilter, int profileAnalysisResult,
501             String classLoaderContext, int dexoptFlags, int uid,
502             CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
503             String dexMetadataPath, int compilationReason) throws LegacyDexoptDisabledException {
504         String oatDir = getPackageOatDirIfSupported(pkgSetting, pkg);
505 
506         int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
507                 classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir);
508         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
509             return DEX_OPT_SKIPPED;
510         }
511 
512         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
513                 + " pkg=" + pkg.getPackageName() + " isa=" + isa
514                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
515                 + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
516                 + " classLoaderContext=" + classLoaderContext);
517 
518         try {
519             long startTime = System.currentTimeMillis();
520 
521             // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
522             // installd only uses downgrade flag for secondary dex files and ignores it for
523             // primary dex files.
524             String seInfo = pkgSetting.getSeInfo();
525             boolean completed = getInstallerLI().dexopt(path, uid, pkg.getPackageName(), isa,
526                     dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.getVolumeUuid(),
527                     classLoaderContext, seInfo, /* downgrade= */ false ,
528                     pkg.getTargetSdkVersion(), profileName, dexMetadataPath,
529                     getAugmentedReasonName(compilationReason, dexMetadataPath != null));
530             if (!completed) {
531                 return DEX_OPT_CANCELLED;
532             }
533             if (packageStats != null) {
534                 long endTime = System.currentTimeMillis();
535                 packageStats.setCompileTime(path, (int)(endTime - startTime));
536             }
537             if (oatDir != null) {
538                 // Release odex/vdex compressed blocks to save user space.
539                 // Compression support will be checked in F2fsUtils.
540                 // The system app may be dexed, oatDir may be null, skip this situation.
541                 final ContentResolver resolver = mContext.getContentResolver();
542                 F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
543             }
544             return DEX_OPT_PERFORMED;
545         } catch (InstallerException e) {
546             Slog.w(TAG, "Failed to dexopt", e);
547             return DEX_OPT_FAILED;
548         }
549     }
550 
getAugmentedReasonName(int compilationReason, boolean useDexMetadata)551     private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
552         String annotation = useDexMetadata
553                 ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
554         return getReasonName(compilationReason) + annotation;
555     }
556 
557     /**
558      * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
559      *
560      * @return
561      *      DEX_OPT_FAILED if there was any exception during dexopt
562      *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
563      * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
564      * didn't need an update. That's because at the moment we don't get more than success/failure
565      * from installd.
566      *
567      * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
568      * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
569      * that seems wasteful.
570      */
571     @DexOptResult
dexOptSecondaryDexPath(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)572     public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
573             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
574             throws LegacyDexoptDisabledException {
575         if (info.uid == -1) {
576             throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid.");
577         }
578         synchronized (mInstallLock) {
579             final long acquireTime = acquireWakeLockLI(info.uid);
580             try {
581                 return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
582             } finally {
583                 releaseWakeLockLI(acquireTime);
584             }
585         }
586     }
587 
588     @GuardedBy("mInstallLock")
acquireWakeLockLI(final int uid)589     private long acquireWakeLockLI(final int uid) {
590         // During boot the system doesn't need to instantiate and obtain a wake lock.
591         // PowerManager might not be ready, but that doesn't mean that we can't proceed with
592         // dexopt.
593         if (!mSystemReady) {
594             return -1;
595         }
596         mDexoptWakeLock.setWorkSource(new WorkSource(uid));
597         mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
598         return SystemClock.elapsedRealtime();
599     }
600 
601     @GuardedBy("mInstallLock")
releaseWakeLockLI(final long acquireTime)602     private void releaseWakeLockLI(final long acquireTime) {
603         if (acquireTime < 0) {
604             return;
605         }
606         try {
607             if (mDexoptWakeLock.isHeld()) {
608                 mDexoptWakeLock.release();
609             }
610             final long duration = SystemClock.elapsedRealtime() - acquireTime;
611             if (duration >= WAKELOCK_TIMEOUT_MS) {
612                 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
613                         + " time out. Operation took " + duration + " ms. Thread: "
614                         + Thread.currentThread().getName());
615             }
616         } catch (RuntimeException e) {
617             Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
618         }
619     }
620 
621     @GuardedBy("mInstallLock")
622     @DexOptResult
dexOptSecondaryDexPathLI(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)623     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
624             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
625             throws LegacyDexoptDisabledException {
626         String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
627                 dexUseInfo.isUsedByOtherApps());
628         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
629         // Secondary dex files are currently not compiled at boot.
630         int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
631         // Check the app storage and add the appropriate flags.
632         if (info.deviceProtectedDataDir != null &&
633                 FileUtils.contains(info.deviceProtectedDataDir, path)) {
634             dexoptFlags |= DEXOPT_STORAGE_DE;
635         } else if (info.credentialProtectedDataDir != null &&
636                 FileUtils.contains(info.credentialProtectedDataDir, path)) {
637             dexoptFlags |= DEXOPT_STORAGE_CE;
638         } else {
639             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
640             return DEX_OPT_FAILED;
641         }
642         String classLoaderContext = null;
643         if (dexUseInfo.isUnsupportedClassLoaderContext()
644                 || dexUseInfo.isVariableClassLoaderContext()) {
645             // If we have an unknown (not yet set), or a variable class loader chain. Just verify
646             // the dex file.
647             compilerFilter = "verify";
648         } else {
649             classLoaderContext = dexUseInfo.getClassLoaderContext();
650         }
651 
652         int reason = options.getCompilationReason();
653         Log.d(TAG, "Running dexopt on: " + path
654                 + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
655                 + " reason=" + getReasonName(reason)
656                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
657                 + " target-filter=" + compilerFilter
658                 + " class-loader-context=" + classLoaderContext);
659 
660         try {
661             for (String isa : dexUseInfo.getLoaderIsas()) {
662                 // Reuse the same dexopt path as for the primary apks. We don't need all the
663                 // arguments as some (dexopNeeded and oatDir) will be computed by installd because
664                 // system server cannot read untrusted app content.
665                 // TODO(calin): maybe add a separate call.
666                 boolean completed = getInstallerLI().dexopt(path, info.uid, info.packageName,
667                         isa, /* dexoptNeeded= */ 0,
668                         /* outputPath= */ null, dexoptFlags,
669                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfo,
670                         options.isDowngrade(), info.targetSdkVersion, /* profileName= */ null,
671                         /* dexMetadataPath= */ null, getReasonName(reason));
672                 if (!completed) {
673                     return DEX_OPT_CANCELLED;
674                 }
675             }
676 
677             return DEX_OPT_PERFORMED;
678         } catch (InstallerException e) {
679             Slog.w(TAG, "Failed to dexopt", e);
680             return DEX_OPT_FAILED;
681         }
682     }
683 
684     /**
685      * Adjust the given dexopt-needed value. Can be overridden to influence the decision to
686      * optimize or not (and in what way).
687      */
adjustDexoptNeeded(int dexoptNeeded)688     protected int adjustDexoptNeeded(int dexoptNeeded) {
689         return dexoptNeeded;
690     }
691 
692     /**
693      * Adjust the given dexopt flags that will be passed to the installer.
694      */
adjustDexoptFlags(int dexoptFlags)695     protected int adjustDexoptFlags(int dexoptFlags) {
696         return dexoptFlags;
697     }
698 
699     /**
700      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
701      */
dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo)702     void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
703             PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo)
704             throws LegacyDexoptDisabledException {
705         final String[] instructionSets = getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(),
706                 pkgSetting.getSecondaryCpuAbi());
707         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
708 
709         final List<String> paths = AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
710 
711         for (String path : paths) {
712             pw.println("path: " + path);
713             pw.increaseIndent();
714 
715             for (String isa : dexCodeInstructionSets) {
716                 try {
717                     DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa);
718                     pw.println(isa + ": [status=" + info.getStatus()
719                             +"] [reason=" + info.getReason() + "]");
720                 } catch (IOException ioe) {
721                     pw.println(isa + ": [Exception]: " + ioe.getMessage());
722                 }
723             }
724 
725             if (useInfo.isUsedByOtherApps(path)) {
726                 pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
727             }
728 
729             Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
730 
731             if (!dexUseInfoMap.isEmpty()) {
732                 pw.println("known secondary dex files:");
733                 pw.increaseIndent();
734                 for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
735                     String dex = e.getKey();
736                     PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
737                     pw.println(dex);
738                     pw.increaseIndent();
739                     // TODO(calin): get the status of the oat file (needs installd call)
740                     pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
741                     if (dexUseInfo.isUsedByOtherApps()) {
742                         pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
743                     }
744                     pw.decreaseIndent();
745                 }
746                 pw.decreaseIndent();
747             }
748             pw.decreaseIndent();
749         }
750     }
751 
752     /**
753      * Returns the compiler filter that should be used to optimize the secondary dex.
754      * The target filter will be updated if the package code is used by other apps
755      * or if it has the safe mode flag set.
756      */
getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps)757     private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
758             boolean isUsedByOtherApps) {
759         if (info.isEmbeddedDexUsed()) {
760             // Downgrade optimizing filters to "verify", but don't upgrade lower filters.
761             return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
762                                                                            : targetCompilerFilter;
763         }
764 
765         // We force vmSafeMode on debuggable apps as well:
766         //  - the runtime ignores their compiled code
767         //  - they generally have lots of methods that could make the compiler used run
768         //    out of memory (b/130828957)
769         // Note that forcing the compiler filter here applies to all compilations (even if they
770         // are done via adb shell commands). That's ok because right now the runtime will ignore
771         // the compiled code anyway. The alternative would have been to update either
772         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
773         // but that would have the downside of possibly producing a big odex files which would
774         // be ignored anyway.
775         boolean vmSafeModeOrDebuggable = ((info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0)
776                 || ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
777 
778         if (vmSafeModeOrDebuggable) {
779             return getSafeModeCompilerFilter(targetCompilerFilter);
780         }
781 
782         if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
783             // If the dex files is used by other apps, apply the shared filter.
784             return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
785                     PackageManagerService.REASON_SHARED);
786         }
787 
788         return targetCompilerFilter;
789     }
790 
791     /**
792      * Returns the compiler filter that should be used to optimize the primary dex.
793      * The target filter will be updated if the package has the safe mode flag set. Note that this
794      * method does NOT take other app use into account. The caller should be responsible for
795      * handling the case where the package code is used by other apps.
796      */
getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter)797     private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) {
798         if (pkg.isUseEmbeddedDex()) {
799             // Downgrade optimizing filters to "verify", but don't upgrade lower filters.
800             return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
801                                                                            : targetCompilerFilter;
802         }
803 
804         // We force vmSafeMode on debuggable apps as well:
805         //  - the runtime ignores their compiled code
806         //  - they generally have lots of methods that could make the compiler used run
807         //    out of memory (b/130828957)
808         // Note that forcing the compiler filter here applies to all compilations (even if they
809         // are done via adb shell commands). That's ok because right now the runtime will ignore
810         // the compiled code anyway. The alternative would have been to update either
811         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
812         // but that would have the downside of possibly producing a big odex files which would
813         // be ignored anyway.
814         boolean vmSafeModeOrDebuggable = pkg.isVmSafeMode() || pkg.isDebuggable();
815 
816         if (vmSafeModeOrDebuggable) {
817             return getSafeModeCompilerFilter(targetCompilerFilter);
818         }
819 
820         return targetCompilerFilter;
821     }
822 
isAppImageEnabled()823     private boolean isAppImageEnabled() {
824         return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
825     }
826 
getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options)827     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
828         return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
829                 info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
830                 info.requestsIsolatedSplitLoading(), compilerFilter, false /* useCloudProfile */,
831                 options);
832     }
833 
getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String compilerFilter, boolean useCloudProfile, DexoptOptions options)834     private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
835             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
836         return getDexFlags(pkg.isDebuggable(),
837                 AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
838                 pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
839                 useCloudProfile, options);
840     }
841 
842     /**
843      * Computes the dex flags that needs to be pass to installd for the given package and compiler
844      * filter.
845      */
getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, String compilerFilter, boolean useCloudProfile, DexoptOptions options)846     private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
847             SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
848             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
849         // Profile guide compiled oat files should not be public unles they are based
850         // on profiles from dex metadata archives.
851         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
852         // the user does not have an existing profile.
853         // The flag useCloudProfile applies only when the cloud profile should be used.
854         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
855         boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()
856                 || useCloudProfile;
857         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
858         // Some apps are executed with restrictions on hidden API usage. If this app is one
859         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
860         // TODO we should pass the actual flag value to dexopt, rather than assuming denylist
861         // TODO(b/135203078): This flag is no longer set as part of AndroidPackage
862         //  and may not be preserved
863         int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED
864                 ? 0
865                 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
866         // Avoid generating CompactDex for modes that are latency critical.
867         final int compilationReason = options.getCompilationReason();
868         boolean generateCompactDex = true;
869         switch (compilationReason) {
870             case PackageManagerService.REASON_FIRST_BOOT:
871             case PackageManagerService.REASON_BOOT_AFTER_OTA:
872             case PackageManagerService.REASON_POST_BOOT:
873             case PackageManagerService.REASON_INSTALL:
874                  generateCompactDex = false;
875         }
876         // Use app images only if it is enabled and we are compiling
877         // profile-guided (so the app image doesn't conservatively contain all classes).
878         // If the app didn't request for the splits to be loaded in isolation or if it does not
879         // declare inter-split dependencies, then all the splits will be loaded in the base
880         // apk class loader (in the order of their definition, otherwise disable app images
881         // because they are unsupported for multiple class loaders. b/7269679
882         boolean generateAppImage = isProfileGuidedFilter && (splitDependencies == null ||
883                 !requestsIsolatedSplitLoading) && isAppImageEnabled();
884         int dexFlags =
885                 (isPublic ? DEXOPT_PUBLIC : 0)
886                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
887                 | profileFlag
888                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
889                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
890                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
891                 | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
892                 | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
893                 | hiddenApiFlag;
894         return adjustDexoptFlags(dexFlags);
895     }
896 
897     /**
898      * Assesses if there's a need to perform dexopt on {@code path} for the given
899      * configuration (isa, compiler filter, profile).
900      */
901     @GuardedBy("mInstallLock")
getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade, int dexoptFlags, String oatDir)902     private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
903             String classLoaderContext, int profileAnalysisResult, boolean downgrade,
904             int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
905         // Allow calls from OtaDexoptService even when ART Service is in use. The installer is
906         // isolated in that case so later calls to it won't call into installd anyway.
907         if (!mInstaller.isIsolated()) {
908             Installer.checkLegacyDexoptDisabled();
909         }
910 
911         final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
912         final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
913         boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
914 
915         if (!newProfile && isProfileGuidedFilter && shouldBePublic
916                 && isOdexPrivate(packageName, path, isa, oatDir)) {
917             // The profile that will be used is a cloud profile, while the profile used previously
918             // is a user profile. Typically, this happens after an app starts being used by other
919             // apps.
920             newProfile = true;
921         }
922 
923         int dexoptNeeded;
924         try {
925             // A profile guided optimizations with an empty profile is essentially 'verify' and
926             // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot
927             // check the profiles because system server does not have access to them.
928             // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and
929             // manually adjust the actual filter before checking.
930             //
931             // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change,
932             // and in the interim we can still improve things here.
933             String actualCompilerFilter = compilerFilter;
934             if (compilerFilterDependsOnProfiles(compilerFilter)
935                     && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) {
936                 actualCompilerFilter = "verify";
937             }
938             dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter,
939                     classLoaderContext, newProfile, downgrade);
940         } catch (IOException ioe) {
941             Slog.w(TAG, "IOException reading apk: " + path, ioe);
942             return DEX_OPT_FAILED;
943         } catch (RuntimeException e) {
944             Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e);
945             return DEX_OPT_FAILED;
946         }
947         return adjustDexoptNeeded(dexoptNeeded);
948     }
949 
950     /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */
compilerFilterDependsOnProfiles(String compilerFilter)951     private boolean compilerFilterDependsOnProfiles(String compilerFilter) {
952         return compilerFilter.endsWith("-profile");
953     }
954 
955     /** Returns true if the current artifacts of the app are private to the app itself. */
956     @GuardedBy("mInstallLock")
isOdexPrivate(String packageName, String path, String isa, String oatDir)957     private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir)
958             throws LegacyDexoptDisabledException {
959         try {
960             return mInstaller.getOdexVisibility(packageName, path, isa, oatDir)
961                     == Installer.ODEX_IS_PRIVATE;
962         } catch (InstallerException e) {
963             Slog.w(TAG, "Failed to get odex visibility for " + path, e);
964             return false;
965         }
966     }
967 
968     /**
969      * Checks if there is an update on the profile information of the {@code pkg}.
970      * If the compiler filter is not profile guided the method returns a safe default:
971      * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA.
972      *
973      * Note that this is a "destructive" operation with side effects. Under the hood the
974      * current profile and the reference profile will be merged and subsequent calls
975      * may return a different result.
976      */
analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter)977     private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
978             String compilerFilter) throws LegacyDexoptDisabledException {
979         Installer.checkLegacyDexoptDisabled();
980 
981         // Check if we are allowed to merge and if the compiler filter is profile guided.
982         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
983             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
984         }
985         // Merge profiles. It returns whether or not there was an updated in the profile info.
986         try {
987             synchronized (mInstallLock) {
988                 return getInstallerLI().mergeProfiles(uid, pkg.getPackageName(), profileName);
989             }
990         } catch (InstallerException e) {
991             Slog.w(TAG, "Failed to merge profiles", e);
992             // We don't need to optimize if we failed to merge.
993             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
994         }
995     }
996 
997     /**
998      * Gets oat dir for the specified package if needed and supported.
999      * In certain cases oat directory
1000      * <strong>cannot</strong> be created:
1001      * <ul>
1002      *      <li>{@code pkg} is a system app, which is not updated.</li>
1003      *      <li>Package location is not a directory, i.e. monolithic install.</li>
1004      * </ul>
1005      *
1006      * @return Absolute path to the oat directory or null, if oat directories
1007      * not needed or unsupported for the package.
1008      */
1009     @Nullable
getPackageOatDirIfSupported(@onNull PackageState packageState, @NonNull AndroidPackage pkg)1010     private String getPackageOatDirIfSupported(@NonNull PackageState packageState,
1011             @NonNull AndroidPackage pkg) {
1012         if (!AndroidPackageUtils.canHaveOatDir(packageState, pkg)) {
1013             return null;
1014         }
1015         File codePath = new File(pkg.getPath());
1016         if (!codePath.isDirectory()) {
1017             return null;
1018         }
1019         return getOatDir(codePath).getAbsolutePath();
1020     }
1021 
1022     /** Returns the oat dir for the given code path */
getOatDir(File codePath)1023     public static File getOatDir(File codePath) {
1024         return new File(codePath, OAT_DIR_NAME);
1025     }
1026 
systemReady()1027     void systemReady() {
1028         mSystemReady = true;
1029     }
1030 
printDexoptFlags(int flags)1031     private String printDexoptFlags(int flags) {
1032         ArrayList<String> flagsList = new ArrayList<>();
1033 
1034         if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) {
1035             flagsList.add("boot_complete");
1036         }
1037         if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) {
1038             flagsList.add("debuggable");
1039         }
1040         if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) {
1041             flagsList.add("profile_guided");
1042         }
1043         if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) {
1044             flagsList.add("public");
1045         }
1046         if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
1047             flagsList.add("secondary");
1048         }
1049         if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
1050             flagsList.add("force");
1051         }
1052         if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
1053             flagsList.add("storage_ce");
1054         }
1055         if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
1056             flagsList.add("storage_de");
1057         }
1058         if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
1059             flagsList.add("idle_background_job");
1060         }
1061         if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
1062             flagsList.add("enable_hidden_api_checks");
1063         }
1064 
1065         return String.join(",", flagsList);
1066     }
1067 
1068     /**
1069      * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a
1070      * dexopt path.
1071      */
1072     public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer {
1073 
ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)1074         public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock,
1075                 Context context, String wakeLockTag) {
1076             super(installer, installLock, context, wakeLockTag);
1077         }
1078 
ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from)1079         public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) {
1080             super(from);
1081         }
1082 
1083         @Override
adjustDexoptNeeded(int dexoptNeeded)1084         protected int adjustDexoptNeeded(int dexoptNeeded) {
1085             if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
1086                 // Ensure compilation by pretending a compiler filter change on the
1087                 // apk/odex location (the reason for the '-'. A positive value means
1088                 // the 'oat' location).
1089                 return -DexFile.DEX2OAT_FOR_FILTER;
1090             }
1091             return dexoptNeeded;
1092         }
1093 
1094         @Override
adjustDexoptFlags(int flags)1095         protected int adjustDexoptFlags(int flags) {
1096             // Add DEXOPT_FORCE flag to signal installd that it should force compilation
1097             // and discard dexoptanalyzer result.
1098             return flags | DEXOPT_FORCE;
1099         }
1100     }
1101 
1102     /**
1103      * Returns {@link #mInstaller} with {@link #mInstallLock}. This should be used for all
1104      * {@link #mInstaller} access unless {@link #getInstallerWithoutLock()} is allowed.
1105      */
1106     @GuardedBy("mInstallLock")
getInstallerLI()1107     private Installer getInstallerLI() {
1108         return mInstaller;
1109     }
1110 
1111     /**
1112      * Returns {@link #mInstaller} without lock. This should be used only inside
1113      * {@link #controlDexOptBlocking(boolean)}.
1114      */
getInstallerWithoutLock()1115     private Installer getInstallerWithoutLock() {
1116         return mInstaller;
1117     }
1118 
1119     /**
1120      * Injector for {@link PackageDexOptimizer} dependencies
1121      */
1122     interface Injector {
getAppHibernationManagerInternal()1123         AppHibernationManagerInternal getAppHibernationManagerInternal();
1124 
getPowerManager(Context context)1125         PowerManager getPowerManager(Context context);
1126     }
1127 }
1128