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
17package com.android.server.pm;
18
19import android.os.SystemProperties;
20
21import dalvik.system.DexFile;
22
23/**
24 * Manage (retrieve) mappings from compilation reason to compilation filter.
25 */
26class 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
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.
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.
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
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     */
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     */
132    public static String getNonProfileGuidedCompilerFilter(String filter) {
133        return DexFile.getNonProfileGuidedCompilerFilter(filter);
134    }
135}
136