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