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