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