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     // The name of package to optimize.
77     private final String mPackageName;
78 
79     // The intended target compiler filter. Note that dexopt might adjust the filter before the
80     // execution based on factors like: vmSafeMode and packageUsedByOtherApps.
81     private final String mCompilerFilter;
82 
83     // The set of flags for the dexopt options. It's a mix of the DEXOPT_* flags.
84     private final int mFlags;
85 
86     // When not null, dexopt will optimize only the split identified by this APK file name (not
87     // split name). It only applies for primary apk and it's always null if mOnlySecondaryDex is
88     // true.
89     private final String mSplitName;
90 
91     // The reason for invoking dexopt (see PackageManagerService.REASON_* constants).
92     // A -1 value denotes an unknown reason.
93     private final int mCompilationReason;
94 
DexoptOptions(String packageName, String compilerFilter, int flags)95     public DexoptOptions(String packageName, String compilerFilter, int flags) {
96         this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags);
97     }
98 
DexoptOptions(String packageName, int compilationReason, int flags)99     public DexoptOptions(String packageName, int compilationReason, int flags) {
100         this(packageName, compilationReason, getCompilerFilterForReason(compilationReason),
101                 /*splitName*/ null, flags);
102     }
103 
DexoptOptions(String packageName, int compilationReason, String compilerFilter, String splitName, int flags)104     public DexoptOptions(String packageName, int compilationReason, String compilerFilter,
105                 String splitName, int flags) {
106         int validityMask =
107                 DEXOPT_CHECK_FOR_PROFILES_UPDATES |
108                 DEXOPT_FORCE |
109                 DEXOPT_BOOT_COMPLETE |
110                 DEXOPT_ONLY_SECONDARY_DEX |
111                 DEXOPT_DOWNGRADE |
112                 DEXOPT_AS_SHARED_LIBRARY |
113                 DEXOPT_IDLE_BACKGROUND_JOB |
114                 DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
115                 DEXOPT_FOR_RESTORE;
116         if ((flags & (~validityMask)) != 0) {
117             throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
118         }
119 
120         mPackageName = packageName;
121         mCompilerFilter = compilerFilter;
122         mFlags = flags;
123         mSplitName = splitName;
124         mCompilationReason = compilationReason;
125     }
126 
getPackageName()127     public String getPackageName() {
128         return mPackageName;
129     }
130 
isCheckForProfileUpdates()131     public boolean isCheckForProfileUpdates() {
132         return (mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) != 0;
133     }
134 
getCompilerFilter()135     public String getCompilerFilter() {
136         return mCompilerFilter;
137     }
138 
isForce()139     public boolean isForce() {
140         return (mFlags & DEXOPT_FORCE) != 0;
141     }
142 
isBootComplete()143     public boolean isBootComplete() {
144         return (mFlags & DEXOPT_BOOT_COMPLETE) != 0;
145     }
146 
isDexoptOnlySecondaryDex()147     public boolean isDexoptOnlySecondaryDex() {
148         return (mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0;
149     }
150 
isDowngrade()151     public boolean isDowngrade() {
152         return (mFlags & DEXOPT_DOWNGRADE) != 0;
153     }
154 
isDexoptAsSharedLibrary()155     public boolean isDexoptAsSharedLibrary() {
156         return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
157     }
158 
isDexoptIdleBackgroundJob()159     public boolean isDexoptIdleBackgroundJob() {
160         return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
161     }
162 
isDexoptInstallWithDexMetadata()163     public boolean isDexoptInstallWithDexMetadata() {
164         return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
165     }
166 
isDexoptInstallForRestore()167     public boolean isDexoptInstallForRestore() {
168         return (mFlags & DEXOPT_FOR_RESTORE) != 0;
169     }
170 
getSplitName()171     public String getSplitName() {
172         return mSplitName;
173     }
174 
getFlags()175     public int getFlags() {
176         return mFlags;
177     }
178 
getCompilationReason()179     public int getCompilationReason() {
180         return mCompilationReason;
181     }
182 
183     /**
184      * Creates a new set of DexoptOptions which are the same with the exception of the compiler
185      * filter (set to the given value).
186      */
overrideCompilerFilter(String newCompilerFilter)187     public DexoptOptions overrideCompilerFilter(String newCompilerFilter) {
188         return new DexoptOptions(
189                 mPackageName,
190                 mCompilationReason,
191                 newCompilerFilter,
192                 mSplitName,
193                 mFlags);
194     }
195 
196     /**
197      * Returns the ART Service reason for the given PackageManagerService reason. Throws unchecked
198      * exceptions for reasons that aren't supported.
199      */
convertToArtServiceDexoptReason(int pmDexoptReason)200     public static @NonNull String convertToArtServiceDexoptReason(int pmDexoptReason) {
201         switch (pmDexoptReason) {
202             case PackageManagerService.REASON_FIRST_BOOT:
203                 return ReasonMapping.REASON_FIRST_BOOT;
204             case PackageManagerService.REASON_BOOT_AFTER_OTA:
205                 return ReasonMapping.REASON_BOOT_AFTER_OTA;
206             case PackageManagerService.REASON_INSTALL:
207                 return ReasonMapping.REASON_INSTALL;
208             case PackageManagerService.REASON_INSTALL_FAST:
209                 return ReasonMapping.REASON_INSTALL_FAST;
210             case PackageManagerService.REASON_INSTALL_BULK:
211                 return ReasonMapping.REASON_INSTALL_BULK;
212             case PackageManagerService.REASON_INSTALL_BULK_SECONDARY:
213                 return ReasonMapping.REASON_INSTALL_BULK_SECONDARY;
214             case PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED:
215                 return ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED;
216             case PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED:
217                 return ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
218             case PackageManagerService.REASON_BACKGROUND_DEXOPT:
219                 return ReasonMapping.REASON_BG_DEXOPT;
220             case PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE:
221                 return ReasonMapping.REASON_INACTIVE;
222             case PackageManagerService.REASON_CMDLINE:
223                 return ReasonMapping.REASON_CMDLINE;
224             case PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE:
225                 return ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE;
226             case PackageManagerService.REASON_POST_BOOT:
227             case PackageManagerService.REASON_SHARED:
228             case PackageManagerService.REASON_AB_OTA:
229                 // REASON_POST_BOOT isn't supported - that dexopt stage is getting removed.
230                 // REASON_SHARED shouldn't go to ART Service - it's only used at lower levels
231                 // in PackageDexOptimizer.
232                 // TODO(b/251921228): OTA isn't supported, so REASON_AB_OTA shouldn't come this way
233                 // either.
234                 throw new UnsupportedOperationException(
235                         "ART Service unsupported compilation reason " + pmDexoptReason);
236             default:
237                 throw new IllegalArgumentException("Invalid compilation reason " + pmDexoptReason);
238         }
239     }
240 
241     /**
242      * Returns an {@link DexoptParams} instance corresponding to this object, for use with
243      * {@link com.android.server.art.ArtManagerLocal}.
244      *
245      * @param extraFlags extra {@link ArtFlags#DexoptFlags} to set in the returned
246      *     {@code DexoptParams} beyond those converted from this object
247      * @throws UnsupportedOperationException if the settings cannot be accurately represented.
248      */
convertToDexoptParams( int extraFlags)249     public @NonNull DexoptParams convertToDexoptParams(/*@DexoptFlags*/ int extraFlags) {
250         if (mSplitName != null) {
251             // ART Service supports dexopting a single split - see ArtFlags.FLAG_FOR_SINGLE_SPLIT.
252             // However using it here requires searching through the splits to find the one matching
253             // the APK file name in mSplitName, and we don't have the AndroidPackage available for
254             // that.
255             //
256             // Hence we throw here instead, under the assumption that no code paths that dexopt
257             // splits need this conversion (e.g. shell commands with the --split argument are
258             // handled by ART Service directly).
259             throw new UnsupportedOperationException(
260                     "Request to optimize only split " + mSplitName + " for " + mPackageName);
261         }
262 
263         /*@DexoptFlags*/ int flags = extraFlags;
264         if ((mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) == 0
265                 && isProfileGuidedCompilerFilter(mCompilerFilter)) {
266             // ART Service doesn't support bypassing the profile update check when profiles are
267             // used, so not setting this flag is not supported.
268             throw new IllegalArgumentException(
269                     "DEXOPT_CHECK_FOR_PROFILES_UPDATES must be set with profile guided filter");
270         }
271         if ((mFlags & DEXOPT_FORCE) != 0) {
272             flags |= ArtFlags.FLAG_FORCE;
273         }
274         if ((mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0) {
275             flags |= ArtFlags.FLAG_FOR_SECONDARY_DEX;
276         } else {
277             flags |= ArtFlags.FLAG_FOR_PRIMARY_DEX;
278         }
279         if ((mFlags & DEXOPT_DOWNGRADE) != 0) {
280             flags |= ArtFlags.FLAG_SHOULD_DOWNGRADE;
281         }
282         if ((mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) == 0) {
283             // ART Service cannot be instructed to ignore a DM file if present.
284             Log.w(TAG,
285                     "DEXOPT_INSTALL_WITH_DEX_METADATA_FILE not set in request to optimise "
286                             + mPackageName
287                             + " - ART Service will unconditionally use a DM file if present.");
288         }
289 
290         /*@PriorityClassApi*/ int priority;
291         // Replicates logic in RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags in installd.
292         if ((mFlags & DEXOPT_BOOT_COMPLETE) != 0) {
293             if ((mFlags & DEXOPT_FOR_RESTORE) != 0) {
294                 priority = ArtFlags.PRIORITY_INTERACTIVE_FAST;
295             } else if ((mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0) {
296                 priority = ArtFlags.PRIORITY_BACKGROUND;
297             } else {
298                 priority = ArtFlags.PRIORITY_INTERACTIVE;
299             }
300         } else {
301             priority = ArtFlags.PRIORITY_BOOT;
302         }
303 
304         // The following flags in mFlags are ignored:
305         //
306         // -  DEXOPT_AS_SHARED_LIBRARY: It's implicit with ART Service since it always looks at
307         //    <uses-library> rather than actual dependencies.
308         //
309         //    We don't require it to be set either. It's safe when switching between old and new
310         //    code paths since the only effect is that some packages may be unnecessarily compiled
311         //    without user profiles.
312 
313         return new DexoptParams.Builder(convertToArtServiceDexoptReason(mCompilationReason), flags)
314                 .setCompilerFilter(mCompilerFilter)
315                 .setPriorityClass(priority)
316                 .build();
317     }
318 }
319