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 static com.android.server.art.model.ArtFlags.DexoptFlags; 20 import static com.android.server.art.model.ArtFlags.PriorityClassApi; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SystemApi; 25 import android.os.Build; 26 27 import androidx.annotation.RequiresApi; 28 29 import com.android.internal.annotations.Immutable; 30 import com.android.server.art.ArtConstants; 31 import com.android.server.art.ReasonMapping; 32 import com.android.server.art.Utils; 33 import com.android.server.art.proto.DexoptParamsProto; 34 35 /** @hide */ 36 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 37 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 38 @Immutable 39 public class DexoptParams { 40 /** @hide */ 41 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 42 public static final class Builder { 43 private DexoptParams mParams = new DexoptParams(); 44 45 /** 46 * Creates a builder. 47 * 48 * Uses default flags ({@link ArtFlags#defaultDexoptFlags()}). 49 * 50 * @param reason Compilation reason. Can be a string defined in {@link ReasonMapping} or a 51 * custom string. If the value is a string defined in {@link ReasonMapping}, it 52 * determines the compiler filter and/or the priority class, if those values are not 53 * explicitly set. If the value is a custom string, the priority class and the 54 * compiler filter must be explicitly set. 55 */ Builder(@onNull String reason)56 public Builder(@NonNull String reason) { 57 this(reason, ArtFlags.defaultDexoptFlags(reason)); 58 } 59 60 /** 61 * Same as above, but allows to specify flags. 62 */ Builder(@onNull String reason, @DexoptFlags int flags)63 public Builder(@NonNull String reason, @DexoptFlags int flags) { 64 mParams.mReason = reason; 65 setFlags(flags); 66 } 67 68 /** Replaces all flags with the given value. */ 69 @NonNull setFlags(@exoptFlags int value)70 public Builder setFlags(@DexoptFlags int value) { 71 mParams.mFlags = value; 72 return this; 73 } 74 75 /** Replaces the flags specified by the mask with the given value. */ 76 @NonNull setFlags(@exoptFlags int value, @DexoptFlags int mask)77 public Builder setFlags(@DexoptFlags int value, @DexoptFlags int mask) { 78 mParams.mFlags = (mParams.mFlags & ~mask) | (value & mask); 79 return this; 80 } 81 82 /** 83 * The target compiler filter, passed as the {@code --compiler-filer} option to dex2oat. 84 * Supported values are listed in 85 * https://source.android.com/docs/core/dalvik/configure#compilation_options. 86 * 87 * Note that the compiler filter might be adjusted before the execution based on factors 88 * like dexopt flags, whether the profile is available, or whether the app is used by other 89 * apps. If not set, the default compiler filter for the given reason will be used. 90 */ 91 @NonNull setCompilerFilter(@onNull String value)92 public Builder setCompilerFilter(@NonNull String value) { 93 mParams.mCompilerFilter = value; 94 return this; 95 } 96 97 /** 98 * The priority of the operation. If not set, the default priority class for the given 99 * reason will be used. 100 * 101 * @see PriorityClassApi 102 */ 103 @NonNull setPriorityClass(@riorityClassApi int value)104 public Builder setPriorityClass(@PriorityClassApi int value) { 105 mParams.mPriorityClass = value; 106 return this; 107 } 108 109 /** 110 * The name of the split to dexopt, or null for the base split. This option is only 111 * available when {@link ArtFlags#FLAG_FOR_SINGLE_SPLIT} is set. 112 */ 113 @NonNull setSplitName(@ullable String value)114 public Builder setSplitName(@Nullable String value) { 115 mParams.mSplitName = value; 116 return this; 117 } 118 119 /** 120 * Returns the built object. 121 * 122 * @throws IllegalArgumentException if the built options would be invalid 123 */ 124 @NonNull build()125 public DexoptParams build() { 126 if (mParams.mReason.isEmpty()) { 127 throw new IllegalArgumentException("Reason must not be empty"); 128 } 129 if (mParams.mReason.equals(ArtConstants.REASON_VDEX)) { 130 throw new IllegalArgumentException( 131 "Reason must not be '" + ArtConstants.REASON_VDEX + "'"); 132 } 133 134 if (mParams.mCompilerFilter.isEmpty()) { 135 mParams.mCompilerFilter = ReasonMapping.getCompilerFilterForReason(mParams.mReason); 136 } else if (!Utils.isValidArtServiceCompilerFilter(mParams.mCompilerFilter)) { 137 throw new IllegalArgumentException( 138 "Invalid compiler filter '" + mParams.mCompilerFilter + "'"); 139 } 140 141 if (mParams.mPriorityClass == ArtFlags.PRIORITY_NONE) { 142 mParams.mPriorityClass = ReasonMapping.getPriorityClassForReason(mParams.mReason); 143 } else if (mParams.mPriorityClass < 0 || mParams.mPriorityClass > 100) { 144 throw new IllegalArgumentException("Invalid priority class " 145 + mParams.mPriorityClass + ". Must be between 0 and 100"); 146 } 147 148 if ((mParams.mFlags & (ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX)) 149 == 0) { 150 throw new IllegalArgumentException("Nothing to dexopt"); 151 } 152 153 if ((mParams.mFlags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0 154 && (mParams.mFlags & ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES) != 0) { 155 throw new IllegalArgumentException( 156 "FLAG_SHOULD_INCLUDE_DEPENDENCIES must not set if FLAG_FOR_PRIMARY_DEX is " 157 + "not set."); 158 } 159 160 if ((mParams.mFlags & ArtFlags.FLAG_FOR_SINGLE_SPLIT) != 0) { 161 if ((mParams.mFlags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0) { 162 throw new IllegalArgumentException( 163 "FLAG_FOR_PRIMARY_DEX must be set when FLAG_FOR_SINGLE_SPLIT is set"); 164 } 165 if ((mParams.mFlags 166 & (ArtFlags.FLAG_FOR_SECONDARY_DEX 167 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)) 168 != 0) { 169 throw new IllegalArgumentException( 170 "FLAG_FOR_SECONDARY_DEX and FLAG_SHOULD_INCLUDE_DEPENDENCIES must " 171 + "not be set when FLAG_FOR_SINGLE_SPLIT is set"); 172 } 173 } else { 174 if (mParams.mSplitName != null) { 175 throw new IllegalArgumentException( 176 "Split name must not be set when FLAG_FOR_SINGLE_SPLIT is not set"); 177 } 178 } 179 180 return mParams; 181 } 182 } 183 184 /** 185 * A value indicating that dexopt shouldn't be run. This value is consumed by ART Services and 186 * is not propagated to dex2oat. 187 */ 188 public static final String COMPILER_FILTER_NOOP = "skip"; 189 190 private @DexoptFlags int mFlags = 0; 191 private @NonNull String mCompilerFilter = ""; 192 private @PriorityClassApi int mPriorityClass = ArtFlags.PRIORITY_NONE; 193 private @NonNull String mReason = ""; 194 private @Nullable String mSplitName = null; 195 DexoptParams()196 private DexoptParams() {} 197 198 /** Returns all flags. */ getFlags()199 public @DexoptFlags int getFlags() { 200 return mFlags; 201 } 202 203 /** The target compiler filter. */ getCompilerFilter()204 public @NonNull String getCompilerFilter() { 205 return mCompilerFilter; 206 } 207 208 /** The priority class. */ getPriorityClass()209 public @PriorityClassApi int getPriorityClass() { 210 return mPriorityClass; 211 } 212 213 /** 214 * The compilation reason. 215 * 216 * DO NOT directly use the string value to determine the resource usage and the process 217 * priority. Use {@link #getPriorityClass}. 218 */ getReason()219 public @NonNull String getReason() { 220 return mReason; 221 } 222 223 /** The name of the split to dexopt, or null for the base split. */ getSplitName()224 public @Nullable String getSplitName() { 225 return mSplitName; 226 } 227 228 /** @hide */ toBuilder()229 public @NonNull Builder toBuilder() { 230 return new Builder(mReason, mFlags) 231 .setCompilerFilter(mCompilerFilter) 232 .setPriorityClass(mPriorityClass) 233 .setSplitName(mSplitName); 234 } 235 236 /** @hide */ fromProto(@onNull DexoptParamsProto proto)237 public static @NonNull DexoptParams fromProto(@NonNull DexoptParamsProto proto) { 238 return new DexoptParams.Builder(proto.getReason(), proto.getFlags()) 239 .setCompilerFilter(proto.getCompilerFilter()) 240 .setPriorityClass(proto.getPriorityClass()) 241 .build(); 242 } 243 244 /** @hide */ toProto()245 public @NonNull DexoptParamsProto toProto() { 246 // The split name is intentionally omitted from the proto because it's for batch dexopt 247 // only. Extend the proto when it's used for other purposes. 248 Utils.check(getSplitName() == null); 249 250 return DexoptParamsProto.newBuilder() 251 .setFlags(getFlags()) 252 .setCompilerFilter(getCompilerFilter()) 253 .setPriorityClass(getPriorityClass()) 254 .setReason(getReason()) 255 .build(); 256 } 257 } 258