1 /* 2 * Copyright (C) 2023 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 com.android.server.art.DexUseManagerLocal.SecondaryDexInfo; 20 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo; 21 import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo; 22 import static com.android.server.art.Utils.Abi; 23 24 import android.annotation.NonNull; 25 import android.content.Context; 26 import android.os.Binder; 27 import android.os.Build; 28 import android.os.RemoteException; 29 import android.os.ServiceSpecificException; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Pair; 33 34 import androidx.annotation.RequiresApi; 35 36 import com.android.internal.annotations.Immutable; 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.server.LocalManagerRegistry; 39 import com.android.server.art.model.DetailedDexInfo; 40 import com.android.server.pm.pkg.AndroidPackage; 41 import com.android.server.pm.pkg.PackageState; 42 43 import dalvik.system.DexFile; 44 45 import com.google.auto.value.AutoValue; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.Objects; 50 51 /** 52 * A helper class to list files that ART Service consumes or produces. 53 * 54 * @hide 55 */ 56 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 57 public class ArtFileManager { 58 @NonNull private final Injector mInjector; 59 ArtFileManager(@onNull Context context)60 public ArtFileManager(@NonNull Context context) { 61 this(new Injector(context)); 62 } 63 64 @VisibleForTesting ArtFileManager(@onNull Injector injector)65 public ArtFileManager(@NonNull Injector injector) { 66 mInjector = injector; 67 } 68 69 @NonNull getDexAndAbis( @onNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull Options options)70 public List<Pair<DetailedDexInfo, Abi>> getDexAndAbis( 71 @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull Options options) { 72 List<Pair<DetailedDexInfo, Abi>> dexAndAbis = new ArrayList<>(); 73 if (options.forPrimaryDex()) { 74 for (DetailedPrimaryDexInfo dexInfo : 75 PrimaryDexUtils.getDetailedDexInfo(pkgState, pkg)) { 76 for (Abi abi : Utils.getAllAbis(pkgState)) { 77 dexAndAbis.add(Pair.create(dexInfo, abi)); 78 } 79 } 80 } 81 if (options.forSecondaryDex()) { 82 List<? extends SecondaryDexInfo> dexInfos = getSecondaryDexInfo(pkgState, options); 83 for (SecondaryDexInfo dexInfo : dexInfos) { 84 if (!mInjector.isSystemOrRootOrShell() 85 && !mInjector.getCallingUserHandle().equals(dexInfo.userHandle())) { 86 continue; 87 } 88 for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { 89 dexAndAbis.add(Pair.create(dexInfo, abi)); 90 } 91 } 92 } 93 return dexAndAbis; 94 } 95 96 /** 97 * Returns the writable paths of artifacts, regardless of whether the artifacts exist or 98 * whether they are usable. 99 */ 100 @NonNull getWritableArtifacts(@onNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull Options options)101 public WritableArtifactLists getWritableArtifacts(@NonNull PackageState pkgState, 102 @NonNull AndroidPackage pkg, @NonNull Options options) throws RemoteException { 103 List<ArtifactsPath> artifacts = new ArrayList<>(); 104 List<SecureDexMetadataWithCompanionPaths> sdmFiles = new ArrayList<>(); 105 List<RuntimeArtifactsPath> runtimeArtifacts = new ArrayList<>(); 106 107 if (options.forPrimaryDex()) { 108 boolean isInDalvikCache = Utils.isInDalvikCache(pkgState, mInjector.getArtd()); 109 for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { 110 for (Abi abi : Utils.getAllAbis(pkgState)) { 111 artifacts.add(AidlUtils.buildArtifactsPathAsInput( 112 dexInfo.dexPath(), abi.isa(), isInDalvikCache)); 113 // SDM files are only for primary dex files. 114 sdmFiles.add(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 115 dexInfo.dexPath(), abi.isa(), isInDalvikCache)); 116 // Runtime images are only generated for primary dex files. 117 runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath( 118 pkgState.getPackageName(), dexInfo.dexPath(), abi.isa())); 119 } 120 } 121 } 122 123 if (options.forSecondaryDex()) { 124 for (SecondaryDexInfo dexInfo : getSecondaryDexInfo(pkgState, options)) { 125 for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { 126 artifacts.add(AidlUtils.buildArtifactsPathAsInput( 127 dexInfo.dexPath(), abi.isa(), false /* isInDalvikCache */)); 128 } 129 } 130 } 131 132 return new WritableArtifactLists(artifacts, sdmFiles, runtimeArtifacts); 133 } 134 135 /** Returns artifacts that are usable, regardless of whether they are writable. */ 136 @NonNull getUsableArtifacts( @onNull PackageState pkgState, @NonNull AndroidPackage pkg)137 public UsableArtifactLists getUsableArtifacts( 138 @NonNull PackageState pkgState, @NonNull AndroidPackage pkg) throws RemoteException { 139 List<ArtifactsPath> artifacts = new ArrayList<>(); 140 List<VdexPath> vdexFiles = new ArrayList<>(); 141 List<SecureDexMetadataWithCompanionPaths> sdmFiles = new ArrayList<>(); 142 List<RuntimeArtifactsPath> runtimeArtifacts = new ArrayList<>(); 143 144 var options = ArtFileManager.Options.builder() 145 .setForPrimaryDex(true) 146 .setForSecondaryDex(true) 147 .setExcludeForObsoleteDexesAndLoaders(true) 148 .build(); 149 for (Pair<DetailedDexInfo, Abi> pair : getDexAndAbis(pkgState, pkg, options)) { 150 DetailedDexInfo dexInfo = pair.first; 151 Abi abi = pair.second; 152 try { 153 GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus( 154 dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); 155 if (result.artifactsLocation == ArtifactsLocation.DALVIK_CACHE 156 || result.artifactsLocation == ArtifactsLocation.NEXT_TO_DEX) { 157 ArtifactsPath thisArtifacts = 158 AidlUtils.buildArtifactsPathAsInput(dexInfo.dexPath(), abi.isa(), 159 result.artifactsLocation == ArtifactsLocation.DALVIK_CACHE); 160 if (result.compilationReason.equals(ArtConstants.REASON_VDEX)) { 161 // Only the VDEX file is usable. 162 vdexFiles.add(VdexPath.artifactsPath(thisArtifacts)); 163 } else { 164 artifacts.add(thisArtifacts); 165 } 166 } else if (result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE 167 || result.artifactsLocation == ArtifactsLocation.SDM_NEXT_TO_DEX) { 168 sdmFiles.add(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 169 dexInfo.dexPath(), abi.isa(), 170 result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE)); 171 } 172 173 if (result.artifactsLocation != ArtifactsLocation.NONE_OR_ERROR) { 174 // Runtime images are only generated for primary dex files. 175 if (dexInfo instanceof DetailedPrimaryDexInfo 176 && !DexFile.isOptimizedCompilerFilter(result.compilerFilter)) { 177 // Those not added to the list are definitely unusable, but those added to 178 // the list are not necessarily usable. For example, runtime artifacts can 179 // be outdated when the corresponding dex file is updated, but they may 180 // still show up in this list. 181 // 182 // However, this is not a severe problem. For `ArtManagerLocal.cleanup`, the 183 // worst result is only that we are keeping more runtime artifacts than 184 // needed. For `ArtManagerLocal.getArtManagedFileStats`, this is an edge 185 // case because the API call is transitively initiated by the app itself, 186 // and the runtime refreshes unusable runtime artifacts as soon as the app 187 // starts. 188 // 189 // TODO(jiakaiz): Improve this. 190 runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath( 191 pkgState.getPackageName(), dexInfo.dexPath(), abi.isa())); 192 } 193 } 194 } catch (ServiceSpecificException e) { 195 AsLog.e(String.format( 196 "Failed to get dexopt status [packageName = %s, dexPath = %s, " 197 + "isa = %s, classLoaderContext = %s]", 198 pkgState.getPackageName(), dexInfo.dexPath(), abi.isa(), 199 dexInfo.classLoaderContext()), 200 e); 201 } 202 } 203 204 return new UsableArtifactLists(artifacts, vdexFiles, sdmFiles, runtimeArtifacts); 205 } 206 207 @NonNull getProfiles( @onNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull Options options)208 public ProfileLists getProfiles( 209 @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull Options options) { 210 List<ProfilePath> refProfiles = new ArrayList<>(); 211 List<ProfilePath> curProfiles = new ArrayList<>(); 212 213 if (options.forPrimaryDex()) { 214 for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { 215 refProfiles.add(PrimaryDexUtils.buildRefProfilePathAsInput(pkgState, dexInfo)); 216 curProfiles.addAll(mInjector.isSystemOrRootOrShell() 217 ? PrimaryDexUtils.getCurProfiles( 218 mInjector.getUserManager(), pkgState, dexInfo) 219 : PrimaryDexUtils.getCurProfiles( 220 List.of(mInjector.getCallingUserHandle()), pkgState, 221 dexInfo)); 222 } 223 } 224 if (options.forSecondaryDex()) { 225 List<? extends SecondaryDexInfo> dexInfos = getSecondaryDexInfo(pkgState, options); 226 for (SecondaryDexInfo dexInfo : dexInfos) { 227 if (!mInjector.isSystemOrRootOrShell() 228 && !mInjector.getCallingUserHandle().equals(dexInfo.userHandle())) { 229 continue; 230 } 231 refProfiles.add( 232 AidlUtils.buildProfilePathForSecondaryRefAsInput(dexInfo.dexPath())); 233 curProfiles.add(AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); 234 } 235 } 236 237 return new ProfileLists(refProfiles, curProfiles); 238 } 239 240 @NonNull getSecondaryDexInfo( @onNull PackageState pkgState, @NonNull Options options)241 private List<? extends SecondaryDexInfo> getSecondaryDexInfo( 242 @NonNull PackageState pkgState, @NonNull Options options) { 243 return options.excludeForObsoleteDexesAndLoaders() 244 ? mInjector.getDexUseManager().getCheckedSecondaryDexInfo( 245 pkgState.getPackageName(), true /* excludeObsoleteDexesAndLoaders */) 246 : mInjector.getDexUseManager().getSecondaryDexInfo(pkgState.getPackageName()); 247 } 248 WritableArtifactLists(@onNull List<ArtifactsPath> artifacts, @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles, @NonNull List<RuntimeArtifactsPath> runtimeArtifacts)249 public record WritableArtifactLists(@NonNull List<ArtifactsPath> artifacts, 250 @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles, 251 @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {} 252 UsableArtifactLists(@onNull List<ArtifactsPath> artifacts, @NonNull List<VdexPath> vdexFiles, @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles, @NonNull List<RuntimeArtifactsPath> runtimeArtifacts)253 public record UsableArtifactLists(@NonNull List<ArtifactsPath> artifacts, 254 @NonNull List<VdexPath> vdexFiles, 255 @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles, 256 @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {} 257 ProfileLists( @onNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles)258 public record ProfileLists( 259 @NonNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles) { 260 public @NonNull List<ProfilePath> allProfiles() { 261 List<ProfilePath> profiles = new ArrayList<>(); 262 profiles.addAll(refProfiles()); 263 profiles.addAll(curProfiles()); 264 return profiles; 265 } 266 } 267 268 @Immutable 269 @AutoValue 270 public abstract static class Options { 271 // Whether to return files for primary dex files. forPrimaryDex()272 public abstract boolean forPrimaryDex(); 273 // Whether to return files for secondary dex files. forSecondaryDex()274 public abstract boolean forSecondaryDex(); 275 // If true, excludes files for secondary dex files and loaders based on file visibility. See 276 // details in {@link DexUseManagerLocal#getCheckedSecondaryDexInfo}. excludeForObsoleteDexesAndLoaders()277 public abstract boolean excludeForObsoleteDexesAndLoaders(); 278 builder()279 public static @NonNull Builder builder() { 280 return new AutoValue_ArtFileManager_Options.Builder() 281 .setForPrimaryDex(false) 282 .setForSecondaryDex(false) 283 .setExcludeForObsoleteDexesAndLoaders(false); 284 } 285 286 @AutoValue.Builder 287 public abstract static class Builder { setForPrimaryDex(boolean value)288 public abstract @NonNull Builder setForPrimaryDex(boolean value); setForSecondaryDex(boolean value)289 public abstract @NonNull Builder setForSecondaryDex(boolean value); setExcludeForObsoleteDexesAndLoaders(boolean value)290 public abstract @NonNull Builder setExcludeForObsoleteDexesAndLoaders(boolean value); build()291 public abstract @NonNull Options build(); 292 } 293 } 294 295 /**Injector pattern for testing purpose. */ 296 @VisibleForTesting 297 public static class Injector { 298 @NonNull private final Context mContext; 299 Injector(@onNull Context context)300 Injector(@NonNull Context context) { 301 mContext = context; 302 303 // Call the getters for the dependencies that aren't optional, to ensure correct 304 // initialization order. 305 GlobalInjector.getInstance().checkArtModuleServiceManager(); 306 getUserManager(); 307 getDexUseManager(); 308 } 309 310 @NonNull getArtd()311 public IArtd getArtd() { 312 return ArtdRefCache.getInstance().getArtd(); 313 } 314 315 @NonNull getUserManager()316 public UserManager getUserManager() { 317 return Objects.requireNonNull(mContext.getSystemService(UserManager.class)); 318 } 319 320 @NonNull getDexUseManager()321 public DexUseManagerLocal getDexUseManager() { 322 return Objects.requireNonNull( 323 LocalManagerRegistry.getManager(DexUseManagerLocal.class)); 324 } 325 isSystemOrRootOrShell()326 public boolean isSystemOrRootOrShell() { 327 // At the time of writing, this is only typically true unless called by an app through 328 // {@link ArtManagerLocal#getArtManagedFileStats}. 329 return Utils.isSystemOrRootOrShell(); 330 } 331 332 @NonNull getCallingUserHandle()333 public UserHandle getCallingUserHandle() { 334 return Binder.getCallingUserHandle(); 335 } 336 } 337 } 338