1 /*
2  * Copyright (C) 2016 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;
18 
19 import android.os.SystemProperties;
20 
21 import dalvik.system.DexFile;
22 
23 /**
24  * Manage (retrieve) mappings from compilation reason to compilation filter.
25  */
26 class PackageManagerServiceCompilerMapping {
27     // Names for compilation reasons.
28     static final String REASON_STRINGS[] = {
29             "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk",
30             "forced-dexopt", "core-app"
31     };
32 
33     // Static block to ensure the strings array is of the right length.
34     static {
35         if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
36             throw new IllegalStateException("REASON_STRINGS not correct");
37         }
38     }
39 
getSystemPropertyName(int reason)40     private static String getSystemPropertyName(int reason) {
41         if (reason < 0 || reason >= REASON_STRINGS.length) {
42             throw new IllegalArgumentException("reason " + reason + " invalid");
43         }
44 
45         return "pm.dexopt." + REASON_STRINGS[reason];
46     }
47 
48     // Load the property for the given reason and check for validity. This will throw an
49     // exception in case the reason or value are invalid.
getAndCheckValidity(int reason)50     private static String getAndCheckValidity(int reason) {
51         String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
52         if (sysPropValue == null || sysPropValue.isEmpty() ||
53                 !DexFile.isValidCompilerFilter(sysPropValue)) {
54             throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
55                     + "(reason " + REASON_STRINGS[reason] + ")");
56         }
57 
58         // Ensure that some reasons are not mapped to profile-guided filters.
59         switch (reason) {
60             case PackageManagerService.REASON_SHARED_APK:
61             case PackageManagerService.REASON_FORCED_DEXOPT:
62                 if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
63                     throw new IllegalStateException("\"" + sysPropValue + "\" is profile-guided, "
64                             + "but not allowed for " + REASON_STRINGS[reason]);
65                 }
66                 break;
67         }
68 
69         return sysPropValue;
70     }
71 
72     // Check that the properties are set and valid.
73     // Note: this is done in a separate method so this class can be statically initialized.
checkProperties()74     static void checkProperties() {
75         // We're gonna check all properties and collect the exceptions, so we can give a general
76         // overview. Store the exceptions here.
77         RuntimeException toThrow = null;
78 
79         for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
80             try {
81                 // Check that the system property name is legal.
82                 String sysPropName = getSystemPropertyName(reason);
83                 if (sysPropName == null ||
84                         sysPropName.isEmpty() ||
85                         sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
86                     throw new IllegalStateException("Reason system property name \"" +
87                             sysPropName +"\" for reason " + REASON_STRINGS[reason]);
88                 }
89 
90                 // Check validity, ignore result.
91                 getAndCheckValidity(reason);
92             } catch (Exception exc) {
93                 if (toThrow == null) {
94                     toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
95                 }
96                 toThrow.addSuppressed(exc);
97             }
98         }
99 
100         if (toThrow != null) {
101             throw toThrow;
102         }
103     }
104 
getCompilerFilterForReason(int reason)105     public static String getCompilerFilterForReason(int reason) {
106         return getAndCheckValidity(reason);
107     }
108 
109     /**
110      * Return the compiler filter for "full" compilation.
111      *
112      * We derive that from the traditional "dalvik.vm.dex2oat-filter" property and just make
113      * sure this isn't profile-guided. Returns "speed" in case of invalid (or missing) values.
114      */
getFullCompilerFilter()115     public static String getFullCompilerFilter() {
116         String value = SystemProperties.get("dalvik.vm.dex2oat-filter");
117         if (value == null || value.isEmpty()) {
118             return "speed";
119         }
120 
121         if (!DexFile.isValidCompilerFilter(value) ||
122                 DexFile.isProfileGuidedCompilerFilter(value)) {
123             return "speed";
124         }
125 
126         return value;
127     }
128 
129     /**
130      * Return the non-profile-guided filter corresponding to the given filter.
131      */
getNonProfileGuidedCompilerFilter(String filter)132     public static String getNonProfileGuidedCompilerFilter(String filter) {
133         return DexFile.getNonProfileGuidedCompilerFilter(filter);
134     }
135 }
136