blob: 1cbe5db352364caeabd34d0b6db933bc9b24d0fe [file] [log] [blame]
Jiakai Zhangc3db1c72022-10-18 10:59:13 +01001/*
2 * Copyright (C) 2022 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
17package com.android.server.art;
18
19import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
20import static com.android.server.art.OutputArtifacts.PermissionSettings;
21import static com.android.server.art.ProfilePath.TmpProfilePath;
22import static com.android.server.art.Utils.Abi;
Jiakai Zhang771f64e2023-01-12 17:32:36 +080023import static com.android.server.art.model.ArtFlags.DexoptFlags;
24import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010025
26import android.R;
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.content.Context;
Jiakai Zhang914197f2022-12-21 10:22:14 +000030import android.content.pm.ApplicationInfo;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010031import android.os.CancellationSignal;
32import android.os.RemoteException;
33import android.os.ServiceSpecificException;
34import android.os.SystemProperties;
35import android.os.UserManager;
Jiakai Zhangde3844b2022-11-30 11:28:11 +000036import android.os.storage.StorageManager;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010037import android.text.TextUtils;
38import android.util.Log;
39import android.util.Pair;
40
41import com.android.internal.annotations.VisibleForTesting;
Jiakai Zhang91fe9512022-11-14 19:42:32 +000042import com.android.server.LocalManagerRegistry;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010043import com.android.server.art.model.ArtFlags;
44import com.android.server.art.model.DetailedDexInfo;
Jiakai Zhang771f64e2023-01-12 17:32:36 +080045import com.android.server.art.model.DexoptParams;
46import com.android.server.art.model.DexoptResult;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010047import com.android.server.pm.pkg.AndroidPackage;
48import com.android.server.pm.pkg.PackageState;
49
50import dalvik.system.DexFile;
51
52import com.google.auto.value.AutoValue;
53
Jiakai Zhangde3844b2022-11-30 11:28:11 +000054import java.io.IOException;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010055import java.util.ArrayList;
56import java.util.List;
57import java.util.Objects;
58
59/** @hide */
60public abstract class DexOptimizer<DexInfoType extends DetailedDexInfo> {
61 private static final String TAG = "DexOptimizer";
62
63 @NonNull protected final Injector mInjector;
64 @NonNull protected final PackageState mPkgState;
65 /** This is always {@code mPkgState.getAndroidPackage()} and guaranteed to be non-null. */
66 @NonNull protected final AndroidPackage mPkg;
Jiakai Zhang771f64e2023-01-12 17:32:36 +080067 @NonNull protected final DexoptParams mParams;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010068 @NonNull protected final CancellationSignal mCancellationSignal;
69
70 protected DexOptimizer(@NonNull Injector injector, @NonNull PackageState pkgState,
Jiakai Zhang771f64e2023-01-12 17:32:36 +080071 @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010072 @NonNull CancellationSignal cancellationSignal) {
73 mInjector = injector;
74 mPkgState = pkgState;
75 mPkg = pkg;
76 mParams = params;
77 mCancellationSignal = cancellationSignal;
78 if (pkgState.getAppId() < 0) {
79 throw new IllegalStateException(
80 "Package '" + pkgState.getPackageName() + "' has invalid app ID");
81 }
82 }
83
84 /**
85 * DO NOT use this method directly. Use {@link
Jiakai Zhang771f64e2023-01-12 17:32:36 +080086 * ArtManagerLocal#dexoptPackage(PackageManagerLocal.FilteredSnapshot, String,
87 * DexoptParams)}.
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010088 */
89 @NonNull
Jiakai Zhang771f64e2023-01-12 17:32:36 +080090 public final List<DexContainerFileDexoptResult> dexopt() throws RemoteException {
91 List<DexContainerFileDexoptResult> results = new ArrayList<>();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010092
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010093 for (DexInfoType dexInfo : getDexInfoList()) {
94 ProfilePath profile = null;
95 boolean succeeded = true;
96 try {
Jiakai Zhang771f64e2023-01-12 17:32:36 +080097 if (!isDexoptable(dexInfo)) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010098 continue;
99 }
100
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100101 String compilerFilter = adjustCompilerFilter(mParams.getCompilerFilter(), dexInfo);
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800102 if (compilerFilter.equals(DexoptParams.COMPILER_FILTER_NOOP)) {
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100103 continue;
104 }
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100105
106 boolean needsToBeShared = needsToBeShared(dexInfo);
107 boolean isOtherReadable = true;
108 // If true, implies that the profile has changed since the last compilation.
109 boolean profileMerged = false;
110 if (DexFile.isProfileGuidedCompilerFilter(compilerFilter)) {
111 if (needsToBeShared) {
112 profile = initReferenceProfile(dexInfo);
113 } else {
114 Pair<ProfilePath, Boolean> pair = getOrInitReferenceProfile(dexInfo);
115 if (pair != null) {
116 profile = pair.first;
117 isOtherReadable = pair.second;
118 }
119 ProfilePath mergedProfile = mergeProfiles(dexInfo, profile);
120 if (mergedProfile != null) {
121 if (profile != null && profile.getTag() == ProfilePath.tmpProfilePath) {
122 mInjector.getArtd().deleteProfile(profile);
123 }
124 profile = mergedProfile;
125 isOtherReadable = false;
126 profileMerged = true;
127 }
128 }
129 if (profile == null) {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800130 // A profile guided dexopt with no profile is essentially 'verify',
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100131 // and dex2oat already makes this transformation. However, we need to
132 // explicitly make this transformation here to guide the later decisions
133 // such as whether the artifacts can be public and whether dexopt is needed.
134 compilerFilter = needsToBeShared
135 ? ReasonMapping.getCompilerFilterForShared()
136 : "verify";
137 }
138 }
139 boolean isProfileGuidedCompilerFilter =
140 DexFile.isProfileGuidedCompilerFilter(compilerFilter);
141 Utils.check(isProfileGuidedCompilerFilter == (profile != null));
142
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100143 boolean canBePublic = (!isProfileGuidedCompilerFilter || isOtherReadable)
144 && isDexFilePublic(dexInfo);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100145 Utils.check(Utils.implies(needsToBeShared, canBePublic));
146 PermissionSettings permissionSettings = getPermissionSettings(dexInfo, canBePublic);
147
Jiakai Zhang2698a492022-11-14 14:36:01 +0000148 DexoptOptions dexoptOptions =
149 getDexoptOptions(dexInfo, isProfileGuidedCompilerFilter);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100150
151 for (Abi abi : getAllAbis(dexInfo)) {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800152 @DexoptResult.DexoptResultStatus int status = DexoptResult.DEXOPT_SKIPPED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100153 long wallTimeMs = 0;
154 long cpuTimeMs = 0;
Jiakai Zhang843ce522022-11-11 14:00:39 +0000155 long sizeBytes = 0;
156 long sizeBeforeBytes = 0;
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000157 boolean isSkippedDueToStorageLow = false;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100158 try {
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000159 var target = DexoptTarget.<DexInfoType>builder()
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100160 .setDexInfo(dexInfo)
161 .setIsa(abi.isa())
162 .setIsInDalvikCache(isInDalvikCache())
163 .setCompilerFilter(compilerFilter)
164 .build();
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000165 var options =
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100166 GetDexoptNeededOptions.builder()
167 .setProfileMerged(profileMerged)
168 .setFlags(mParams.getFlags())
169 .setNeedsToBePublic(needsToBeShared)
170 .build();
171
172 GetDexoptNeededResult getDexoptNeededResult =
173 getDexoptNeeded(target, options);
174
175 if (!getDexoptNeededResult.isDexoptNeeded) {
176 continue;
177 }
178
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000179 try {
180 // `StorageManager.getAllocatableBytes` returns (free space + space used
181 // by clearable cache - low storage threshold). Since we only compare
182 // the result with 0, the clearable cache doesn't make a difference.
183 // When the free space is below the threshold, there should be no
184 // clearable cache left because system cleans up cache every minute.
185 if ((mParams.getFlags() & ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) != 0
186 && mInjector.getStorageManager().getAllocatableBytes(
187 mPkg.getStorageUuid())
188 <= 0) {
189 isSkippedDueToStorageLow = true;
190 continue;
191 }
192 } catch (IOException e) {
193 Log.e(TAG, "Failed to check storage. Assuming storage not low", e);
194 }
195
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100196 IArtdCancellationSignal artdCancellationSignal =
197 mInjector.getArtd().createCancellationSignal();
198 mCancellationSignal.setOnCancelListener(() -> {
199 try {
200 artdCancellationSignal.cancel();
201 } catch (RemoteException e) {
202 Log.e(TAG, "An error occurred when sending a cancellation signal",
203 e);
204 }
205 });
206
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800207 ArtdDexoptResult dexoptResult = dexoptFile(target, profile,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100208 getDexoptNeededResult, permissionSettings,
209 mParams.getPriorityClass(), dexoptOptions, artdCancellationSignal);
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800210 status = dexoptResult.cancelled ? DexoptResult.DEXOPT_CANCELLED
211 : DexoptResult.DEXOPT_PERFORMED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100212 wallTimeMs = dexoptResult.wallTimeMs;
213 cpuTimeMs = dexoptResult.cpuTimeMs;
Jiakai Zhang843ce522022-11-11 14:00:39 +0000214 sizeBytes = dexoptResult.sizeBytes;
215 sizeBeforeBytes = dexoptResult.sizeBeforeBytes;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100216
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800217 if (status == DexoptResult.DEXOPT_CANCELLED) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100218 return results;
219 }
220 } catch (ServiceSpecificException e) {
221 // Log the error and continue.
222 Log.e(TAG,
223 String.format("Failed to dexopt [packageName = %s, dexPath = %s, "
224 + "isa = %s, classLoaderContext = %s]",
225 mPkgState.getPackageName(), dexInfo.dexPath(), abi.isa(),
226 dexInfo.classLoaderContext()),
227 e);
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800228 status = DexoptResult.DEXOPT_FAILED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100229 } finally {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800230 results.add(DexContainerFileDexoptResult.create(dexInfo.dexPath(),
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100231 abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs,
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000232 cpuTimeMs, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow));
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800233 if (status != DexoptResult.DEXOPT_SKIPPED
234 && status != DexoptResult.DEXOPT_PERFORMED) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100235 succeeded = false;
236 }
237 // Make sure artd does not leak even if the caller holds
238 // `mCancellationSignal` forever.
239 mCancellationSignal.setOnCancelListener(null);
240 }
241 }
242
243 if (profile != null && succeeded) {
244 if (profile.getTag() == ProfilePath.tmpProfilePath) {
245 // Commit the profile only if dexopt succeeds.
246 if (commitProfileChanges(profile.getTmpProfilePath())) {
247 profile = null;
248 }
249 }
250 if (profileMerged) {
251 // Note that this is just an optimization, to reduce the amount of data that
252 // the runtime writes on every profile save. The profile merge result on the
253 // next run won't change regardless of whether the cleanup is done or not
254 // because profman only looks at the diff.
255 // A caveat is that it may delete more than what has been merged, if the
256 // runtime writes additional entries between the merge and the cleanup, but
257 // this is fine because the runtime writes all JITed classes and methods on
258 // every save and the additional entries will likely be written back on the
259 // next save.
260 cleanupCurProfiles(dexInfo);
261 }
262 }
263 } finally {
264 if (profile != null && profile.getTag() == ProfilePath.tmpProfilePath) {
265 mInjector.getArtd().deleteProfile(profile);
266 }
267 }
268 }
269
270 return results;
271 }
272
273 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100274 private String adjustCompilerFilter(
275 @NonNull String targetCompilerFilter, @NonNull DexInfoType dexInfo) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100276 if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) {
277 String systemUiCompilerFilter = getSystemUiCompilerFilter();
278 if (!systemUiCompilerFilter.isEmpty()) {
279 return systemUiCompilerFilter;
280 }
281 }
282
283 // We force vmSafeMode on debuggable apps as well:
284 // - the runtime ignores their compiled code
285 // - they generally have lots of methods that could make the compiler used run out of
286 // memory (b/130828957)
287 // Note that forcing the compiler filter here applies to all compilations (even if they
288 // are done via adb shell commands). This is okay because the runtime will ignore the
289 // compiled code anyway.
290 if (mPkg.isVmSafeMode() || mPkg.isDebuggable()) {
291 return DexFile.getSafeModeCompilerFilter(targetCompilerFilter);
292 }
293
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100294 // We cannot do AOT compilation if we don't have a valid class loader context.
Jiakai Zhang4851c472022-11-14 16:32:03 +0000295 if (dexInfo.classLoaderContext() == null) {
296 return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
297 : targetCompilerFilter;
298 }
299
300 // This application wants to use the embedded dex in the APK, rather than extracted or
301 // locally compiled variants, so we only verify it.
302 // "verify" does not prevent dex2oat from extracting the dex code, but in practice, dex2oat
303 // won't extract the dex code because the APK is uncompressed, and the assumption is that
304 // such applications always use uncompressed APKs.
305 if (mPkg.isUseEmbeddedDex()) {
306 return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
307 : targetCompilerFilter;
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100308 }
309
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100310 return targetCompilerFilter;
311 }
312
313 @NonNull
314 private String getSystemUiCompilerFilter() {
315 String compilerFilter = SystemProperties.get("dalvik.vm.systemuicompilerfilter");
316 if (!compilerFilter.isEmpty() && !Utils.isValidArtServiceCompilerFilter(compilerFilter)) {
317 throw new IllegalStateException(
318 "Got invalid compiler filter '" + compilerFilter + "' for System UI");
319 }
320 return compilerFilter;
321 }
322
323 /**
324 * Gets the existing reference profile if exists, or initializes a reference profile from an
325 * external profile.
326 *
327 * @return A pair where the first element is the found or initialized profile, and the second
328 * element is true if the profile is readable by others. Or null if there is no
329 * reference profile or external profile to use.
330 */
331 @Nullable
332 private Pair<ProfilePath, Boolean> getOrInitReferenceProfile(@NonNull DexInfoType dexInfo)
333 throws RemoteException {
334 ProfilePath refProfile = buildRefProfilePath(dexInfo);
335 try {
336 if (mInjector.getArtd().isProfileUsable(refProfile, dexInfo.dexPath())) {
337 boolean isOtherReadable = mInjector.getArtd().getProfileVisibility(refProfile)
338 == FileVisibility.OTHER_READABLE;
339 return Pair.create(refProfile, isOtherReadable);
340 }
341 } catch (ServiceSpecificException e) {
342 Log.e(TAG,
343 "Failed to use the existing reference profile "
344 + AidlUtils.toString(refProfile),
345 e);
346 }
347
348 ProfilePath initializedProfile = initReferenceProfile(dexInfo);
349 return initializedProfile != null ? Pair.create(initializedProfile, true) : null;
350 }
351
352 @NonNull
Jiakai Zhang2698a492022-11-14 14:36:01 +0000353 private DexoptOptions getDexoptOptions(
354 @NonNull DexInfoType dexInfo, boolean isProfileGuidedFilter) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100355 DexoptOptions dexoptOptions = new DexoptOptions();
356 dexoptOptions.compilationReason = mParams.getReason();
357 dexoptOptions.targetSdkVersion = mPkg.getTargetSdkVersion();
358 dexoptOptions.debuggable = mPkg.isDebuggable() || isAlwaysDebuggable();
359 // Generating a meaningful app image needs a profile to determine what to include in the
360 // image. Otherwise, the app image will be nearly empty.
361 dexoptOptions.generateAppImage =
Jiakai Zhang2698a492022-11-14 14:36:01 +0000362 isProfileGuidedFilter && isAppImageAllowed(dexInfo) && isAppImageEnabled();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100363 dexoptOptions.hiddenApiPolicyEnabled = isHiddenApiPolicyEnabled();
364 return dexoptOptions;
365 }
366
367 private boolean isAlwaysDebuggable() {
368 return SystemProperties.getBoolean("dalvik.vm.always_debuggable", false /* def */);
369 }
370
371 private boolean isAppImageEnabled() {
372 return !SystemProperties.get("dalvik.vm.appimageformat").isEmpty();
373 }
374
375 private boolean isHiddenApiPolicyEnabled() {
Jiakai Zhang914197f2022-12-21 10:22:14 +0000376 return mPkgState.getHiddenApiEnforcementPolicy()
377 != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100378 }
379
380 @NonNull
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000381 GetDexoptNeededResult getDexoptNeeded(@NonNull DexoptTarget<DexInfoType> target,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100382 @NonNull GetDexoptNeededOptions options) throws RemoteException {
383 int dexoptTrigger = getDexoptTrigger(target, options);
384
385 // The result should come from artd even if all the bits of `dexoptTrigger` are set
386 // because the result also contains information about the usable VDEX file.
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100387 // Note that the class loader context can be null. In that case, we intentionally pass the
388 // null value down to lower levels to indicate that the class loader context check should be
389 // skipped because we are only going to verify the dex code (see `adjustCompilerFilter`).
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100390 GetDexoptNeededResult result = mInjector.getArtd().getDexoptNeeded(
391 target.dexInfo().dexPath(), target.isa(), target.dexInfo().classLoaderContext(),
392 target.compilerFilter(), dexoptTrigger);
393
394 return result;
395 }
396
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000397 int getDexoptTrigger(@NonNull DexoptTarget<DexInfoType> target,
398 @NonNull GetDexoptNeededOptions options) throws RemoteException {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100399 if ((options.flags() & ArtFlags.FLAG_FORCE) != 0) {
400 return DexoptTrigger.COMPILER_FILTER_IS_BETTER | DexoptTrigger.COMPILER_FILTER_IS_SAME
401 | DexoptTrigger.COMPILER_FILTER_IS_WORSE
402 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE;
403 }
404
405 if ((options.flags() & ArtFlags.FLAG_SHOULD_DOWNGRADE) != 0) {
406 return DexoptTrigger.COMPILER_FILTER_IS_WORSE;
407 }
408
409 int dexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
410 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE;
411 if (options.profileMerged()) {
412 dexoptTrigger |= DexoptTrigger.COMPILER_FILTER_IS_SAME;
413 }
414
415 ArtifactsPath existingArtifactsPath = AidlUtils.buildArtifactsPath(
416 target.dexInfo().dexPath(), target.isa(), target.isInDalvikCache());
417
418 if (options.needsToBePublic()
419 && mInjector.getArtd().getArtifactsVisibility(existingArtifactsPath)
420 == FileVisibility.NOT_OTHER_READABLE) {
421 // Typically, this happens after an app starts being used by other apps.
422 // This case should be the same as force as we have no choice but to trigger a new
423 // dexopt.
424 dexoptTrigger |=
425 DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE;
426 }
427
428 return dexoptTrigger;
429 }
430
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800431 private ArtdDexoptResult dexoptFile(@NonNull DexoptTarget<DexInfoType> target,
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000432 @Nullable ProfilePath profile, @NonNull GetDexoptNeededResult getDexoptNeededResult,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100433 @NonNull PermissionSettings permissionSettings, @PriorityClass int priorityClass,
434 @NonNull DexoptOptions dexoptOptions, IArtdCancellationSignal artdCancellationSignal)
435 throws RemoteException {
436 OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(target.dexInfo().dexPath(),
437 target.isa(), target.isInDalvikCache(), permissionSettings);
438
439 VdexPath inputVdex =
440 getInputVdex(getDexoptNeededResult, target.dexInfo().dexPath(), target.isa());
441
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000442 DexMetadataPath dmFile = getDmFile(target.dexInfo());
443 if (dmFile != null
444 && ReasonMapping.REASONS_FOR_INSTALL.contains(dexoptOptions.compilationReason)) {
445 // If the DM file is passed to dex2oat, then add the "-dm" suffix to the reason (e.g.,
446 // "install-dm").
447 // Note that this only applies to reasons for app install because the goal is to give
448 // Play a signal that a DM file is downloaded at install time. We actually pass the DM
449 // file regardless of the compilation reason, but we don't append a suffix when the
450 // compilation reason is not a reason for app install.
451 // Also note that the "-dm" suffix does NOT imply anything in the DM file being used by
452 // dex2oat. dex2oat may ignore some contents of the DM file when appropriate. The
453 // compilation reason can still be "install-dm" even if dex2oat left all contents of the
454 // DM file unused or an empty DM file is passed to dex2oat.
455 dexoptOptions.compilationReason = dexoptOptions.compilationReason + "-dm";
456 }
457
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100458 return mInjector.getArtd().dexopt(outputArtifacts, target.dexInfo().dexPath(), target.isa(),
459 target.dexInfo().classLoaderContext(), target.compilerFilter(), profile, inputVdex,
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000460 dmFile, priorityClass, dexoptOptions, artdCancellationSignal);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100461 }
462
463 @Nullable
464 private VdexPath getInputVdex(@NonNull GetDexoptNeededResult getDexoptNeededResult,
465 @NonNull String dexPath, @NonNull String isa) {
466 if (!getDexoptNeededResult.isVdexUsable) {
467 return null;
468 }
469 switch (getDexoptNeededResult.artifactsLocation) {
470 case ArtifactsLocation.DALVIK_CACHE:
471 return VdexPath.artifactsPath(
472 AidlUtils.buildArtifactsPath(dexPath, isa, true /* isInDalvikCache */));
473 case ArtifactsLocation.NEXT_TO_DEX:
474 return VdexPath.artifactsPath(
475 AidlUtils.buildArtifactsPath(dexPath, isa, false /* isInDalvikCache */));
476 case ArtifactsLocation.DM:
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000477 // The DM file is passed to dex2oat as a separate flag whenever it exists.
478 return null;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100479 default:
480 // This should never happen as the value is got from artd.
481 throw new IllegalStateException(
482 "Unknown artifacts location " + getDexoptNeededResult.artifactsLocation);
483 }
484 }
485
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000486 @Nullable
487 private DexMetadataPath getDmFile(@NonNull DexInfoType dexInfo) throws RemoteException {
488 DexMetadataPath path = buildDmPath(dexInfo);
489 if (path == null) {
490 return null;
491 }
492 try {
493 if (mInjector.getArtd().getDmFileVisibility(path) != FileVisibility.NOT_FOUND) {
494 return path;
495 }
496 } catch (ServiceSpecificException e) {
497 Log.e(TAG, "Failed to check DM file for " + dexInfo.dexPath(), e);
498 }
499 return null;
500 }
501
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100502 private boolean commitProfileChanges(@NonNull TmpProfilePath profile) throws RemoteException {
503 try {
504 mInjector.getArtd().commitTmpProfile(profile);
505 return true;
506 } catch (ServiceSpecificException e) {
507 Log.e(TAG, "Failed to commit profile changes " + AidlUtils.toString(profile.finalPath),
508 e);
509 return false;
510 }
511 }
512
513 @Nullable
514 private ProfilePath mergeProfiles(@NonNull DexInfoType dexInfo,
515 @Nullable ProfilePath referenceProfile) throws RemoteException {
516 OutputProfile output = buildOutputProfile(dexInfo, false /* isPublic */);
517
518 try {
Jiakai Zhangb1d3db62022-11-11 14:00:44 +0000519 if (mInjector.getArtd().mergeProfiles(getCurProfiles(dexInfo), referenceProfile, output,
520 List.of(dexInfo.dexPath()), new MergeProfileOptions())) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100521 return ProfilePath.tmpProfilePath(output.profilePath);
522 }
523 } catch (ServiceSpecificException e) {
524 Log.e(TAG,
525 "Failed to merge profiles " + AidlUtils.toString(output.profilePath.finalPath),
526 e);
527 }
528
529 return null;
530 }
531
532 private void cleanupCurProfiles(@NonNull DexInfoType dexInfo) throws RemoteException {
533 for (ProfilePath profile : getCurProfiles(dexInfo)) {
534 mInjector.getArtd().deleteProfile(profile);
535 }
536 }
537
538 // Methods to be implemented by child classes.
539
540 /** Returns true if the artifacts should be written to the global dalvik-cache directory. */
541 protected abstract boolean isInDalvikCache();
542
543 /** Returns information about all dex files. */
544 @NonNull protected abstract List<DexInfoType> getDexInfoList();
545
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800546 /** Returns true if the given dex file should be dexopted. */
547 protected abstract boolean isDexoptable(@NonNull DexInfoType dexInfo);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100548
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100549 /**
550 * Returns true if the artifacts should be shared with other apps. Note that this must imply
551 * {@link #isDexFilePublic(DexInfoType)}.
552 */
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100553 protected abstract boolean needsToBeShared(@NonNull DexInfoType dexInfo);
554
555 /**
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100556 * Returns true if the filesystem permission of the dex file has the "read" bit for "others"
557 * (S_IROTH).
558 */
559 protected abstract boolean isDexFilePublic(@NonNull DexInfoType dexInfo);
560
561 /**
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100562 * Returns a reference profile initialized from an external profile (e.g., a DM profile) if
563 * one exists, or null otherwise.
564 */
565 @Nullable
566 protected abstract ProfilePath initReferenceProfile(@NonNull DexInfoType dexInfo)
567 throws RemoteException;
568
569 /** Returns the permission settings to use for the artifacts of the given dex file. */
570 @NonNull
571 protected abstract PermissionSettings getPermissionSettings(
572 @NonNull DexInfoType dexInfo, boolean canBePublic);
573
574 /** Returns all ABIs that the given dex file should be compiled for. */
575 @NonNull protected abstract List<Abi> getAllAbis(@NonNull DexInfoType dexInfo);
576
577 /** Returns the path to the reference profile of the given dex file. */
578 @NonNull protected abstract ProfilePath buildRefProfilePath(@NonNull DexInfoType dexInfo);
579
580 /** Returns true if app image (--app-image-fd) is allowed. */
Jiakai Zhang2698a492022-11-14 14:36:01 +0000581 protected abstract boolean isAppImageAllowed(@NonNull DexInfoType dexInfo);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100582
583 /**
584 * Returns the data structure that represents the temporary profile to use during processing.
585 */
586 @NonNull
587 protected abstract OutputProfile buildOutputProfile(
588 @NonNull DexInfoType dexInfo, boolean isPublic);
589
590 /** Returns the paths to the current profiles of the given dex file. */
591 @NonNull protected abstract List<ProfilePath> getCurProfiles(@NonNull DexInfoType dexInfo);
592
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000593 /**
594 * Returns the path to the DM file that should be passed to dex2oat, or null if no DM file
595 * should be passed.
596 */
597 @Nullable protected abstract DexMetadataPath buildDmPath(@NonNull DexInfoType dexInfo);
598
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100599 @AutoValue
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000600 abstract static class DexoptTarget<DexInfoType extends DetailedDexInfo> {
601 abstract @NonNull DexInfoType dexInfo();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100602 abstract @NonNull String isa();
603 abstract boolean isInDalvikCache();
604 abstract @NonNull String compilerFilter();
605
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000606 static <DexInfoType extends DetailedDexInfo> Builder<DexInfoType> builder() {
607 return new AutoValue_DexOptimizer_DexoptTarget.Builder<DexInfoType>();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100608 }
609
610 @AutoValue.Builder
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000611 abstract static class Builder<DexInfoType extends DetailedDexInfo> {
612 abstract Builder setDexInfo(@NonNull DexInfoType value);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100613 abstract Builder setIsa(@NonNull String value);
614 abstract Builder setIsInDalvikCache(boolean value);
615 abstract Builder setCompilerFilter(@NonNull String value);
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000616 abstract DexoptTarget<DexInfoType> build();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100617 }
618 }
619
620 @AutoValue
621 abstract static class GetDexoptNeededOptions {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800622 abstract @DexoptFlags int flags();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100623 abstract boolean profileMerged();
624 abstract boolean needsToBePublic();
625
626 static Builder builder() {
627 return new AutoValue_DexOptimizer_GetDexoptNeededOptions.Builder();
628 }
629
630 @AutoValue.Builder
631 abstract static class Builder {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800632 abstract Builder setFlags(@DexoptFlags int value);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100633 abstract Builder setProfileMerged(boolean value);
634 abstract Builder setNeedsToBePublic(boolean value);
635 abstract GetDexoptNeededOptions build();
636 }
637 }
638
639 /**
640 * Injector pattern for testing purpose.
641 *
642 * @hide
643 */
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100644 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100645 public static class Injector {
646 @NonNull private final Context mContext;
647
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100648 public Injector(@NonNull Context context) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100649 mContext = context;
Jiakai Zhang20a85422022-12-21 13:58:00 +0000650
651 // Call the getters for various dependencies, to ensure correct initialization order.
652 getUserManager();
653 getDexUseManager();
654 getStorageManager();
655 ArtModuleServiceInitializer.getArtModuleServiceManager();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100656 }
657
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100658 public boolean isSystemUiPackage(@NonNull String packageName) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100659 return packageName.equals(mContext.getString(R.string.config_systemUi));
660 }
661
662 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100663 public UserManager getUserManager() {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100664 return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
665 }
666
667 @NonNull
Jiakai Zhang91fe9512022-11-14 19:42:32 +0000668 public DexUseManagerLocal getDexUseManager() {
669 return Objects.requireNonNull(
670 LocalManagerRegistry.getManager(DexUseManagerLocal.class));
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100671 }
672
673 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100674 public IArtd getArtd() {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100675 return Utils.getArtd();
676 }
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000677
678 @NonNull
679 public StorageManager getStorageManager() {
680 return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
681 }
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100682 }
683}