1 /*
2  * Copyright (C) 2022 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.art;
18 
19 import static com.android.server.art.model.ArtFlags.PriorityClassApi;
20 
21 import android.annotation.NonNull;
22 import android.annotation.StringDef;
23 import android.annotation.SystemApi;
24 import android.os.Build;
25 import android.os.SystemProperties;
26 import android.text.TextUtils;
27 
28 import androidx.annotation.RequiresApi;
29 
30 import com.android.server.art.model.ArtFlags;
31 import com.android.server.pm.PackageManagerLocal;
32 
33 import dalvik.system.DexFile;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.Set;
38 
39 /**
40  * Maps a compilation reason to a compiler filter and a priority class.
41  *
42  * @hide
43  */
44 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
45 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
46 public class ReasonMapping {
ReasonMapping()47     private ReasonMapping() {}
48 
49     // Keep this in sync with `ArtShellCommand.printHelp` except for 'inactive'.
50 
51     /** Dexopting apps on the first boot after flashing or factory resetting the device. */
52     public static final String REASON_FIRST_BOOT = "first-boot";
53     /** Dexopting apps on the next boot after an OTA. */
54     public static final String REASON_BOOT_AFTER_OTA = "boot-after-ota";
55     /** Dexopting apps on the next boot after a mainline update. */
56     public static final String REASON_BOOT_AFTER_MAINLINE_UPDATE = "boot-after-mainline-update";
57     /** Installing an app after user presses the "install"/"update" button. */
58     public static final String REASON_INSTALL = "install";
59     /** Dexopting apps in the background. */
60     public static final String REASON_BG_DEXOPT = "bg-dexopt";
61     /** Invoked by cmdline. */
62     public static final String REASON_CMDLINE = "cmdline";
63     /** Downgrading the compiler filter when an app is not used for a long time. */
64     public static final String REASON_INACTIVE = "inactive";
65     /** @hide */
66     public static final String REASON_PRE_REBOOT_DEXOPT = "ab-ota";
67 
68     // Reasons for Play Install Hints (go/install-hints).
69     public static final String REASON_INSTALL_FAST = "install-fast";
70     public static final String REASON_INSTALL_BULK = "install-bulk";
71     public static final String REASON_INSTALL_BULK_SECONDARY = "install-bulk-secondary";
72     public static final String REASON_INSTALL_BULK_DOWNGRADED = "install-bulk-downgraded";
73     public static final String REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
74             "install-bulk-secondary-downgraded";
75 
76     /** @hide */
77     public static final Set<String> REASONS_FOR_INSTALL = Set.of(REASON_INSTALL,
78             REASON_INSTALL_FAST, REASON_INSTALL_BULK, REASON_INSTALL_BULK_SECONDARY,
79             REASON_INSTALL_BULK_DOWNGRADED, REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
80 
81     // Keep this in sync with `ArtShellCommand.printHelp`.
82     /** @hide */
83     public static final Set<String> BATCH_DEXOPT_REASONS =
84             Set.of(REASON_FIRST_BOOT, REASON_BOOT_AFTER_OTA, REASON_BOOT_AFTER_MAINLINE_UPDATE,
85                     REASON_BG_DEXOPT, REASON_PRE_REBOOT_DEXOPT);
86 
87     /** @hide */
88     public static final Set<String> BOOT_REASONS =
89             Set.of(REASON_FIRST_BOOT, REASON_BOOT_AFTER_OTA, REASON_BOOT_AFTER_MAINLINE_UPDATE);
90 
91     /**
92      * Reasons for {@link ArtManagerLocal#dexoptPackages}.
93      *
94      * @hide
95      */
96     // clang-format off
97     @StringDef(prefix = "REASON_", value = {
98         REASON_FIRST_BOOT,
99         REASON_BOOT_AFTER_OTA,
100         REASON_BOOT_AFTER_MAINLINE_UPDATE,
101         REASON_BG_DEXOPT,
102         REASON_PRE_REBOOT_DEXOPT,
103     })
104     // clang-format on
105     @Retention(RetentionPolicy.SOURCE)
106     public @interface BatchDexoptReason {}
107 
108     /**
109      * Reasons for {@link ArtManagerLocal#onBoot(String, Executor, Consumer<OperationProgress>)}.
110      *
111      * @hide
112      */
113     // clang-format off
114     @StringDef(prefix = "REASON_", value = {
115         REASON_FIRST_BOOT,
116         REASON_BOOT_AFTER_OTA,
117         REASON_BOOT_AFTER_MAINLINE_UPDATE,
118     })
119     // clang-format on
120     @Retention(RetentionPolicy.SOURCE)
121     public @interface BootReason {}
122 
123     /**
124      * Loads the compiler filter from the system property for the given reason and checks for
125      * validity.
126      *
127      * @throws IllegalArgumentException if the reason is invalid
128      * @throws IllegalStateException if the system property value is invalid
129      *
130      * @hide
131      */
132     @NonNull
getCompilerFilterForReason(@onNull String reason)133     public static String getCompilerFilterForReason(@NonNull String reason) {
134         String value = SystemProperties.get("pm.dexopt." + reason);
135         if (TextUtils.isEmpty(value)) {
136             throw new IllegalArgumentException("No compiler filter for reason '" + reason + "'");
137         }
138         if (!Utils.isValidArtServiceCompilerFilter(value)) {
139             throw new IllegalStateException(
140                     "Got invalid compiler filter '" + value + "' for reason '" + reason + "'");
141         }
142         return value;
143     }
144 
145     /**
146      * Loads the compiler filter from the system property for:
147      * - shared libraries
148      * - apps used by other apps without a dex metadata file
149      *
150      * @throws IllegalStateException if the system property value is invalid
151      *
152      * @hide
153      */
154     @NonNull
getCompilerFilterForShared()155     public static String getCompilerFilterForShared() {
156         // "shared" is technically not a compilation reason, but the compiler filter is defined as a
157         // system property as if "shared" is a reason.
158         String value = getCompilerFilterForReason("shared");
159         if (DexFile.isProfileGuidedCompilerFilter(value)) {
160             throw new IllegalStateException(
161                     "Compiler filter for 'shared' must not be profile guided, got '" + value + "'");
162         }
163         return value;
164     }
165 
166     /**
167      * Returns the priority for the given reason.
168      *
169      * @throws IllegalArgumentException if the reason is invalid
170      * @see PriorityClassApi
171      *
172      * @hide
173      */
getPriorityClassForReason(@onNull String reason)174     public static @PriorityClassApi byte getPriorityClassForReason(@NonNull String reason) {
175         switch (reason) {
176             case REASON_FIRST_BOOT:
177             case REASON_BOOT_AFTER_OTA:
178             case REASON_BOOT_AFTER_MAINLINE_UPDATE:
179                 return ArtFlags.PRIORITY_BOOT;
180             case REASON_INSTALL_FAST:
181                 return ArtFlags.PRIORITY_INTERACTIVE_FAST;
182             case REASON_INSTALL:
183             case REASON_CMDLINE:
184                 return ArtFlags.PRIORITY_INTERACTIVE;
185             case REASON_BG_DEXOPT:
186             case REASON_PRE_REBOOT_DEXOPT:
187             case REASON_INACTIVE:
188             case REASON_INSTALL_BULK:
189             case REASON_INSTALL_BULK_SECONDARY:
190             case REASON_INSTALL_BULK_DOWNGRADED:
191             case REASON_INSTALL_BULK_SECONDARY_DOWNGRADED:
192                 return ArtFlags.PRIORITY_BACKGROUND;
193             default:
194                 throw new IllegalArgumentException("No priority class for reason '" + reason + "'");
195         }
196     }
197 
198     /**
199      * Loads the concurrency from the system property, for batch dexopt ({@link
200      * ArtManagerLocal#dexoptPackages}). The default is tuned to strike a good balance between
201      * device load and dexopt coverage, depending on the situation.
202      *
203      * @hide
204      */
getConcurrencyForReason(@onNull @atchDexoptReason String reason)205     public static int getConcurrencyForReason(@NonNull @BatchDexoptReason String reason) {
206         // TODO(jiakaiz): Revisit the concurrency for non-boot reasons.
207         return SystemProperties.getInt("pm.dexopt." + reason + ".concurrency",
208                 BOOT_REASONS.contains(reason) ? 4 : 1 /* def */);
209     }
210 }
211