1 /* 2 * Copyright (C) 2017 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.pm.dex; 18 19 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; 20 21 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; 22 23 import android.annotation.NonNull; 24 import android.util.Log; 25 26 import com.android.server.art.ReasonMapping; 27 import com.android.server.art.model.ArtFlags; 28 import com.android.server.art.model.DexoptParams; 29 import com.android.server.pm.PackageManagerService; 30 31 /** 32 * Options used for dexopt invocations. 33 */ 34 public final class DexoptOptions { 35 private static final String TAG = "DexoptOptions"; 36 37 // When set, the profiles will be checked for updates before calling dexopt. If 38 // the apps profiles didn't update in a meaningful way (decided by the compiler), dexopt 39 // will be skipped. 40 // Currently this only affects the optimization of primary apks. Secondary dex files 41 // will always check the profiles for updates. 42 public static final int DEXOPT_CHECK_FOR_PROFILES_UPDATES = 1 << 0; 43 44 // When set, dexopt will execute unconditionally (even if not needed). 45 public static final int DEXOPT_FORCE = 1 << 1; 46 47 // Whether or not the invocation of dexopt is done after the boot is completed. This is used 48 // in order to adjust the priority of the compilation thread. 49 public static final int DEXOPT_BOOT_COMPLETE = 1 << 2; 50 51 // When set, the dexopt invocation will optimize only the secondary dex files. If false, dexopt 52 // will only consider the primary apk. 53 public static final int DEXOPT_ONLY_SECONDARY_DEX = 1 << 3; 54 55 // When set, dexopt will attempt to scale down the optimizations previously applied in order 56 // save disk space. 57 public static final int DEXOPT_DOWNGRADE = 1 << 5; 58 59 // When set, dexopt will compile the dex file as a shared library even if it is not actually 60 // used by other apps. This is used to force the compilation or shared libraries declared 61 // with in the manifest with ''uses-library' before we have a chance to detect they are 62 // actually shared at runtime. 63 public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6; 64 65 // When set, indicates that dexopt is invoked from the background service. 66 public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9; 67 68 // When set, indicates that dexopt is invoked from the install time flow and 69 // should get the dex metdata file if present. 70 public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10; 71 72 // When set, indicates that dexopt is being invoked from the install flow during device restore 73 // or device setup and should be scheduled appropriately. 74 public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove 75 76 /** 77 * A value indicating that dexopt shouldn't be run. This string is only used when loading 78 * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat. 79 */ 80 public static final String COMPILER_FILTER_NOOP = "skip"; 81 82 // The name of package to optimize. 83 private final String mPackageName; 84 85 // The intended target compiler filter. Note that dexopt might adjust the filter before the 86 // execution based on factors like: vmSafeMode and packageUsedByOtherApps. 87 private final String mCompilerFilter; 88 89 // The set of flags for the dexopt options. It's a mix of the DEXOPT_* flags. 90 private final int mFlags; 91 92 // When not null, dexopt will optimize only the split identified by this APK file name (not 93 // split name). It only applies for primary apk and it's always null if mOnlySecondaryDex is 94 // true. 95 private final String mSplitName; 96 97 // The reason for invoking dexopt (see PackageManagerService.REASON_* constants). 98 // A -1 value denotes an unknown reason. 99 private final int mCompilationReason; 100 DexoptOptions(String packageName, String compilerFilter, int flags)101 public DexoptOptions(String packageName, String compilerFilter, int flags) { 102 this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags); 103 } 104 DexoptOptions(String packageName, int compilationReason, int flags)105 public DexoptOptions(String packageName, int compilationReason, int flags) { 106 this(packageName, compilationReason, getCompilerFilterForReason(compilationReason), 107 /*splitName*/ null, flags); 108 } 109 DexoptOptions(String packageName, int compilationReason, String compilerFilter, String splitName, int flags)110 public DexoptOptions(String packageName, int compilationReason, String compilerFilter, 111 String splitName, int flags) { 112 int validityMask = 113 DEXOPT_CHECK_FOR_PROFILES_UPDATES | 114 DEXOPT_FORCE | 115 DEXOPT_BOOT_COMPLETE | 116 DEXOPT_ONLY_SECONDARY_DEX | 117 DEXOPT_DOWNGRADE | 118 DEXOPT_AS_SHARED_LIBRARY | 119 DEXOPT_IDLE_BACKGROUND_JOB | 120 DEXOPT_INSTALL_WITH_DEX_METADATA_FILE | 121 DEXOPT_FOR_RESTORE; 122 if ((flags & (~validityMask)) != 0) { 123 throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags)); 124 } 125 126 mPackageName = packageName; 127 mCompilerFilter = compilerFilter; 128 mFlags = flags; 129 mSplitName = splitName; 130 mCompilationReason = compilationReason; 131 } 132 getPackageName()133 public String getPackageName() { 134 return mPackageName; 135 } 136 isCheckForProfileUpdates()137 public boolean isCheckForProfileUpdates() { 138 return (mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) != 0; 139 } 140 getCompilerFilter()141 public String getCompilerFilter() { 142 return mCompilerFilter; 143 } 144 isForce()145 public boolean isForce() { 146 return (mFlags & DEXOPT_FORCE) != 0; 147 } 148 isBootComplete()149 public boolean isBootComplete() { 150 return (mFlags & DEXOPT_BOOT_COMPLETE) != 0; 151 } 152 isDexoptOnlySecondaryDex()153 public boolean isDexoptOnlySecondaryDex() { 154 return (mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0; 155 } 156 isDowngrade()157 public boolean isDowngrade() { 158 return (mFlags & DEXOPT_DOWNGRADE) != 0; 159 } 160 isDexoptAsSharedLibrary()161 public boolean isDexoptAsSharedLibrary() { 162 return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0; 163 } 164 isDexoptIdleBackgroundJob()165 public boolean isDexoptIdleBackgroundJob() { 166 return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0; 167 } 168 isDexoptInstallWithDexMetadata()169 public boolean isDexoptInstallWithDexMetadata() { 170 return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0; 171 } 172 isDexoptInstallForRestore()173 public boolean isDexoptInstallForRestore() { 174 return (mFlags & DEXOPT_FOR_RESTORE) != 0; 175 } 176 getSplitName()177 public String getSplitName() { 178 return mSplitName; 179 } 180 getFlags()181 public int getFlags() { 182 return mFlags; 183 } 184 getCompilationReason()185 public int getCompilationReason() { 186 return mCompilationReason; 187 } 188 isCompilationEnabled()189 public boolean isCompilationEnabled() { 190 return !mCompilerFilter.equals(COMPILER_FILTER_NOOP); 191 } 192 193 /** 194 * Creates a new set of DexoptOptions which are the same with the exception of the compiler 195 * filter (set to the given value). 196 */ overrideCompilerFilter(String newCompilerFilter)197 public DexoptOptions overrideCompilerFilter(String newCompilerFilter) { 198 return new DexoptOptions( 199 mPackageName, 200 mCompilationReason, 201 newCompilerFilter, 202 mSplitName, 203 mFlags); 204 } 205 206 /** 207 * Returns the ART Service reason for the given PackageManagerService reason. Throws unchecked 208 * exceptions for reasons that aren't supported. 209 */ convertToArtServiceDexoptReason(int pmDexoptReason)210 public static @NonNull String convertToArtServiceDexoptReason(int pmDexoptReason) { 211 switch (pmDexoptReason) { 212 case PackageManagerService.REASON_FIRST_BOOT: 213 return ReasonMapping.REASON_FIRST_BOOT; 214 case PackageManagerService.REASON_BOOT_AFTER_OTA: 215 return ReasonMapping.REASON_BOOT_AFTER_OTA; 216 case PackageManagerService.REASON_INSTALL: 217 return ReasonMapping.REASON_INSTALL; 218 case PackageManagerService.REASON_INSTALL_FAST: 219 return ReasonMapping.REASON_INSTALL_FAST; 220 case PackageManagerService.REASON_INSTALL_BULK: 221 return ReasonMapping.REASON_INSTALL_BULK; 222 case PackageManagerService.REASON_INSTALL_BULK_SECONDARY: 223 return ReasonMapping.REASON_INSTALL_BULK_SECONDARY; 224 case PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED: 225 return ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED; 226 case PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED: 227 return ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; 228 case PackageManagerService.REASON_BACKGROUND_DEXOPT: 229 return ReasonMapping.REASON_BG_DEXOPT; 230 case PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE: 231 return ReasonMapping.REASON_INACTIVE; 232 case PackageManagerService.REASON_CMDLINE: 233 return ReasonMapping.REASON_CMDLINE; 234 case PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE: 235 return ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE; 236 case PackageManagerService.REASON_POST_BOOT: 237 case PackageManagerService.REASON_SHARED: 238 case PackageManagerService.REASON_AB_OTA: 239 // REASON_POST_BOOT isn't supported - that dexopt stage is getting removed. 240 // REASON_SHARED shouldn't go to ART Service - it's only used at lower levels 241 // in PackageDexOptimizer. 242 // TODO(b/251921228): OTA isn't supported, so REASON_AB_OTA shouldn't come this way 243 // either. 244 throw new UnsupportedOperationException( 245 "ART Service unsupported compilation reason " + pmDexoptReason); 246 default: 247 throw new IllegalArgumentException("Invalid compilation reason " + pmDexoptReason); 248 } 249 } 250 251 /** 252 * Returns an {@link DexoptParams} instance corresponding to this object, for use with 253 * {@link com.android.server.art.ArtManagerLocal}. 254 * 255 * @param extraFlags extra {@link ArtFlags#DexoptFlags} to set in the returned 256 * {@code DexoptParams} beyond those converted from this object 257 * @throws UnsupportedOperationException if the settings cannot be accurately represented. 258 */ convertToDexoptParams( int extraFlags)259 public @NonNull DexoptParams convertToDexoptParams(/*@DexoptFlags*/ int extraFlags) { 260 if (mSplitName != null) { 261 // ART Service supports dexopting a single split - see ArtFlags.FLAG_FOR_SINGLE_SPLIT. 262 // However using it here requires searching through the splits to find the one matching 263 // the APK file name in mSplitName, and we don't have the AndroidPackage available for 264 // that. 265 // 266 // Hence we throw here instead, under the assumption that no code paths that dexopt 267 // splits need this conversion (e.g. shell commands with the --split argument are 268 // handled by ART Service directly). 269 throw new UnsupportedOperationException( 270 "Request to optimize only split " + mSplitName + " for " + mPackageName); 271 } 272 273 /*@DexoptFlags*/ int flags = extraFlags; 274 if ((mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) == 0 275 && isProfileGuidedCompilerFilter(mCompilerFilter)) { 276 // ART Service doesn't support bypassing the profile update check when profiles are 277 // used, so not setting this flag is not supported. 278 throw new IllegalArgumentException( 279 "DEXOPT_CHECK_FOR_PROFILES_UPDATES must be set with profile guided filter"); 280 } 281 if ((mFlags & DEXOPT_FORCE) != 0) { 282 flags |= ArtFlags.FLAG_FORCE; 283 } 284 if ((mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0) { 285 flags |= ArtFlags.FLAG_FOR_SECONDARY_DEX; 286 } else { 287 flags |= ArtFlags.FLAG_FOR_PRIMARY_DEX; 288 } 289 if ((mFlags & DEXOPT_DOWNGRADE) != 0) { 290 flags |= ArtFlags.FLAG_SHOULD_DOWNGRADE; 291 } 292 if ((mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) == 0) { 293 // ART Service cannot be instructed to ignore a DM file if present. 294 Log.w(TAG, 295 "DEXOPT_INSTALL_WITH_DEX_METADATA_FILE not set in request to optimise " 296 + mPackageName 297 + " - ART Service will unconditionally use a DM file if present."); 298 } 299 300 /*@PriorityClassApi*/ int priority; 301 // Replicates logic in RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags in installd. 302 if ((mFlags & DEXOPT_BOOT_COMPLETE) != 0) { 303 if ((mFlags & DEXOPT_FOR_RESTORE) != 0) { 304 priority = ArtFlags.PRIORITY_INTERACTIVE_FAST; 305 } else if ((mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0) { 306 priority = ArtFlags.PRIORITY_BACKGROUND; 307 } else { 308 priority = ArtFlags.PRIORITY_INTERACTIVE; 309 } 310 } else { 311 priority = ArtFlags.PRIORITY_BOOT; 312 } 313 314 // The following flags in mFlags are ignored: 315 // 316 // - DEXOPT_AS_SHARED_LIBRARY: It's implicit with ART Service since it always looks at 317 // <uses-library> rather than actual dependencies. 318 // 319 // We don't require it to be set either. It's safe when switching between old and new 320 // code paths since the only effect is that some packages may be unnecessarily compiled 321 // without user profiles. 322 323 return new DexoptParams.Builder(convertToArtServiceDexoptReason(mCompilationReason), flags) 324 .setCompilerFilter(mCompilerFilter) 325 .setPriorityClass(priority) 326 .build(); 327 } 328 } 329