• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.car.watchdog;
18 
19 import static com.android.car.watchdog.WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
20 import static com.android.car.watchdog.WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
21 
22 import android.automotive.watchdog.PerStateBytes;
23 import android.automotive.watchdog.internal.ApplicationCategoryType;
24 import android.automotive.watchdog.internal.ComponentType;
25 import android.automotive.watchdog.internal.IoOveruseConfiguration;
26 import android.automotive.watchdog.internal.PackageMetadata;
27 import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
28 import android.automotive.watchdog.internal.ResourceOveruseConfiguration;
29 import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
30 import android.car.builtin.util.Slogf;
31 import android.util.ArrayMap;
32 import android.util.ArraySet;
33 import android.util.SparseArray;
34 
35 import com.android.car.internal.util.IndentingPrintWriter;
36 import com.android.internal.annotations.GuardedBy;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.function.BiFunction;
42 
43 /**
44  * Cache to store overuse configurations in memory.
45  *
46  * <p>It assumes that the error checking and loading/merging initial configs are done prior to
47  * setting the cache.
48  */
49 public final class OveruseConfigurationCache {
50     private static final String TAG = OveruseConfigurationCache.class.getSimpleName();
51     static final PerStateBytes DEFAULT_THRESHOLD =
52             constructPerStateBytes(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);
53 
54     private final Object mLock = new Object();
55     @GuardedBy("mLock")
56     private final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>();
57     @GuardedBy("mLock")
58     private final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>();
59     @GuardedBy("mLock")
60     private final List<String> mVendorPackagePrefixes = new ArrayList<>();
61     @GuardedBy("mLock")
62     private final SparseArray<ArraySet<String>> mPackagesByAppCategoryType = new SparseArray<>();
63     @GuardedBy("mLock")
64     private final SparseArray<PerStateBytes> mGenericIoThresholdsByComponent = new SparseArray<>();
65     @GuardedBy("mLock")
66     private final ArrayMap<String, PerStateBytes> mIoThresholdsBySystemPackages = new ArrayMap<>();
67     @GuardedBy("mLock")
68     private final ArrayMap<String, PerStateBytes> mIoThresholdsByVendorPackages = new ArrayMap<>();
69     @GuardedBy("mLock")
70     private final SparseArray<PerStateBytes> mIoThresholdsByAppCategoryType = new SparseArray<>();
71 
72     /** Dumps the contents of the cache. */
dump(IndentingPrintWriter writer)73     public void dump(IndentingPrintWriter writer) {
74         writer.println("*" + getClass().getSimpleName() + "*");
75         writer.increaseIndent();
76         synchronized (mLock) {
77             writer.println("mSafeToKillSystemPackages: " + mSafeToKillSystemPackages);
78             writer.println("mSafeToKillVendorPackages: " + mSafeToKillVendorPackages);
79             writer.println("mVendorPackagePrefixes: " + mVendorPackagePrefixes);
80             writer.println("mPackagesByAppCategoryType: ");
81             writer.increaseIndent();
82             for (int i = 0; i < mPackagesByAppCategoryType.size(); ++i) {
83                 writer.print("App category: "
84                         + toApplicationCategoryTypeString(mPackagesByAppCategoryType.keyAt(i)));
85                 writer.println(", Packages: " + mPackagesByAppCategoryType.valueAt(i));
86             }
87             writer.decreaseIndent();
88             writer.println("mGenericIoThresholdsByComponent: ");
89             writer.increaseIndent();
90             for (int i = 0; i < mGenericIoThresholdsByComponent.size(); ++i) {
91                 writer.print("Component type: "
92                         + toComponentTypeString(mGenericIoThresholdsByComponent.keyAt(i)));
93                 writer.print(", Threshold: ");
94                 dumpPerStateBytes(mGenericIoThresholdsByComponent.valueAt(i), writer);
95             }
96             writer.decreaseIndent();
97             writer.println("mIoThresholdsBySystemPackages: ");
98             writer.increaseIndent();
99             for (int i = 0; i < mIoThresholdsBySystemPackages.size(); ++i) {
100                 writer.print("Package name: " + mIoThresholdsBySystemPackages.keyAt(i));
101                 writer.print(", Threshold: ");
102                 dumpPerStateBytes(mIoThresholdsBySystemPackages.valueAt(i), writer);
103             }
104             writer.decreaseIndent();
105             writer.println("mIoThresholdsByVendorPackages: ");
106             writer.increaseIndent();
107             for (int i = 0; i < mIoThresholdsByVendorPackages.size(); ++i) {
108                 writer.print("Package name: " + mIoThresholdsByVendorPackages.keyAt(i));
109                 writer.print(", Threshold: ");
110                 dumpPerStateBytes(mIoThresholdsByVendorPackages.valueAt(i), writer);
111             }
112             writer.decreaseIndent();
113             writer.println("mIoThresholdsByAppCategoryType: ");
114             writer.increaseIndent();
115             for (int i = 0; i < mIoThresholdsByAppCategoryType.size(); ++i) {
116                 writer.print("App category: "
117                         + toApplicationCategoryTypeString(mIoThresholdsByAppCategoryType.keyAt(i)));
118                 writer.print(", Threshold: ");
119                 dumpPerStateBytes(mIoThresholdsByAppCategoryType.valueAt(i), writer);
120             }
121             writer.decreaseIndent();
122         }
123         writer.decreaseIndent();
124     }
125 
126     /** Overwrites the configurations in the cache. */
set(List<ResourceOveruseConfiguration> configs)127     public void set(List<ResourceOveruseConfiguration> configs) {
128         synchronized (mLock) {
129             clearLocked();
130             for (int i = 0; i < configs.size(); i++) {
131                 ResourceOveruseConfiguration config = configs.get(i);
132                 switch (config.componentType) {
133                     case ComponentType.SYSTEM:
134                         mSafeToKillSystemPackages.addAll(config.safeToKillPackages);
135                         break;
136                     case ComponentType.VENDOR:
137                         mSafeToKillVendorPackages.addAll(config.safeToKillPackages);
138                         mVendorPackagePrefixes.addAll(config.vendorPackagePrefixes);
139                         for (int j = 0; j < config.packageMetadata.size(); ++j) {
140                             PackageMetadata meta = config.packageMetadata.get(j);
141                             ArraySet<String> packages =
142                                     mPackagesByAppCategoryType.get(meta.appCategoryType);
143                             if (packages == null) {
144                                 packages = new ArraySet<>();
145                             }
146                             packages.add(meta.packageName);
147                             mPackagesByAppCategoryType.append(meta.appCategoryType, packages);
148                         }
149                         break;
150                     default:
151                         // All third-party apps are killable.
152                         break;
153                 }
154                 for (int j = 0; j < config.resourceSpecificConfigurations.size(); ++j) {
155                     if (config.resourceSpecificConfigurations.get(j).getTag()
156                             == ResourceSpecificConfiguration.ioOveruseConfiguration) {
157                         setIoThresholdsLocked(config.componentType,
158                                 config.resourceSpecificConfigurations.get(j)
159                                         .getIoOveruseConfiguration());
160                     }
161                 }
162             }
163         }
164     }
165 
166     /** Returns the threshold for the given package and component type. */
fetchThreshold(String genericPackageName, @ComponentType int componentType)167     public PerStateBytes fetchThreshold(String genericPackageName,
168             @ComponentType int componentType) {
169         synchronized (mLock) {
170             PerStateBytes threshold = null;
171             switch (componentType) {
172                 case ComponentType.SYSTEM:
173                     threshold = mIoThresholdsBySystemPackages.get(genericPackageName);
174                     if (threshold != null) {
175                         return copyPerStateBytes(threshold);
176                     }
177                     break;
178                 case ComponentType.VENDOR:
179                     threshold = mIoThresholdsByVendorPackages.get(genericPackageName);
180                     if (threshold != null) {
181                         return copyPerStateBytes(threshold);
182                     }
183                     break;
184                 default:
185                     // THIRD_PARTY and UNKNOWN components are set with the
186                     // default thresholds.
187                     break;
188             }
189             threshold = fetchAppCategorySpecificThresholdLocked(genericPackageName);
190             if (threshold != null) {
191                 return copyPerStateBytes(threshold);
192             }
193             threshold = mGenericIoThresholdsByComponent.get(componentType);
194             return threshold != null ? copyPerStateBytes(threshold)
195                     : copyPerStateBytes(DEFAULT_THRESHOLD);
196         }
197     }
198 
199     /** Returns whether or not the given package is safe-to-kill on resource overuse. */
isSafeToKill(String genericPackageName, @ComponentType int componentType, List<String> sharedPackages)200     public boolean isSafeToKill(String genericPackageName, @ComponentType int componentType,
201             List<String> sharedPackages) {
202         synchronized (mLock) {
203             BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
204                     (packages, safeToKillPackages) -> {
205                         if (packages == null) {
206                             return false;
207                         }
208                         for (int i = 0; i < packages.size(); i++) {
209                             if (safeToKillPackages.contains(packages.get(i))) {
210                                 return true;
211                             }
212                         }
213                         return false;
214                     };
215 
216             switch (componentType) {
217                 case ComponentType.SYSTEM:
218                     if (mSafeToKillSystemPackages.contains(genericPackageName)) {
219                         return true;
220                     }
221                     return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
222                 case ComponentType.VENDOR:
223                     if (mSafeToKillVendorPackages.contains(genericPackageName)) {
224                         return true;
225                     }
226                     /*
227                      * Packages under the vendor shared UID may contain system packages because when
228                      * CarWatchdogService derives the shared component type it attributes system
229                      * packages as vendor packages when there is at least one vendor package.
230                      */
231                     return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
232                             || isSafeToKillAnyPackage.apply(sharedPackages,
233                             mSafeToKillVendorPackages);
234                 default:
235                     // Third-party apps are always killable
236                     return true;
237             }
238         }
239     }
240 
241     /** Returns the list of vendor package prefixes. */
getVendorPackagePrefixes()242     public List<String> getVendorPackagePrefixes() {
243         synchronized (mLock) {
244             return new ArrayList<>(mVendorPackagePrefixes);
245         }
246     }
247 
248     @GuardedBy("mLock")
clearLocked()249     private void clearLocked() {
250         mSafeToKillSystemPackages.clear();
251         mSafeToKillVendorPackages.clear();
252         mVendorPackagePrefixes.clear();
253         mPackagesByAppCategoryType.clear();
254         mGenericIoThresholdsByComponent.clear();
255         mIoThresholdsBySystemPackages.clear();
256         mIoThresholdsByVendorPackages.clear();
257         mIoThresholdsByAppCategoryType.clear();
258     }
259 
260     @GuardedBy("mLock")
setIoThresholdsLocked(int componentType, IoOveruseConfiguration ioConfig)261     private void setIoThresholdsLocked(int componentType, IoOveruseConfiguration ioConfig) {
262         mGenericIoThresholdsByComponent.append(componentType,
263                 ioConfig.componentLevelThresholds.perStateWriteBytes);
264         switch (componentType) {
265             case ComponentType.SYSTEM:
266                 populateThresholdsByPackagesLocked(
267                         ioConfig.packageSpecificThresholds, mIoThresholdsBySystemPackages);
268                 break;
269             case ComponentType.VENDOR:
270                 populateThresholdsByPackagesLocked(
271                         ioConfig.packageSpecificThresholds, mIoThresholdsByVendorPackages);
272                 setIoThresholdsByAppCategoryTypeLocked(ioConfig.categorySpecificThresholds);
273                 break;
274             default:
275                 Slogf.i(TAG, "Ignoring I/O overuse threshold for invalid component type: %d",
276                         componentType);
277         }
278     }
279 
280     @GuardedBy("mLock")
setIoThresholdsByAppCategoryTypeLocked( List<PerStateIoOveruseThreshold> thresholds)281     private void setIoThresholdsByAppCategoryTypeLocked(
282             List<PerStateIoOveruseThreshold> thresholds) {
283         for (int i = 0; i < thresholds.size(); ++i) {
284             PerStateIoOveruseThreshold threshold = thresholds.get(i);
285             switch(threshold.name) {
286                 case INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS:
287                     mIoThresholdsByAppCategoryType.append(
288                             ApplicationCategoryType.MAPS, threshold.perStateWriteBytes);
289                     break;
290                 case INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA:
291                     mIoThresholdsByAppCategoryType.append(ApplicationCategoryType.MEDIA,
292                             threshold.perStateWriteBytes);
293                     break;
294                 default:
295                     Slogf.i(TAG,
296                             "Ignoring I/O overuse threshold for invalid application category: %s",
297                             threshold.name);
298             }
299         }
300     }
301 
302     @GuardedBy("mLock")
populateThresholdsByPackagesLocked(List<PerStateIoOveruseThreshold> thresholds, ArrayMap<String, PerStateBytes> thresholdsByPackages)303     private void populateThresholdsByPackagesLocked(List<PerStateIoOveruseThreshold> thresholds,
304             ArrayMap<String, PerStateBytes> thresholdsByPackages) {
305         for (int i = 0; i < thresholds.size(); ++i) {
306             thresholdsByPackages.put(
307                     thresholds.get(i).name, thresholds.get(i).perStateWriteBytes);
308         }
309     }
310 
311     @GuardedBy("mLock")
fetchAppCategorySpecificThresholdLocked(String genericPackageName)312     private PerStateBytes fetchAppCategorySpecificThresholdLocked(String genericPackageName) {
313         for (int i = 0; i < mPackagesByAppCategoryType.size(); ++i) {
314             if (mPackagesByAppCategoryType.valueAt(i).contains(genericPackageName)) {
315                 return mIoThresholdsByAppCategoryType.get(mPackagesByAppCategoryType.keyAt(i));
316             }
317         }
318         return null;
319     }
320 
toApplicationCategoryTypeString(@pplicationCategoryType int type)321     private static String toApplicationCategoryTypeString(@ApplicationCategoryType int type) {
322         switch (type) {
323             case ApplicationCategoryType.MAPS:
324                 return "ApplicationCategoryType.MAPS";
325             case ApplicationCategoryType.MEDIA:
326                 return "ApplicationCategoryType.MEDIA";
327             case ApplicationCategoryType.OTHERS:
328                 return "ApplicationCategoryType.OTHERS";
329             default:
330                 return "Invalid ApplicationCategoryType";
331         }
332     }
333 
toComponentTypeString(@omponentType int type)334     private static String toComponentTypeString(@ComponentType int type) {
335         switch (type) {
336             case ComponentType.SYSTEM:
337                 return "ComponentType.SYSTEM";
338             case ComponentType.VENDOR:
339                 return "ComponentType.VENDOR";
340             case ComponentType.THIRD_PARTY:
341                 return "ComponentType.THIRD_PARTY";
342             default:
343                 return "ComponentType.UNKNOWN";
344         }
345     }
346 
dumpPerStateBytes(PerStateBytes perStateBytes, IndentingPrintWriter writer)347     private static void dumpPerStateBytes(PerStateBytes perStateBytes,
348             IndentingPrintWriter writer) {
349         if (perStateBytes == null) {
350             writer.println("{NULL}");
351             return;
352         }
353         writer.println("{Foreground bytes: " + perStateBytes.foregroundBytes
354                 + ", Background bytes: " + perStateBytes.backgroundBytes + ", Garage mode bytes: "
355                 + perStateBytes.garageModeBytes + '}');
356     }
357 
constructPerStateBytes(long fgBytes, long bgBytes, long gmBytes)358     private static PerStateBytes constructPerStateBytes(long fgBytes, long bgBytes, long gmBytes) {
359         return new PerStateBytes() {{
360                 foregroundBytes = fgBytes;
361                 backgroundBytes = bgBytes;
362                 garageModeBytes = gmBytes;
363             }};
364     }
365 
366     private static PerStateBytes copyPerStateBytes(PerStateBytes perStateBytes) {
367         return new PerStateBytes() {{
368                 foregroundBytes = perStateBytes.foregroundBytes;
369                 backgroundBytes = perStateBytes.backgroundBytes;
370                 garageModeBytes = perStateBytes.garageModeBytes;
371             }};
372     }
373 }
374