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