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 public class PackageManagerServiceCompilerMapping {
27     // Names for compilation reasons.
28     public static final String REASON_STRINGS[] = {
29             "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
30     };
31 
32     static final int REASON_SHARED_INDEX = 6;
33 
34     // Static block to ensure the strings array is of the right length.
35     static {
36         if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
37             throw new IllegalStateException("REASON_STRINGS not correct");
38         }
39         if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
40             throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
41         }
42     }
43 
getSystemPropertyName(int reason)44     private static String getSystemPropertyName(int reason) {
45         if (reason < 0 || reason >= REASON_STRINGS.length) {
46             throw new IllegalArgumentException("reason " + reason + " invalid");
47         }
48 
49         return "pm.dexopt." + REASON_STRINGS[reason];
50     }
51 
52     // Load the property for the given reason and check for validity. This will throw an
53     // exception in case the reason or value are invalid.
getAndCheckValidity(int reason)54     private static String getAndCheckValidity(int reason) {
55         String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
56         if (sysPropValue == null || sysPropValue.isEmpty() ||
57                 !DexFile.isValidCompilerFilter(sysPropValue)) {
58             throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
59                     + "(reason " + REASON_STRINGS[reason] + ")");
60         } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
61             throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
62                     + "(reason " + REASON_STRINGS[reason] + ")");
63         }
64 
65         return sysPropValue;
66     }
67 
isFilterAllowedForReason(int reason, String filter)68     private static boolean isFilterAllowedForReason(int reason, String filter) {
69         return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
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 || sysPropName.isEmpty()) {
84                     throw new IllegalStateException("Reason system property name \"" +
85                             sysPropName +"\" for reason " + REASON_STRINGS[reason]);
86                 }
87 
88                 // Check validity, ignore result.
89                 getAndCheckValidity(reason);
90             } catch (Exception exc) {
91                 if (toThrow == null) {
92                     toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
93                 }
94                 toThrow.addSuppressed(exc);
95             }
96         }
97 
98         if (toThrow != null) {
99             throw toThrow;
100         }
101     }
102 
getCompilerFilterForReason(int reason)103     public static String getCompilerFilterForReason(int reason) {
104         return getAndCheckValidity(reason);
105     }
106 
107     /**
108      * Return the default compiler filter for compilation.
109      *
110      * We derive that from the traditional "dalvik.vm.dex2oat-filter" property and just make
111      * sure this isn't profile-guided. Returns "speed" in case of invalid (or missing) values.
112      */
getDefaultCompilerFilter()113     public static String getDefaultCompilerFilter() {
114         String value = SystemProperties.get("dalvik.vm.dex2oat-filter");
115         if (value == null || value.isEmpty()) {
116             return "speed";
117         }
118 
119         if (!DexFile.isValidCompilerFilter(value) ||
120                 DexFile.isProfileGuidedCompilerFilter(value)) {
121             return "speed";
122         }
123 
124         return value;
125     }
126 
getReasonName(int reason)127     public static String getReasonName(int reason) {
128         if (reason == PackageManagerService.REASON_UNKNOWN) {
129             return "unknown";
130         }
131         if (reason < 0 || reason >= REASON_STRINGS.length) {
132             throw new IllegalArgumentException("reason " + reason + " invalid");
133         }
134         return REASON_STRINGS[reason];
135     }
136 }
137