• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.art;
18 
19 import static android.app.ActivityManager.RunningAppProcessInfo;
20 
21 import static com.android.server.art.ProfilePath.TmpProfilePath;
22 
23 import android.R;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.ActivityManager;
27 import android.app.role.RoleManager;
28 import android.apphibernation.AppHibernationManager;
29 import android.content.Context;
30 import android.os.Binder;
31 import android.os.Build;
32 import android.os.DeadObjectException;
33 import android.os.Process;
34 import android.os.RemoteException;
35 import android.os.ServiceSpecificException;
36 import android.os.SystemClock;
37 import android.os.SystemProperties;
38 import android.os.Trace;
39 import android.os.UserHandle;
40 import android.os.UserManager;
41 import android.text.TextUtils;
42 import android.util.Log;
43 import android.util.Pair;
44 import android.util.SparseArray;
45 
46 import androidx.annotation.RequiresApi;
47 
48 import com.android.modules.utils.build.SdkLevel;
49 import com.android.modules.utils.pm.PackageStateModulesUtils;
50 import com.android.server.art.model.DexoptParams;
51 import com.android.server.pm.PackageManagerLocal;
52 import com.android.server.pm.pkg.AndroidPackage;
53 import com.android.server.pm.pkg.PackageState;
54 
55 import dalvik.system.DexFile;
56 import dalvik.system.VMRuntime;
57 
58 import com.google.auto.value.AutoValue;
59 
60 import java.io.File;
61 import java.io.IOException;
62 import java.nio.file.Files;
63 import java.nio.file.Path;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.Comparator;
69 import java.util.List;
70 import java.util.Objects;
71 import java.util.Set;
72 import java.util.concurrent.CompletableFuture;
73 import java.util.concurrent.ExecutionException;
74 import java.util.concurrent.Executor;
75 import java.util.concurrent.Future;
76 import java.util.function.Supplier;
77 
78 /** @hide */
79 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
80 public final class Utils {
81     public static final String PLATFORM_PACKAGE_NAME = "android";
82 
83     /** A copy of {@link android.os.Trace.TRACE_TAG_DALVIK}. */
84     private static final long TRACE_TAG_DALVIK = 1L << 14;
85 
Utils()86     private Utils() {}
87 
88     /**
89      * Checks if given array is null or has zero elements.
90      */
isEmpty(@ullable Collection<T> array)91     public static <T> boolean isEmpty(@Nullable Collection<T> array) {
92         return array == null || array.isEmpty();
93     }
94 
95     /**
96      * Checks if given array is null or has zero elements.
97      */
isEmpty(@ullable SparseArray<T> array)98     public static <T> boolean isEmpty(@Nullable SparseArray<T> array) {
99         return array == null || array.size() == 0;
100     }
101 
102     /**
103      * Checks if given array is null or has zero elements.
104      */
isEmpty(@ullable int[] array)105     public static boolean isEmpty(@Nullable int[] array) {
106         return array == null || array.length == 0;
107     }
108 
109     /** Returns the ABI information for the package. The primary ABI comes first. */
110     @NonNull
getAllAbis(@onNull PackageState pkgState)111     public static List<Abi> getAllAbis(@NonNull PackageState pkgState) {
112         List<Abi> abis = new ArrayList<>();
113         abis.add(getPrimaryAbi(pkgState));
114         String pkgPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
115         String pkgSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
116         if (pkgSecondaryCpuAbi != null) {
117             Utils.check(pkgState.getPrimaryCpuAbi() != null);
118             String isa = getTranslatedIsa(VMRuntime.getInstructionSet(pkgSecondaryCpuAbi));
119             if (isa != null) {
120                 abis.add(Abi.create(nativeIsaToAbi(isa), isa, false /* isPrimaryAbi */));
121             }
122         }
123         // Primary and secondary ABIs should be guaranteed to have different ISAs.
124         if (abis.size() == 2 && abis.get(0).isa().equals(abis.get(1).isa())) {
125             throw new IllegalStateException(String.format(
126                     "Duplicate ISA: primary ABI '%s' ('%s'), secondary ABI '%s' ('%s')",
127                     pkgPrimaryCpuAbi, abis.get(0).name(), pkgSecondaryCpuAbi, abis.get(1).name()));
128         }
129         return abis;
130     }
131 
132     /**
133      * Returns the ABI information for the ABIs with the given names. The primary ABI comes first,
134      * if given.
135      */
136     @NonNull
getAllAbisForNames( @onNull Set<String> abiNames, @NonNull PackageState pkgState)137     public static List<Abi> getAllAbisForNames(
138             @NonNull Set<String> abiNames, @NonNull PackageState pkgState) {
139         Utils.check(abiNames.stream().allMatch(Utils::isNativeAbi));
140         Abi pkgPrimaryAbi = getPrimaryAbi(pkgState);
141         return abiNames.stream()
142                 .map(name
143                         -> Abi.create(name, VMRuntime.getInstructionSet(name),
144                                 name.equals(pkgPrimaryAbi.name())))
145                 .sorted(Comparator.comparing(Abi::isPrimaryAbi).reversed())
146                 .toList();
147     }
148 
149     @NonNull
getPrimaryAbi(@onNull PackageState pkgState)150     public static Abi getPrimaryAbi(@NonNull PackageState pkgState) {
151         String primaryCpuAbi = pkgState.getPrimaryCpuAbi();
152         if (primaryCpuAbi != null) {
153             String isa = getTranslatedIsa(VMRuntime.getInstructionSet(primaryCpuAbi));
154             // Fall through if there is no native bridge support.
155             if (isa != null) {
156                 return Abi.create(nativeIsaToAbi(isa), isa, true /* isPrimaryAbi */);
157             }
158         }
159         // This is the most common case. Either the package manager can't infer the ABIs, probably
160         // because the package doesn't contain any native library, or the primary ABI is a foreign
161         // one and there is no native bridge support. The app is launched with the device's
162         // preferred ABI.
163         String preferredAbi = Constants.getPreferredAbi();
164         Utils.check(isNativeAbi(preferredAbi));
165         return Abi.create(
166                 preferredAbi, VMRuntime.getInstructionSet(preferredAbi), true /* isPrimaryAbi */);
167     }
168 
169     /**
170      * If the given ISA isn't native to the device, returns the ISA that the native bridge
171      * translates it to, or null if there is no native bridge support. Otherwise, returns the ISA as
172      * is. This is the ISA that the app is actually launched with and therefore the ISA that should
173      * be used to compile the app.
174      */
175     @Nullable
getTranslatedIsa(@onNull String isa)176     private static String getTranslatedIsa(@NonNull String isa) {
177         String abi64 = Constants.getNative64BitAbi();
178         String abi32 = Constants.getNative32BitAbi();
179         if ((abi64 != null && isa.equals(VMRuntime.getInstructionSet(abi64)))
180                 || (abi32 != null && isa.equals(VMRuntime.getInstructionSet(abi32)))) {
181             return isa;
182         }
183         String translatedIsa = SystemProperties.get("ro.dalvik.vm.isa." + isa);
184         if (TextUtils.isEmpty(translatedIsa)) {
185             return null;
186         }
187         return translatedIsa;
188     }
189 
190     @NonNull
nativeIsaToAbi(@onNull String isa)191     private static String nativeIsaToAbi(@NonNull String isa) {
192         String abi64 = Constants.getNative64BitAbi();
193         if (abi64 != null && isa.equals(VMRuntime.getInstructionSet(abi64))) {
194             return abi64;
195         }
196         String abi32 = Constants.getNative32BitAbi();
197         if (abi32 != null && isa.equals(VMRuntime.getInstructionSet(abi32))) {
198             return abi32;
199         }
200         throw new IllegalStateException(String.format("Non-native isa '%s'", isa));
201     }
202 
isNativeAbi(@onNull String abiName)203     public static boolean isNativeAbi(@NonNull String abiName) {
204         return abiName.equals(Constants.getNative64BitAbi())
205                 || abiName.equals(Constants.getNative32BitAbi());
206     }
207 
getNativeIsas()208     public static List<String> getNativeIsas() {
209         return Arrays.asList(Constants.getNative64BitAbi(), Constants.getNative32BitAbi())
210                 .stream()
211                 .filter(Objects::nonNull)
212                 .map(VMRuntime::getInstructionSet)
213                 .toList();
214     }
215 
216     /**
217      * Returns whether the artifacts of the primary dex files should be in the global dalvik-cache
218      * directory.
219      *
220      * This method is not needed for secondary dex files because they are always in writable
221      * locations.
222      */
223     @NonNull
isInDalvikCache(@onNull PackageState pkgState, @NonNull IArtd artd)224     public static boolean isInDalvikCache(@NonNull PackageState pkgState, @NonNull IArtd artd)
225             throws RemoteException {
226         try {
227             return artd.isInDalvikCache(pkgState.getAndroidPackage().getSplits().get(0).getPath());
228         } catch (ServiceSpecificException e) {
229             // This should never happen. Ignore the error and conservatively use dalvik-cache to
230             // minimize the risk.
231             // TODO(jiakaiz): Throw the error instead of ignoring it.
232             AsLog.e("Failed to determine the location of the artifacts", e);
233             return true;
234         }
235     }
236 
237     /** Returns true if the given string is a valid compiler filter. */
isValidArtServiceCompilerFilter(@onNull String compilerFilter)238     public static boolean isValidArtServiceCompilerFilter(@NonNull String compilerFilter) {
239         if (compilerFilter.equals(DexoptParams.COMPILER_FILTER_NOOP)) {
240             return true;
241         }
242         return DexFile.isValidCompilerFilter(compilerFilter);
243     }
244 
implies(boolean cond1, boolean cond2)245     public static boolean implies(boolean cond1, boolean cond2) {
246         return cond1 ? cond2 : true;
247     }
248 
check(boolean cond)249     public static void check(boolean cond) {
250         // This cannot be replaced with `assert` because `assert` is not enabled in Android.
251         if (!cond) {
252             throw new IllegalStateException("Check failed");
253         }
254     }
255 
256     @NonNull
getPackageStateOrThrow( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)257     public static PackageState getPackageStateOrThrow(
258             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
259         PackageState pkgState = snapshot.getPackageState(packageName);
260         if (pkgState == null) {
261             throw new IllegalArgumentException("Package not found: " + packageName);
262         }
263         return pkgState;
264     }
265 
266     @NonNull
getPackageOrThrow(@onNull PackageState pkgState)267     public static AndroidPackage getPackageOrThrow(@NonNull PackageState pkgState) {
268         AndroidPackage pkg = pkgState.getAndroidPackage();
269         if (pkg == null) {
270             throw new IllegalArgumentException(
271                     "Unable to get package " + pkgState.getPackageName());
272         }
273         return pkg;
274     }
275 
276     @NonNull
assertNonEmpty(@ullable String str)277     public static String assertNonEmpty(@Nullable String str) {
278         if (TextUtils.isEmpty(str)) {
279             throw new IllegalArgumentException();
280         }
281         return str;
282     }
283 
executeAndWait(@onNull Executor executor, @NonNull Runnable runnable)284     public static void executeAndWait(@NonNull Executor executor, @NonNull Runnable runnable) {
285         getFuture(CompletableFuture.runAsync(runnable, executor));
286     }
287 
executeAndWait(@onNull Executor executor, @NonNull Supplier<T> supplier)288     public static <T> T executeAndWait(@NonNull Executor executor, @NonNull Supplier<T> supplier) {
289         return getFuture(CompletableFuture.supplyAsync(supplier, executor));
290     }
291 
getFuture(Future<T> future)292     public static <T> T getFuture(Future<T> future) {
293         try {
294             return future.get();
295         } catch (ExecutionException e) {
296             throw toRuntimeException(e.getCause());
297         } catch (InterruptedException e) {
298             throw new RuntimeException(e);
299         }
300     }
301 
302     @NonNull
toRuntimeException(@onNull Throwable t)303     public static RuntimeException toRuntimeException(@NonNull Throwable t) {
304         if (t instanceof RuntimeException r) {
305             return r;
306         }
307         var r = new RuntimeException(t);
308         // Clear the unhelpful stack trace, which is the stack trace of the constructor call above,
309         // so that the user can focus on the stack trace of `t`.
310         r.setStackTrace(new StackTraceElement[0]);
311         return r;
312     }
313 
314     /**
315      * Returns true if the given package is dexoptable.
316      *
317      * @param appHibernationManager the {@link AppHibernationManager} instance for checking
318      *         hibernation status, or null to skip the check
319      */
canDexoptPackage( @onNull PackageState pkgState, @Nullable AppHibernationManager appHibernationManager)320     public static boolean canDexoptPackage(
321             @NonNull PackageState pkgState, @Nullable AppHibernationManager appHibernationManager) {
322         if (!PackageStateModulesUtils.isDexoptable(pkgState)) {
323             return false;
324         }
325 
326         // We do not dexopt unused packages.
327         // If `appHibernationManager` is null, the caller's intention is to skip the check.
328         if (appHibernationManager != null
329                 && shouldSkipDexoptDueToHibernation(pkgState, appHibernationManager)) {
330             return false;
331         }
332 
333         return true;
334     }
335 
shouldSkipDexoptDueToHibernation( @onNull PackageState pkgState, @NonNull AppHibernationManager appHibernationManager)336     public static boolean shouldSkipDexoptDueToHibernation(
337             @NonNull PackageState pkgState, @NonNull AppHibernationManager appHibernationManager) {
338         return appHibernationManager.isHibernatingGlobally(pkgState.getPackageName())
339                 && appHibernationManager.isOatArtifactDeletionEnabled();
340     }
341 
getPackageLastActiveTime(@onNull PackageState pkgState, @NonNull DexUseManagerLocal dexUseManager, @NonNull UserManager userManager)342     public static long getPackageLastActiveTime(@NonNull PackageState pkgState,
343             @NonNull DexUseManagerLocal dexUseManager, @NonNull UserManager userManager) {
344         long lastUsedAtMs = dexUseManager.getPackageLastUsedAtMs(pkgState.getPackageName());
345         // The time where the last user installed the package the first time.
346         long lastFirstInstallTimeMs =
347                 userManager.getUserHandles(true /* excludeDying */)
348                         .stream()
349                         .map(handle -> pkgState.getStateForUser(handle))
350                         .map(userState -> userState.getFirstInstallTimeMillis())
351                         .max(Long::compare)
352                         .orElse(0l);
353         return Math.max(lastUsedAtMs, lastFirstInstallTimeMs);
354     }
355 
deleteIfExistsSafe(@ullable File file)356     public static void deleteIfExistsSafe(@Nullable File file) {
357         if (file != null) {
358             deleteIfExistsSafe(file.toPath());
359         }
360     }
361 
deleteIfExistsSafe(@onNull Path path)362     public static void deleteIfExistsSafe(@NonNull Path path) {
363         try {
364             Files.deleteIfExists(path);
365         } catch (IOException e) {
366             AsLog.e("Failed to delete file '" + path + "'", e);
367         }
368     }
369 
isSystemUiPackage(@onNull Context context, @NonNull String packageName)370     public static boolean isSystemUiPackage(@NonNull Context context, @NonNull String packageName) {
371         return packageName.equals(context.getString(R.string.config_systemUi));
372     }
373 
isLauncherPackage(@onNull Context context, @NonNull String packageName)374     public static boolean isLauncherPackage(@NonNull Context context, @NonNull String packageName) {
375         RoleManager roleManager = context.getSystemService(RoleManager.class);
376         return roleManager.getRoleHolders(RoleManager.ROLE_HOME).contains(packageName);
377     }
378 
379     /**
380      * Gets the existing reference profile if one exists, or initializes a reference profile from an
381      * external profile.
382      *
383      * If the reference profile is initialized from an external profile, the returned profile path
384      * will be a {@link TmpProfilePath}. It's the callers responsibility to either commit it to the
385      * final location by calling {@link IArtd#commitTmpProfile} or clean it up by calling {@link
386      * IArtd#deleteProfile}.
387      *
388      * Note: "External profile" means profiles that are external to the device, as opposed to local
389      * profiles, which are collected on the device. An embedded profile (a profile embedded in the
390      * dex file) is also an external profile.
391      *
392      * @param dexPath the path to the dex file that the profile is checked against
393      * @param refProfile the path where an existing reference profile would be found, if present
394      * @param externalProfiles a list of external profiles to initialize the reference profile from,
395      *         in the order of preference
396      * @param enableEmbeddedProfile whether to allow initializing the reference profile from the
397      *         embedded profile
398      * @param initOutput the final location to initialize the reference profile to
399      */
400     @NonNull
getOrInitReferenceProfile(@onNull IArtd artd, @NonNull String dexPath, @NonNull ProfilePath refProfile, @NonNull List<ProfilePath> externalProfiles, boolean enableEmbeddedProfile, @NonNull OutputProfile initOutput)401     public static InitProfileResult getOrInitReferenceProfile(@NonNull IArtd artd,
402             @NonNull String dexPath, @NonNull ProfilePath refProfile,
403             @NonNull List<ProfilePath> externalProfiles, boolean enableEmbeddedProfile,
404             @NonNull OutputProfile initOutput) throws RemoteException {
405         try {
406             if (artd.isProfileUsable(refProfile, dexPath)) {
407                 boolean isOtherReadable =
408                         artd.getProfileVisibility(refProfile) == FileVisibility.OTHER_READABLE;
409                 return InitProfileResult.create(
410                         refProfile, isOtherReadable, List.of() /* externalProfileErrors */);
411             }
412         } catch (ServiceSpecificException e) {
413             AsLog.e("Failed to use the existing reference profile "
414                             + AidlUtils.toString(refProfile),
415                     e);
416         }
417 
418         return initReferenceProfile(
419                 artd, dexPath, externalProfiles, enableEmbeddedProfile, initOutput);
420     }
421 
422     /**
423      * Similar to above, but never uses an existing profile.
424      *
425      * The {@link InitProfileResult#isOtherReadable} field is always set to true. The profile
426      * returned by this method is initialized from an external profile, meaning it has no user data,
427      * so it's always readable by others.
428      */
429     @Nullable
initReferenceProfile(@onNull IArtd artd, @NonNull String dexPath, @NonNull List<ProfilePath> externalProfiles, boolean enableEmbeddedProfile, @NonNull OutputProfile output)430     public static InitProfileResult initReferenceProfile(@NonNull IArtd artd,
431             @NonNull String dexPath, @NonNull List<ProfilePath> externalProfiles,
432             boolean enableEmbeddedProfile, @NonNull OutputProfile output) throws RemoteException {
433         // Each element is a pair of a profile name (for logging) and the corresponding initializer.
434         // The order matters. Non-embedded profiles should take precedence.
435         List<Pair<String, ProfileInitializer>> profileInitializers = new ArrayList<>();
436         for (ProfilePath profile : externalProfiles) {
437             // If the profile path is a PrebuiltProfilePath, and the APK is really a prebuilt
438             // one, rewriting the profile is unnecessary because the dex location is known at
439             // build time and is correctly set in the profile header. However, the APK can also
440             // be an installed one, in which case partners may place a profile file next to the
441             // APK at install time. Rewriting the profile in the latter case is necessary.
442             profileInitializers.add(Pair.create(AidlUtils.toString(profile),
443                     () -> artd.copyAndRewriteProfile(profile, output, dexPath)));
444         }
445         if (enableEmbeddedProfile && SdkLevel.isAtLeastV()) {
446             profileInitializers.add(Pair.create(
447                     "embedded profile", () -> artd.copyAndRewriteEmbeddedProfile(output, dexPath)));
448         }
449 
450         List<String> externalProfileErrors = new ArrayList<>();
451         for (var pair : profileInitializers) {
452             try {
453                 CopyAndRewriteProfileResult result = pair.second.get();
454                 if (result.status == CopyAndRewriteProfileResult.Status.SUCCESS) {
455                     return InitProfileResult.create(ProfilePath.tmpProfilePath(output.profilePath),
456                             true /* isOtherReadable */, externalProfileErrors);
457                 }
458                 if (result.status == CopyAndRewriteProfileResult.Status.BAD_PROFILE) {
459                     externalProfileErrors.add(result.errorMsg);
460                 }
461             } catch (ServiceSpecificException e) {
462                 AsLog.e("Failed to initialize profile from " + pair.first, e);
463             }
464         }
465 
466         return InitProfileResult.create(
467                 null /* profile */, true /* isOtherReadable */, externalProfileErrors);
468     }
469 
logArtdException(@onNull RemoteException e)470     public static void logArtdException(@NonNull RemoteException e) {
471         String message = "An error occurred when calling artd";
472         if (e instanceof DeadObjectException) {
473             // We assume that `DeadObjectException` only happens in two cases:
474             // 1. artd crashed, in which case a native stack trace was logged.
475             // 2. artd was killed before system server during device shutdown, in which case the
476             //    exception is expected.
477             // In either case, we don't need to surface the exception from here.
478             // The Java stack trace is intentionally omitted because it's not helpful.
479             AsLog.e(message);
480         } else {
481             // Not expected. Log wtf to surface it.
482             AsLog.wtf(message, e);
483         }
484     }
485 
isSystemOrRootOrShell()486     public static boolean isSystemOrRootOrShell() {
487         int uid = Binder.getCallingUid();
488         return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID || uid == Process.SHELL_UID;
489     }
490 
sleep(long millis)491     public static void sleep(long millis) {
492         try {
493             Thread.sleep(millis);
494         } catch (InterruptedException e) {
495             AsLog.wtf("Sleep interrupted", e);
496         }
497     }
498 
pathStartsWith(@onNull String path, @NonNull String prefix)499     public static boolean pathStartsWith(@NonNull String path, @NonNull String prefix) {
500         check(!prefix.isEmpty() && !path.isEmpty() && prefix.charAt(0) == '/'
501                 && path.charAt(0) == '/');
502         int prefixLen = prefix.length();
503         if (prefix.charAt(prefixLen - 1) == '/') {
504             prefixLen--;
505         }
506         if (path.length() < prefixLen) {
507             return false;
508         }
509         for (int i = 0; i < prefixLen; i++) {
510             if (path.charAt(i) != prefix.charAt(i)) {
511                 return false;
512             }
513         }
514         return path.length() == prefixLen || path.charAt(prefixLen) == '/';
515     }
516 
517     /**
518      * Replaces the file extension of the given path with the given new extension.
519      *
520      * @param path the path to replace the extension for
521      * @param newExtension the new extension, including the leading dot
522      */
523     @NonNull
replaceFileExtension(@onNull String path, @NonNull String newExtension)524     public static String replaceFileExtension(@NonNull String path, @NonNull String newExtension) {
525         int pos = path.lastIndexOf('.');
526         int slashPos = path.indexOf('/', pos);
527         return ((pos != -1 && slashPos == -1) ? path.substring(0, pos) : path) + newExtension;
528     }
529 
getRunningProcessInfoForPackage( @onNull ActivityManager am, @NonNull PackageState pkgState)530     public static List<RunningAppProcessInfo> getRunningProcessInfoForPackage(
531             @NonNull ActivityManager am, @NonNull PackageState pkgState) {
532         return am.getRunningAppProcesses()
533                 .stream()
534                 .filter(info -> UserHandle.getAppId(info.uid) == pkgState.getAppId())
535                 .filter(info
536                         -> Arrays.stream(info.pkgList)
537                                 .anyMatch(name -> name.equals(pkgState.getPackageName())))
538                 // Filter by importance to only include running processes.
539                 // The intention of this filter is to filter out `IMPORTANCE_CACHED`. Cached
540                 // processes can be frozen by Cached apps freezer and don't respond to signals.
541                 .filter(info -> info.importance <= RunningAppProcessInfo.IMPORTANCE_SERVICE)
542                 .toList();
543     }
544 
545     @AutoValue
546     public abstract static class Abi {
create( @onNull String name, @NonNull String isa, boolean isPrimaryAbi)547         static @NonNull Abi create(
548                 @NonNull String name, @NonNull String isa, boolean isPrimaryAbi) {
549             return new AutoValue_Utils_Abi(name, isa, isPrimaryAbi);
550         }
551 
552         // The ABI name. E.g., "arm64-v8a".
name()553         abstract @NonNull String name();
554 
555         // The instruction set name. E.g., "arm64".
isa()556         abstract @NonNull String isa();
557 
isPrimaryAbi()558         abstract boolean isPrimaryAbi();
559     }
560 
561     public static class Tracing implements AutoCloseable {
Tracing(@onNull String methodName)562         public Tracing(@NonNull String methodName) {
563             Trace.traceBegin(TRACE_TAG_DALVIK, methodName);
564         }
565 
566         @Override
close()567         public void close() {
568             Trace.traceEnd(TRACE_TAG_DALVIK);
569         }
570     }
571 
572     public static class TracingWithTimingLogging extends Tracing {
573         @NonNull private final String mTag;
574         @NonNull private final String mMethodName;
575         @NonNull private final long mStartTimeMs;
576 
TracingWithTimingLogging(@onNull String tag, @NonNull String methodName)577         public TracingWithTimingLogging(@NonNull String tag, @NonNull String methodName) {
578             super(methodName);
579             mTag = tag;
580             mMethodName = methodName;
581             mStartTimeMs = SystemClock.elapsedRealtime();
582             Log.d(tag, methodName);
583         }
584 
585         @Override
close()586         public void close() {
587             Log.d(mTag,
588                     mMethodName + " took to complete: "
589                             + (SystemClock.elapsedRealtime() - mStartTimeMs) + "ms");
590             super.close();
591         }
592     }
593 
594     /** The result of {@link #getOrInitReferenceProfile} and {@link #initReferenceProfile}. */
595     @AutoValue
596     @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava.
597     public abstract static class InitProfileResult {
create(@ullable ProfilePath profile, boolean isOtherReadable, @NonNull List<String> externalProfileErrors)598         static @NonNull InitProfileResult create(@Nullable ProfilePath profile,
599                 boolean isOtherReadable, @NonNull List<String> externalProfileErrors) {
600             return new AutoValue_Utils_InitProfileResult(
601                     profile, isOtherReadable, Collections.unmodifiableList(externalProfileErrors));
602         }
603 
604         /**
605          * The found or initialized profile, or null if there is no reference profile or external
606          * profile to use.
607          */
profile()608         abstract @Nullable ProfilePath profile();
609 
610         /**
611          * Whether the profile is readable by others.
612          *
613          * If {@link #profile} returns null, this field is always true.
614          */
isOtherReadable()615         abstract boolean isOtherReadable();
616 
617         /** Errors encountered when initializing from external profiles. */
externalProfileErrors()618         abstract @NonNull List<String> externalProfileErrors();
619     }
620 
621     @FunctionalInterface
622     private interface ProfileInitializer {
get()623         CopyAndRewriteProfileResult get() throws RemoteException;
624     }
625 }
626