blob: 6e220c752e571c21358c67a21d5900340f28d2cd [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 */
Jiakai Zhang2777d7a2023-01-13 15:37:13 +080060public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
61 private static final String TAG = "Dexopter";
Jiakai Zhangc3db1c72022-10-18 10:59:13 +010062
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
Jiakai Zhang2777d7a2023-01-13 15:37:13 +080070 protected Dexopter(@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 Zhang2777d7a2023-01-13 15:37:13 +0800160 .setDexInfo(dexInfo)
161 .setIsa(abi.isa())
162 .setIsInDalvikCache(isInDalvikCache())
163 .setCompilerFilter(compilerFilter)
164 .build();
165 var options = GetDexoptNeededOptions.builder()
166 .setProfileMerged(profileMerged)
167 .setFlags(mParams.getFlags())
168 .setNeedsToBePublic(needsToBeShared)
169 .build();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100170
171 GetDexoptNeededResult getDexoptNeededResult =
172 getDexoptNeeded(target, options);
173
174 if (!getDexoptNeededResult.isDexoptNeeded) {
175 continue;
176 }
177
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000178 try {
179 // `StorageManager.getAllocatableBytes` returns (free space + space used
180 // by clearable cache - low storage threshold). Since we only compare
181 // the result with 0, the clearable cache doesn't make a difference.
182 // When the free space is below the threshold, there should be no
183 // clearable cache left because system cleans up cache every minute.
184 if ((mParams.getFlags() & ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) != 0
185 && mInjector.getStorageManager().getAllocatableBytes(
186 mPkg.getStorageUuid())
187 <= 0) {
188 isSkippedDueToStorageLow = true;
189 continue;
190 }
191 } catch (IOException e) {
192 Log.e(TAG, "Failed to check storage. Assuming storage not low", e);
193 }
194
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100195 IArtdCancellationSignal artdCancellationSignal =
196 mInjector.getArtd().createCancellationSignal();
197 mCancellationSignal.setOnCancelListener(() -> {
198 try {
199 artdCancellationSignal.cancel();
200 } catch (RemoteException e) {
201 Log.e(TAG, "An error occurred when sending a cancellation signal",
202 e);
203 }
204 });
205
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800206 ArtdDexoptResult dexoptResult = dexoptFile(target, profile,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100207 getDexoptNeededResult, permissionSettings,
208 mParams.getPriorityClass(), dexoptOptions, artdCancellationSignal);
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800209 status = dexoptResult.cancelled ? DexoptResult.DEXOPT_CANCELLED
210 : DexoptResult.DEXOPT_PERFORMED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100211 wallTimeMs = dexoptResult.wallTimeMs;
212 cpuTimeMs = dexoptResult.cpuTimeMs;
Jiakai Zhang843ce522022-11-11 14:00:39 +0000213 sizeBytes = dexoptResult.sizeBytes;
214 sizeBeforeBytes = dexoptResult.sizeBeforeBytes;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100215
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800216 if (status == DexoptResult.DEXOPT_CANCELLED) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100217 return results;
218 }
219 } catch (ServiceSpecificException e) {
220 // Log the error and continue.
221 Log.e(TAG,
222 String.format("Failed to dexopt [packageName = %s, dexPath = %s, "
223 + "isa = %s, classLoaderContext = %s]",
224 mPkgState.getPackageName(), dexInfo.dexPath(), abi.isa(),
225 dexInfo.classLoaderContext()),
226 e);
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800227 status = DexoptResult.DEXOPT_FAILED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100228 } finally {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800229 results.add(DexContainerFileDexoptResult.create(dexInfo.dexPath(),
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100230 abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs,
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000231 cpuTimeMs, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow));
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800232 if (status != DexoptResult.DEXOPT_SKIPPED
233 && status != DexoptResult.DEXOPT_PERFORMED) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100234 succeeded = false;
235 }
236 // Make sure artd does not leak even if the caller holds
237 // `mCancellationSignal` forever.
238 mCancellationSignal.setOnCancelListener(null);
239 }
240 }
241
242 if (profile != null && succeeded) {
243 if (profile.getTag() == ProfilePath.tmpProfilePath) {
244 // Commit the profile only if dexopt succeeds.
245 if (commitProfileChanges(profile.getTmpProfilePath())) {
246 profile = null;
247 }
248 }
249 if (profileMerged) {
250 // Note that this is just an optimization, to reduce the amount of data that
251 // the runtime writes on every profile save. The profile merge result on the
252 // next run won't change regardless of whether the cleanup is done or not
253 // because profman only looks at the diff.
254 // A caveat is that it may delete more than what has been merged, if the
255 // runtime writes additional entries between the merge and the cleanup, but
256 // this is fine because the runtime writes all JITed classes and methods on
257 // every save and the additional entries will likely be written back on the
258 // next save.
259 cleanupCurProfiles(dexInfo);
260 }
261 }
262 } finally {
263 if (profile != null && profile.getTag() == ProfilePath.tmpProfilePath) {
264 mInjector.getArtd().deleteProfile(profile);
265 }
266 }
267 }
268
269 return results;
270 }
271
272 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100273 private String adjustCompilerFilter(
274 @NonNull String targetCompilerFilter, @NonNull DexInfoType dexInfo) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100275 if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) {
276 String systemUiCompilerFilter = getSystemUiCompilerFilter();
277 if (!systemUiCompilerFilter.isEmpty()) {
278 return systemUiCompilerFilter;
279 }
280 }
281
282 // We force vmSafeMode on debuggable apps as well:
283 // - the runtime ignores their compiled code
284 // - they generally have lots of methods that could make the compiler used run out of
285 // memory (b/130828957)
286 // Note that forcing the compiler filter here applies to all compilations (even if they
287 // are done via adb shell commands). This is okay because the runtime will ignore the
288 // compiled code anyway.
289 if (mPkg.isVmSafeMode() || mPkg.isDebuggable()) {
290 return DexFile.getSafeModeCompilerFilter(targetCompilerFilter);
291 }
292
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100293 // We cannot do AOT compilation if we don't have a valid class loader context.
Jiakai Zhang4851c472022-11-14 16:32:03 +0000294 if (dexInfo.classLoaderContext() == null) {
295 return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
296 : targetCompilerFilter;
297 }
298
299 // This application wants to use the embedded dex in the APK, rather than extracted or
300 // locally compiled variants, so we only verify it.
301 // "verify" does not prevent dex2oat from extracting the dex code, but in practice, dex2oat
302 // won't extract the dex code because the APK is uncompressed, and the assumption is that
303 // such applications always use uncompressed APKs.
304 if (mPkg.isUseEmbeddedDex()) {
305 return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
306 : targetCompilerFilter;
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100307 }
308
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100309 return targetCompilerFilter;
310 }
311
312 @NonNull
313 private String getSystemUiCompilerFilter() {
314 String compilerFilter = SystemProperties.get("dalvik.vm.systemuicompilerfilter");
315 if (!compilerFilter.isEmpty() && !Utils.isValidArtServiceCompilerFilter(compilerFilter)) {
316 throw new IllegalStateException(
317 "Got invalid compiler filter '" + compilerFilter + "' for System UI");
318 }
319 return compilerFilter;
320 }
321
322 /**
323 * Gets the existing reference profile if exists, or initializes a reference profile from an
324 * external profile.
325 *
326 * @return A pair where the first element is the found or initialized profile, and the second
327 * element is true if the profile is readable by others. Or null if there is no
328 * reference profile or external profile to use.
329 */
330 @Nullable
331 private Pair<ProfilePath, Boolean> getOrInitReferenceProfile(@NonNull DexInfoType dexInfo)
332 throws RemoteException {
333 ProfilePath refProfile = buildRefProfilePath(dexInfo);
334 try {
335 if (mInjector.getArtd().isProfileUsable(refProfile, dexInfo.dexPath())) {
336 boolean isOtherReadable = mInjector.getArtd().getProfileVisibility(refProfile)
337 == FileVisibility.OTHER_READABLE;
338 return Pair.create(refProfile, isOtherReadable);
339 }
340 } catch (ServiceSpecificException e) {
341 Log.e(TAG,
342 "Failed to use the existing reference profile "
343 + AidlUtils.toString(refProfile),
344 e);
345 }
346
347 ProfilePath initializedProfile = initReferenceProfile(dexInfo);
348 return initializedProfile != null ? Pair.create(initializedProfile, true) : null;
349 }
350
351 @NonNull
Jiakai Zhang2698a492022-11-14 14:36:01 +0000352 private DexoptOptions getDexoptOptions(
353 @NonNull DexInfoType dexInfo, boolean isProfileGuidedFilter) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100354 DexoptOptions dexoptOptions = new DexoptOptions();
355 dexoptOptions.compilationReason = mParams.getReason();
356 dexoptOptions.targetSdkVersion = mPkg.getTargetSdkVersion();
357 dexoptOptions.debuggable = mPkg.isDebuggable() || isAlwaysDebuggable();
358 // Generating a meaningful app image needs a profile to determine what to include in the
359 // image. Otherwise, the app image will be nearly empty.
360 dexoptOptions.generateAppImage =
Jiakai Zhang2698a492022-11-14 14:36:01 +0000361 isProfileGuidedFilter && isAppImageAllowed(dexInfo) && isAppImageEnabled();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100362 dexoptOptions.hiddenApiPolicyEnabled = isHiddenApiPolicyEnabled();
363 return dexoptOptions;
364 }
365
366 private boolean isAlwaysDebuggable() {
367 return SystemProperties.getBoolean("dalvik.vm.always_debuggable", false /* def */);
368 }
369
370 private boolean isAppImageEnabled() {
371 return !SystemProperties.get("dalvik.vm.appimageformat").isEmpty();
372 }
373
374 private boolean isHiddenApiPolicyEnabled() {
Jiakai Zhang914197f2022-12-21 10:22:14 +0000375 return mPkgState.getHiddenApiEnforcementPolicy()
376 != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100377 }
378
379 @NonNull
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000380 GetDexoptNeededResult getDexoptNeeded(@NonNull DexoptTarget<DexInfoType> target,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100381 @NonNull GetDexoptNeededOptions options) throws RemoteException {
382 int dexoptTrigger = getDexoptTrigger(target, options);
383
384 // The result should come from artd even if all the bits of `dexoptTrigger` are set
385 // because the result also contains information about the usable VDEX file.
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100386 // Note that the class loader context can be null. In that case, we intentionally pass the
387 // null value down to lower levels to indicate that the class loader context check should be
388 // skipped because we are only going to verify the dex code (see `adjustCompilerFilter`).
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100389 GetDexoptNeededResult result = mInjector.getArtd().getDexoptNeeded(
390 target.dexInfo().dexPath(), target.isa(), target.dexInfo().classLoaderContext(),
391 target.compilerFilter(), dexoptTrigger);
392
393 return result;
394 }
395
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000396 int getDexoptTrigger(@NonNull DexoptTarget<DexInfoType> target,
397 @NonNull GetDexoptNeededOptions options) throws RemoteException {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100398 if ((options.flags() & ArtFlags.FLAG_FORCE) != 0) {
399 return DexoptTrigger.COMPILER_FILTER_IS_BETTER | DexoptTrigger.COMPILER_FILTER_IS_SAME
400 | DexoptTrigger.COMPILER_FILTER_IS_WORSE
401 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE;
402 }
403
404 if ((options.flags() & ArtFlags.FLAG_SHOULD_DOWNGRADE) != 0) {
405 return DexoptTrigger.COMPILER_FILTER_IS_WORSE;
406 }
407
408 int dexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
409 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE;
410 if (options.profileMerged()) {
411 dexoptTrigger |= DexoptTrigger.COMPILER_FILTER_IS_SAME;
412 }
413
414 ArtifactsPath existingArtifactsPath = AidlUtils.buildArtifactsPath(
415 target.dexInfo().dexPath(), target.isa(), target.isInDalvikCache());
416
417 if (options.needsToBePublic()
418 && mInjector.getArtd().getArtifactsVisibility(existingArtifactsPath)
419 == FileVisibility.NOT_OTHER_READABLE) {
420 // Typically, this happens after an app starts being used by other apps.
421 // This case should be the same as force as we have no choice but to trigger a new
422 // dexopt.
423 dexoptTrigger |=
424 DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE;
425 }
426
427 return dexoptTrigger;
428 }
429
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800430 private ArtdDexoptResult dexoptFile(@NonNull DexoptTarget<DexInfoType> target,
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000431 @Nullable ProfilePath profile, @NonNull GetDexoptNeededResult getDexoptNeededResult,
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100432 @NonNull PermissionSettings permissionSettings, @PriorityClass int priorityClass,
433 @NonNull DexoptOptions dexoptOptions, IArtdCancellationSignal artdCancellationSignal)
434 throws RemoteException {
435 OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(target.dexInfo().dexPath(),
436 target.isa(), target.isInDalvikCache(), permissionSettings);
437
438 VdexPath inputVdex =
439 getInputVdex(getDexoptNeededResult, target.dexInfo().dexPath(), target.isa());
440
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000441 DexMetadataPath dmFile = getDmFile(target.dexInfo());
442 if (dmFile != null
443 && ReasonMapping.REASONS_FOR_INSTALL.contains(dexoptOptions.compilationReason)) {
444 // If the DM file is passed to dex2oat, then add the "-dm" suffix to the reason (e.g.,
445 // "install-dm").
446 // Note that this only applies to reasons for app install because the goal is to give
447 // Play a signal that a DM file is downloaded at install time. We actually pass the DM
448 // file regardless of the compilation reason, but we don't append a suffix when the
449 // compilation reason is not a reason for app install.
450 // Also note that the "-dm" suffix does NOT imply anything in the DM file being used by
451 // dex2oat. dex2oat may ignore some contents of the DM file when appropriate. The
452 // compilation reason can still be "install-dm" even if dex2oat left all contents of the
453 // DM file unused or an empty DM file is passed to dex2oat.
454 dexoptOptions.compilationReason = dexoptOptions.compilationReason + "-dm";
455 }
456
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100457 return mInjector.getArtd().dexopt(outputArtifacts, target.dexInfo().dexPath(), target.isa(),
458 target.dexInfo().classLoaderContext(), target.compilerFilter(), profile, inputVdex,
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000459 dmFile, priorityClass, dexoptOptions, artdCancellationSignal);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100460 }
461
462 @Nullable
463 private VdexPath getInputVdex(@NonNull GetDexoptNeededResult getDexoptNeededResult,
464 @NonNull String dexPath, @NonNull String isa) {
465 if (!getDexoptNeededResult.isVdexUsable) {
466 return null;
467 }
468 switch (getDexoptNeededResult.artifactsLocation) {
469 case ArtifactsLocation.DALVIK_CACHE:
470 return VdexPath.artifactsPath(
471 AidlUtils.buildArtifactsPath(dexPath, isa, true /* isInDalvikCache */));
472 case ArtifactsLocation.NEXT_TO_DEX:
473 return VdexPath.artifactsPath(
474 AidlUtils.buildArtifactsPath(dexPath, isa, false /* isInDalvikCache */));
475 case ArtifactsLocation.DM:
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000476 // The DM file is passed to dex2oat as a separate flag whenever it exists.
477 return null;
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100478 default:
479 // This should never happen as the value is got from artd.
480 throw new IllegalStateException(
481 "Unknown artifacts location " + getDexoptNeededResult.artifactsLocation);
482 }
483 }
484
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000485 @Nullable
486 private DexMetadataPath getDmFile(@NonNull DexInfoType dexInfo) throws RemoteException {
487 DexMetadataPath path = buildDmPath(dexInfo);
488 if (path == null) {
489 return null;
490 }
491 try {
492 if (mInjector.getArtd().getDmFileVisibility(path) != FileVisibility.NOT_FOUND) {
493 return path;
494 }
495 } catch (ServiceSpecificException e) {
496 Log.e(TAG, "Failed to check DM file for " + dexInfo.dexPath(), e);
497 }
498 return null;
499 }
500
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100501 private boolean commitProfileChanges(@NonNull TmpProfilePath profile) throws RemoteException {
502 try {
503 mInjector.getArtd().commitTmpProfile(profile);
504 return true;
505 } catch (ServiceSpecificException e) {
506 Log.e(TAG, "Failed to commit profile changes " + AidlUtils.toString(profile.finalPath),
507 e);
508 return false;
509 }
510 }
511
512 @Nullable
513 private ProfilePath mergeProfiles(@NonNull DexInfoType dexInfo,
514 @Nullable ProfilePath referenceProfile) throws RemoteException {
515 OutputProfile output = buildOutputProfile(dexInfo, false /* isPublic */);
516
517 try {
Jiakai Zhangb1d3db62022-11-11 14:00:44 +0000518 if (mInjector.getArtd().mergeProfiles(getCurProfiles(dexInfo), referenceProfile, output,
519 List.of(dexInfo.dexPath()), new MergeProfileOptions())) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100520 return ProfilePath.tmpProfilePath(output.profilePath);
521 }
522 } catch (ServiceSpecificException e) {
523 Log.e(TAG,
524 "Failed to merge profiles " + AidlUtils.toString(output.profilePath.finalPath),
525 e);
526 }
527
528 return null;
529 }
530
531 private void cleanupCurProfiles(@NonNull DexInfoType dexInfo) throws RemoteException {
532 for (ProfilePath profile : getCurProfiles(dexInfo)) {
533 mInjector.getArtd().deleteProfile(profile);
534 }
535 }
536
537 // Methods to be implemented by child classes.
538
539 /** Returns true if the artifacts should be written to the global dalvik-cache directory. */
540 protected abstract boolean isInDalvikCache();
541
542 /** Returns information about all dex files. */
543 @NonNull protected abstract List<DexInfoType> getDexInfoList();
544
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800545 /** Returns true if the given dex file should be dexopted. */
546 protected abstract boolean isDexoptable(@NonNull DexInfoType dexInfo);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100547
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100548 /**
549 * Returns true if the artifacts should be shared with other apps. Note that this must imply
550 * {@link #isDexFilePublic(DexInfoType)}.
551 */
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100552 protected abstract boolean needsToBeShared(@NonNull DexInfoType dexInfo);
553
554 /**
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100555 * Returns true if the filesystem permission of the dex file has the "read" bit for "others"
556 * (S_IROTH).
557 */
558 protected abstract boolean isDexFilePublic(@NonNull DexInfoType dexInfo);
559
560 /**
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100561 * Returns a reference profile initialized from an external profile (e.g., a DM profile) if
562 * one exists, or null otherwise.
563 */
564 @Nullable
565 protected abstract ProfilePath initReferenceProfile(@NonNull DexInfoType dexInfo)
566 throws RemoteException;
567
568 /** Returns the permission settings to use for the artifacts of the given dex file. */
569 @NonNull
570 protected abstract PermissionSettings getPermissionSettings(
571 @NonNull DexInfoType dexInfo, boolean canBePublic);
572
573 /** Returns all ABIs that the given dex file should be compiled for. */
574 @NonNull protected abstract List<Abi> getAllAbis(@NonNull DexInfoType dexInfo);
575
576 /** Returns the path to the reference profile of the given dex file. */
577 @NonNull protected abstract ProfilePath buildRefProfilePath(@NonNull DexInfoType dexInfo);
578
579 /** Returns true if app image (--app-image-fd) is allowed. */
Jiakai Zhang2698a492022-11-14 14:36:01 +0000580 protected abstract boolean isAppImageAllowed(@NonNull DexInfoType dexInfo);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100581
582 /**
583 * Returns the data structure that represents the temporary profile to use during processing.
584 */
585 @NonNull
586 protected abstract OutputProfile buildOutputProfile(
587 @NonNull DexInfoType dexInfo, boolean isPublic);
588
589 /** Returns the paths to the current profiles of the given dex file. */
590 @NonNull protected abstract List<ProfilePath> getCurProfiles(@NonNull DexInfoType dexInfo);
591
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000592 /**
593 * Returns the path to the DM file that should be passed to dex2oat, or null if no DM file
594 * should be passed.
595 */
596 @Nullable protected abstract DexMetadataPath buildDmPath(@NonNull DexInfoType dexInfo);
597
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100598 @AutoValue
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000599 abstract static class DexoptTarget<DexInfoType extends DetailedDexInfo> {
600 abstract @NonNull DexInfoType dexInfo();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100601 abstract @NonNull String isa();
602 abstract boolean isInDalvikCache();
603 abstract @NonNull String compilerFilter();
604
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000605 static <DexInfoType extends DetailedDexInfo> Builder<DexInfoType> builder() {
Jiakai Zhang2777d7a2023-01-13 15:37:13 +0800606 return new AutoValue_Dexopter_DexoptTarget.Builder<DexInfoType>();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100607 }
608
609 @AutoValue.Builder
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000610 abstract static class Builder<DexInfoType extends DetailedDexInfo> {
611 abstract Builder setDexInfo(@NonNull DexInfoType value);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100612 abstract Builder setIsa(@NonNull String value);
613 abstract Builder setIsInDalvikCache(boolean value);
614 abstract Builder setCompilerFilter(@NonNull String value);
Jiakai Zhangbfd155d2022-11-08 13:17:50 +0000615 abstract DexoptTarget<DexInfoType> build();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100616 }
617 }
618
619 @AutoValue
620 abstract static class GetDexoptNeededOptions {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800621 abstract @DexoptFlags int flags();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100622 abstract boolean profileMerged();
623 abstract boolean needsToBePublic();
624
625 static Builder builder() {
Jiakai Zhang2777d7a2023-01-13 15:37:13 +0800626 return new AutoValue_Dexopter_GetDexoptNeededOptions.Builder();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100627 }
628
629 @AutoValue.Builder
630 abstract static class Builder {
Jiakai Zhang771f64e2023-01-12 17:32:36 +0800631 abstract Builder setFlags(@DexoptFlags int value);
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100632 abstract Builder setProfileMerged(boolean value);
633 abstract Builder setNeedsToBePublic(boolean value);
634 abstract GetDexoptNeededOptions build();
635 }
636 }
637
638 /**
639 * Injector pattern for testing purpose.
640 *
641 * @hide
642 */
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100643 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100644 public static class Injector {
645 @NonNull private final Context mContext;
646
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100647 public Injector(@NonNull Context context) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100648 mContext = context;
Jiakai Zhang20a85422022-12-21 13:58:00 +0000649
650 // Call the getters for various dependencies, to ensure correct initialization order.
651 getUserManager();
652 getDexUseManager();
653 getStorageManager();
654 ArtModuleServiceInitializer.getArtModuleServiceManager();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100655 }
656
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100657 public boolean isSystemUiPackage(@NonNull String packageName) {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100658 return packageName.equals(mContext.getString(R.string.config_systemUi));
659 }
660
661 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100662 public UserManager getUserManager() {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100663 return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
664 }
665
666 @NonNull
Jiakai Zhang91fe9512022-11-14 19:42:32 +0000667 public DexUseManagerLocal getDexUseManager() {
668 return Objects.requireNonNull(
669 LocalManagerRegistry.getManager(DexUseManagerLocal.class));
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100670 }
671
672 @NonNull
Jiakai Zhang12f45c42022-10-27 15:23:03 +0100673 public IArtd getArtd() {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100674 return Utils.getArtd();
675 }
Jiakai Zhangde3844b2022-11-30 11:28:11 +0000676
677 @NonNull
678 public StorageManager getStorageManager() {
679 return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
680 }
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100681 }
682}