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.model; 18 19 import android.annotation.DurationMillisLong; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 25 import com.android.internal.annotations.Immutable; 26 27 import com.google.auto.value.AutoValue; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.List; 32 33 /** @hide */ 34 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 35 @Immutable 36 @AutoValue 37 public abstract class DexoptResult { 38 // Possible values of {@link #DexoptResultStatus}. 39 // A larger number means a higher priority. If multiple dex container files are processed, the 40 // final status will be the one with the highest priority. 41 /** Dexopt is skipped because there is no need to do it. */ 42 public static final int DEXOPT_SKIPPED = 10; 43 /** Dexopt is performed successfully. */ 44 public static final int DEXOPT_PERFORMED = 20; 45 /** Dexopt is failed. */ 46 public static final int DEXOPT_FAILED = 30; 47 /** Dexopt is cancelled. */ 48 public static final int DEXOPT_CANCELLED = 40; 49 50 /** @hide */ 51 // clang-format off 52 @IntDef(prefix = {"DEXOPT_"}, value = { 53 DEXOPT_SKIPPED, 54 DEXOPT_FAILED, 55 DEXOPT_PERFORMED, 56 DEXOPT_CANCELLED, 57 }) 58 // clang-format on 59 @Retention(RetentionPolicy.SOURCE) 60 public @interface DexoptResultStatus {} 61 62 /** @hide */ DexoptResult()63 protected DexoptResult() {} 64 65 /** @hide */ create(@onNull String requestedCompilerFilter, @NonNull String reason, @NonNull List<PackageDexoptResult> packageDexoptResult)66 public static @NonNull DexoptResult create(@NonNull String requestedCompilerFilter, 67 @NonNull String reason, @NonNull List<PackageDexoptResult> packageDexoptResult) { 68 return new AutoValue_DexoptResult(requestedCompilerFilter, reason, packageDexoptResult); 69 } 70 71 /** 72 * The requested compiler filter. Note that the compiler filter might be adjusted before the 73 * execution based on factors like whether the profile is available or whether the app is 74 * used by other apps. 75 * 76 * @see DexoptParams.Builder#setCompilerFilter(String) 77 * @see DexContainerFileDexoptResult#getActualCompilerFilter() 78 */ getRequestedCompilerFilter()79 public abstract @NonNull String getRequestedCompilerFilter(); 80 81 /** The compilation reason. */ getReason()82 public abstract @NonNull String getReason(); 83 84 /** 85 * The result of each individual package. 86 * 87 * If the request is to dexopt a single package without dexopting dependencies, the only 88 * element is the result of the requested package. 89 * 90 * If the request is to dexopt a single package with {@link 91 * ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES} set, the first element is the result of the 92 * requested package, and the rest are the results of the dependency packages. 93 * 94 * If the request is to dexopt multiple packages, the list contains the results of all the 95 * requested packages. The results of their dependency packages are also included if {@link 96 * ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES} is set. 97 * 98 * If the request is a batch dexopt operation that got cancelled, the list still has an entry 99 * for every package that was requested to be optimized. 100 */ getPackageDexoptResults()101 public abstract @NonNull List<PackageDexoptResult> getPackageDexoptResults(); 102 103 /** The final status. */ getFinalStatus()104 public @DexoptResultStatus int getFinalStatus() { 105 return getPackageDexoptResults() 106 .stream() 107 .mapToInt(result -> result.getStatus()) 108 .max() 109 .orElse(DEXOPT_SKIPPED); 110 } 111 112 /** @hide */ 113 @NonNull dexoptResultStatusToString(@exoptResultStatus int status)114 public static String dexoptResultStatusToString(@DexoptResultStatus int status) { 115 switch (status) { 116 case DexoptResult.DEXOPT_SKIPPED: 117 return "SKIPPED"; 118 case DexoptResult.DEXOPT_PERFORMED: 119 return "PERFORMED"; 120 case DexoptResult.DEXOPT_FAILED: 121 return "FAILED"; 122 case DexoptResult.DEXOPT_CANCELLED: 123 return "CANCELLED"; 124 } 125 throw new IllegalArgumentException("Unknown dexopt status " + status); 126 } 127 128 /** 129 * Describes the result of a package. 130 * 131 * @hide 132 */ 133 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 134 @Immutable 135 @AutoValue 136 public static abstract class PackageDexoptResult { 137 /** @hide */ PackageDexoptResult()138 protected PackageDexoptResult() {} 139 140 /** @hide */ create(@onNull String packageName, @NonNull List<DexContainerFileDexoptResult> dexContainerFileDexoptResults, @Nullable @DexoptResultStatus Integer packageLevelStatus)141 public static @NonNull PackageDexoptResult create(@NonNull String packageName, 142 @NonNull List<DexContainerFileDexoptResult> dexContainerFileDexoptResults, 143 @Nullable @DexoptResultStatus Integer packageLevelStatus) { 144 return new AutoValue_DexoptResult_PackageDexoptResult( 145 packageName, dexContainerFileDexoptResults, packageLevelStatus); 146 } 147 148 /** The package name. */ getPackageName()149 public abstract @NonNull String getPackageName(); 150 151 /** 152 * The results of dexopting dex container files. Note that there can be multiple entries 153 * for the same dex container file, but for different ABIs. 154 */ 155 public abstract @NonNull List<DexContainerFileDexoptResult> getDexContainerFileDexoptResults()156 getDexContainerFileDexoptResults(); 157 158 /** @hide */ getPackageLevelStatus()159 @Nullable @DexoptResultStatus public abstract Integer getPackageLevelStatus(); 160 161 /** The overall status of the package. */ getStatus()162 public @DexoptResultStatus int getStatus() { 163 return getPackageLevelStatus() != null ? getPackageLevelStatus() 164 : getDexContainerFileDexoptResults() 165 .stream() 166 .mapToInt(result -> result.getStatus()) 167 .max() 168 .orElse(DEXOPT_SKIPPED); 169 } 170 171 /** True if the package has any artifacts updated by this operation. */ hasUpdatedArtifacts()172 public boolean hasUpdatedArtifacts() { 173 return getDexContainerFileDexoptResults().stream().anyMatch( 174 result -> result.getStatus() == DEXOPT_PERFORMED); 175 } 176 } 177 178 /** 179 * Describes the result of dexopting a dex container file. 180 * 181 * @hide 182 */ 183 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 184 @Immutable 185 @AutoValue 186 public static abstract class DexContainerFileDexoptResult { 187 /** @hide */ DexContainerFileDexoptResult()188 protected DexContainerFileDexoptResult() {} 189 190 /** @hide */ create(@onNull String dexContainerFile, boolean isPrimaryAbi, @NonNull String abi, @NonNull String compilerFilter, @DexoptResultStatus int status, long dex2oatWallTimeMillis, long dex2oatCpuTimeMillis, long sizeBytes, long sizeBeforeBytes, boolean isSkippedDueToStorageLow)191 public static @NonNull DexContainerFileDexoptResult create(@NonNull String dexContainerFile, 192 boolean isPrimaryAbi, @NonNull String abi, @NonNull String compilerFilter, 193 @DexoptResultStatus int status, long dex2oatWallTimeMillis, 194 long dex2oatCpuTimeMillis, long sizeBytes, long sizeBeforeBytes, 195 boolean isSkippedDueToStorageLow) { 196 return new AutoValue_DexoptResult_DexContainerFileDexoptResult(dexContainerFile, 197 isPrimaryAbi, abi, compilerFilter, status, dex2oatWallTimeMillis, 198 dex2oatCpuTimeMillis, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow); 199 } 200 201 /** The absolute path to the dex container file. */ getDexContainerFile()202 public abstract @NonNull String getDexContainerFile(); 203 204 /** 205 * If true, the dexopt is for the primary ABI of the package (the ABI that the 206 * application is launched with). Otherwise, the dexopt is for an ABI that other 207 * applications might be launched with when using this application's code. 208 */ isPrimaryAbi()209 public abstract boolean isPrimaryAbi(); 210 211 /** 212 * Returns the ABI that the dexopt is for. Possible values are documented at 213 * https://developer.android.com/ndk/guides/abis#sa. 214 */ getAbi()215 public abstract @NonNull String getAbi(); 216 217 /** 218 * The actual compiler filter. 219 * 220 * @see DexoptParams.Builder#setCompilerFilter(String) 221 */ getActualCompilerFilter()222 public abstract @NonNull String getActualCompilerFilter(); 223 224 /** The status of dexopting this dex container file. */ getStatus()225 public abstract @DexoptResultStatus int getStatus(); 226 227 /** 228 * The wall time of the dex2oat invocation, in milliseconds, if dex2oat succeeded or was 229 * cancelled. Returns 0 if dex2oat failed or was not run, or if failed to get the value. 230 */ getDex2oatWallTimeMillis()231 public abstract @DurationMillisLong long getDex2oatWallTimeMillis(); 232 233 /** 234 * The CPU time of the dex2oat invocation, in milliseconds, if dex2oat succeeded or was 235 * cancelled. Returns 0 if dex2oat failed or was not run, or if failed to get the value. 236 */ getDex2oatCpuTimeMillis()237 public abstract @DurationMillisLong long getDex2oatCpuTimeMillis(); 238 239 /** 240 * The total size, in bytes, of the dexopt artifacts. Returns 0 if {@link #getStatus()} 241 * is not {@link #DEXOPT_PERFORMED}. 242 */ getSizeBytes()243 public abstract long getSizeBytes(); 244 245 /** 246 * The total size, in bytes, of the previous dexopt artifacts that has been replaced. 247 * Returns 0 if there were no previous dexopt artifacts or {@link #getStatus()} is not 248 * {@link #DEXOPT_PERFORMED}. 249 */ getSizeBeforeBytes()250 public abstract long getSizeBeforeBytes(); 251 252 /** @hide */ isSkippedDueToStorageLow()253 public abstract boolean isSkippedDueToStorageLow(); 254 255 @Override 256 @NonNull toString()257 public String toString() { 258 return String.format("DexContainerFileDexoptResult{" 259 + "dexContainerFile=%s, " 260 + "primaryAbi=%b, " 261 + "abi=%s, " 262 + "actualCompilerFilter=%s, " 263 + "status=%s, " 264 + "dex2oatWallTimeMillis=%d, " 265 + "dex2oatCpuTimeMillis=%d, " 266 + "sizeBytes=%d, " 267 + "sizeBeforeBytes=%d}", 268 getDexContainerFile(), isPrimaryAbi(), getAbi(), getActualCompilerFilter(), 269 DexoptResult.dexoptResultStatusToString(getStatus()), 270 getDex2oatWallTimeMillis(), getDex2oatCpuTimeMillis(), getSizeBytes(), 271 getSizeBeforeBytes()); 272 } 273 } 274 } 275