• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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