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 android.app.StatsManager.PULL_SKIP; 20 import static android.app.StatsManager.PULL_SUCCESS; 21 import static android.car.builtin.os.UserManagerHelper.USER_NULL; 22 import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE; 23 import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO; 24 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY; 25 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_15_DAYS; 26 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_30_DAYS; 27 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_3_DAYS; 28 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS; 29 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER; 30 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO; 31 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES; 32 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; 33 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 34 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 35 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 36 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 37 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; 38 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 39 import static android.os.Process.INVALID_UID; 40 import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; 41 42 import static com.android.car.CarStatsLog.CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED; 43 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED; 44 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE; 45 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE; 46 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_INTERACTION_MODE; 47 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE; 48 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE; 49 import static com.android.car.CarStatsLog.CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY; 50 import static com.android.car.CarStatsLog.CAR_WATCHDOG_UID_IO_USAGE_SUMMARY; 51 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 52 import static com.android.car.util.Utils.getContentResolverForUser; 53 import static com.android.car.watchdog.CarWatchdogService.ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION; 54 import static com.android.car.watchdog.CarWatchdogService.ACTION_LAUNCH_APP_SETTINGS; 55 import static com.android.car.watchdog.CarWatchdogService.ACTION_RESOURCE_OVERUSE_DISABLE_APP; 56 import static com.android.car.watchdog.CarWatchdogService.DEBUG; 57 import static com.android.car.watchdog.CarWatchdogService.TAG; 58 import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX; 59 import static com.android.car.watchdog.TimeSource.ZONE_OFFSET; 60 import static com.android.car.watchdog.WatchdogStorage.RETENTION_PERIOD; 61 62 import android.annotation.IntDef; 63 import android.annotation.NonNull; 64 import android.annotation.Nullable; 65 import android.annotation.UserIdInt; 66 import android.app.ActivityManager; 67 import android.app.StatsManager; 68 import android.app.StatsManager.PullAtomMetadata; 69 import android.automotive.watchdog.internal.ApplicationCategoryType; 70 import android.automotive.watchdog.internal.ComponentType; 71 import android.automotive.watchdog.internal.GarageMode; 72 import android.automotive.watchdog.internal.IoUsageStats; 73 import android.automotive.watchdog.internal.PackageIoOveruseStats; 74 import android.automotive.watchdog.internal.PackageMetadata; 75 import android.automotive.watchdog.internal.PerStateIoOveruseThreshold; 76 import android.automotive.watchdog.internal.ResourceSpecificConfiguration; 77 import android.automotive.watchdog.internal.UserPackageIoUsageStats; 78 import android.car.builtin.content.pm.PackageManagerHelper; 79 import android.car.builtin.util.Slogf; 80 import android.car.drivingstate.CarUxRestrictions; 81 import android.car.drivingstate.ICarUxRestrictionsChangeListener; 82 import android.car.watchdog.CarWatchdogManager; 83 import android.car.watchdog.IResourceOveruseListener; 84 import android.car.watchdog.IoOveruseAlertThreshold; 85 import android.car.watchdog.IoOveruseConfiguration; 86 import android.car.watchdog.IoOveruseStats; 87 import android.car.watchdog.PackageKillableState; 88 import android.car.watchdog.PackageKillableState.KillableState; 89 import android.car.watchdog.PerStateBytes; 90 import android.car.watchdog.ResourceOveruseConfiguration; 91 import android.car.watchdog.ResourceOveruseStats; 92 import android.car.watchdoglib.CarWatchdogDaemonHelper; 93 import android.content.ContentResolver; 94 import android.content.Context; 95 import android.content.Intent; 96 import android.content.pm.ApplicationInfo; 97 import android.content.pm.PackageInfo; 98 import android.content.pm.PackageManager; 99 import android.content.res.Resources; 100 import android.net.Uri; 101 import android.os.Binder; 102 import android.os.Handler; 103 import android.os.IBinder; 104 import android.os.Looper; 105 import android.os.RemoteException; 106 import android.os.SystemClock; 107 import android.os.TransactionTooLargeException; 108 import android.os.UserHandle; 109 import android.os.UserManager; 110 import android.provider.Settings; 111 import android.text.TextUtils; 112 import android.util.ArrayMap; 113 import android.util.ArraySet; 114 import android.util.AtomicFile; 115 import android.util.JsonReader; 116 import android.util.JsonWriter; 117 import android.util.Pair; 118 import android.util.SparseArray; 119 import android.util.StatsEvent; 120 import android.view.Display; 121 122 import com.android.car.BuiltinPackageDependency; 123 import com.android.car.CarLocalServices; 124 import com.android.car.CarServiceUtils; 125 import com.android.car.CarStatsLog; 126 import com.android.car.CarUxRestrictionsManagerService; 127 import com.android.car.R; 128 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 129 import com.android.car.internal.NotificationHelperBase; 130 import com.android.car.internal.util.ConcurrentUtils; 131 import com.android.car.internal.util.IndentingPrintWriter; 132 import com.android.internal.annotations.GuardedBy; 133 import com.android.internal.annotations.VisibleForTesting; 134 import com.android.internal.util.Preconditions; 135 136 import java.io.File; 137 import java.io.FileInputStream; 138 import java.io.FileOutputStream; 139 import java.io.IOException; 140 import java.io.InputStreamReader; 141 import java.io.OutputStreamWriter; 142 import java.lang.annotation.Retention; 143 import java.lang.annotation.RetentionPolicy; 144 import java.nio.charset.StandardCharsets; 145 import java.time.ZonedDateTime; 146 import java.time.format.DateTimeFormatter; 147 import java.time.format.DateTimeParseException; 148 import java.time.temporal.ChronoField; 149 import java.time.temporal.ChronoUnit; 150 import java.util.ArrayList; 151 import java.util.Arrays; 152 import java.util.Collections; 153 import java.util.List; 154 import java.util.Map; 155 import java.util.Objects; 156 import java.util.Set; 157 import java.util.concurrent.TimeUnit; 158 import java.util.function.BiConsumer; 159 import java.util.function.BiFunction; 160 import java.util.function.Consumer; 161 162 /** 163 * Handles system resource performance monitoring module. 164 */ 165 public final class WatchdogPerfHandler { 166 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS"; 167 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA"; 168 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN"; 169 170 static final String INTENT_EXTRA_NOTIFICATION_ID = "notification_id"; 171 static final String USER_PACKAGE_SEPARATOR = ":"; 172 static final String PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR = ";"; 173 174 private static final String METADATA_FILENAME = "metadata.json"; 175 private static final String SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE = 176 "systemIoUsageSummaryReportedDate"; 177 private static final String UID_IO_USAGE_SUMMARY_REPORTED_DATE = 178 "uidIoUsageSummaryReportedDate"; 179 private static final long OVERUSE_HANDLING_DELAY_MILLS = 10_000; 180 private static final long MAX_WAIT_TIME_MILLS = 3_000; 181 182 private static final PullAtomMetadata PULL_ATOM_METADATA = 183 new PullAtomMetadata.Builder() 184 // Summary atoms are populated only once a week, so a longer duration is 185 // tolerable. However, the cool down duration should be smaller than a short 186 // drive, so summary atoms can be pulled with short drives. 187 .setCoolDownMillis(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.MINUTES)) 188 // When summary atoms are populated once a week, watchdog needs additional time 189 // for reading from disk/DB. 190 .setTimeoutMillis(10_000) 191 .build(); 192 193 /** 194 * Don't distract the user by sending user notifications/dialogs, killing foreground 195 * applications, repeatedly killing persistent background services, or disabling any 196 * application. 197 */ 198 private static final int UX_STATE_NO_DISTRACTION = 1; 199 /** The user can safely receive user notifications or dialogs. */ 200 private static final int UX_STATE_USER_NOTIFICATION = 2; 201 /** 202 * Any application or service can be safely killed/disabled. User notifications can be sent 203 * only to the notification center. 204 */ 205 private static final int UX_STATE_NO_INTERACTION = 3; 206 207 @Retention(RetentionPolicy.SOURCE) 208 @IntDef(prefix = {"UX_STATE_"}, value = { 209 UX_STATE_NO_DISTRACTION, 210 UX_STATE_USER_NOTIFICATION, 211 UX_STATE_NO_INTERACTION 212 }) 213 private @interface UxStateType{} 214 215 private final Context mContext; 216 /** 217 * Context of the builtin car service that hosts the permissions, resources, and external 218 * facing services required for showing notifications. 219 */ 220 private final Context mBuiltinPackageContext; 221 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper; 222 private final PackageInfoHandler mPackageInfoHandler; 223 private final Handler mMainHandler; 224 private final Handler mServiceHandler; 225 private final WatchdogStorage mWatchdogStorage; 226 private final OveruseConfigurationCache mOveruseConfigurationCache; 227 private final int mUidIoUsageSummaryTopCount; 228 private final int mIoUsageSummaryMinSystemTotalWrittenBytes; 229 private final int mRecurringOverusePeriodInDays; 230 private final int mRecurringOveruseTimes; 231 private final int mResourceOveruseNotificationBaseId; 232 private final int mResourceOveruseNotificationMaxOffset; 233 private final TimeSource mTimeSource; 234 private final Object mLock = new Object(); 235 /** 236 * Tracks user packages' resource usage. When cache is updated, call 237 * {@link WatchdogStorage#markDirty} to notify database is out of sync. 238 */ 239 @GuardedBy("mLock") 240 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>(); 241 @GuardedBy("mLock") 242 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid = 243 new SparseArray<>(); 244 @GuardedBy("mLock") 245 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> 246 mOveruseSystemListenerInfosByUid = new SparseArray<>(); 247 /** 248 * Default killable state for packages. Updated only for {@link UserHandle#ALL} user handle. 249 * When cache is updated, call {@link WatchdogStorage#markDirty} to notify database is out of 250 * sync. 251 */ 252 @GuardedBy("mLock") 253 private final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>(); 254 /** Keys in {@link mUsageByUserPackage} for user notification on resource overuse. */ 255 @GuardedBy("mLock") 256 private final ArraySet<String> mUserNotifiablePackages = new ArraySet<>(); 257 /** Values are the unique ids generated by {@code getUserPackageUniqueId}. */ 258 @GuardedBy("mLock") 259 private final SparseArray<String> mActiveUserNotificationsByNotificationId = 260 new SparseArray<>(); 261 /** Keys are the unique ids generated by {@code getUserPackageUniqueId}. */ 262 @GuardedBy("mLock") 263 private final ArraySet<String> mActiveUserNotifications = new ArraySet<>(); 264 /** 265 * Keys in {@link mUsageByUserPackage} that should be killed/disabled due to resource overuse. 266 */ 267 @GuardedBy("mLock") 268 private final ArraySet<String> mActionableUserPackages = new ArraySet<>(); 269 /** 270 * Tracks user packages disabled due to resource overuse. 271 */ 272 @GuardedBy("mLock") 273 private final SparseArray<ArraySet<String>> mDisabledUserPackagesByUserId = new SparseArray<>(); 274 @GuardedBy("mLock") 275 private ZonedDateTime mLatestStatsReportDate; 276 @GuardedBy("mLock") 277 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> 278 mPendingSetResourceOveruseConfigurationsRequest = null; 279 @GuardedBy("mLock") 280 private boolean mIsConnectedToDaemon; 281 @GuardedBy("mLock") 282 private @UxStateType int mCurrentUxState = UX_STATE_NO_DISTRACTION; 283 @GuardedBy("mLock") 284 private CarUxRestrictions mCurrentUxRestrictions; 285 @GuardedBy("mLock") 286 private boolean mIsHeadsUpNotificationSent; 287 @GuardedBy("mLock") 288 private int mCurrentOveruseNotificationIdOffset; 289 @GuardedBy("mLock") 290 private @GarageMode int mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF; 291 @GuardedBy("mLock") 292 private long mOveruseHandlingDelayMills = OVERUSE_HANDLING_DELAY_MILLS; 293 @GuardedBy("mLock") 294 private ZonedDateTime mLastSystemIoUsageSummaryReportedDate; 295 @GuardedBy("mLock") 296 private ZonedDateTime mLastUidIoUsageSummaryReportedDate; 297 298 private final ICarUxRestrictionsChangeListener mCarUxRestrictionsChangeListener = 299 new ICarUxRestrictionsChangeListener.Stub() { 300 @Override 301 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) { 302 synchronized (mLock) { 303 mCurrentUxRestrictions = new CarUxRestrictions(restrictions); 304 applyCurrentUxRestrictionsLocked(); 305 } 306 } 307 }; 308 WatchdogPerfHandler(Context context, Context builtinPackageContext, CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler, WatchdogStorage watchdogStorage, TimeSource timeSource)309 public WatchdogPerfHandler(Context context, Context builtinPackageContext, 310 CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler, 311 WatchdogStorage watchdogStorage, TimeSource timeSource) { 312 mContext = context; 313 mBuiltinPackageContext = builtinPackageContext; 314 mCarWatchdogDaemonHelper = daemonHelper; 315 mPackageInfoHandler = packageInfoHandler; 316 mMainHandler = new Handler(Looper.getMainLooper()); 317 mServiceHandler = new Handler(CarServiceUtils.getHandlerThread( 318 CarWatchdogService.class.getSimpleName()).getLooper()); 319 mWatchdogStorage = watchdogStorage; 320 mOveruseConfigurationCache = new OveruseConfigurationCache(); 321 mTimeSource = timeSource; 322 Resources resources = mContext.getResources(); 323 mUidIoUsageSummaryTopCount = resources.getInteger(R.integer.uidIoUsageSummaryTopCount); 324 mIoUsageSummaryMinSystemTotalWrittenBytes = resources 325 .getInteger(R.integer.ioUsageSummaryMinSystemTotalWrittenBytes); 326 mRecurringOverusePeriodInDays = resources 327 .getInteger(R.integer.recurringResourceOverusePeriodInDays); 328 mRecurringOveruseTimes = resources.getInteger(R.integer.recurringResourceOveruseTimes); 329 mResourceOveruseNotificationBaseId = 330 NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_BASE_ID; 331 mResourceOveruseNotificationMaxOffset = 332 NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_MAX_OFFSET; 333 } 334 335 /** Initializes the handler. */ init()336 public void init() { 337 // First database read is expensive, so post it on a separate handler thread. 338 mServiceHandler.post(() -> { 339 readFromDatabase(); 340 // Set atom pull callbacks only after the internal datastructures are updated. When the 341 // pull happens, the service is already initialized and ready to populate the pulled 342 // atoms. 343 StatsManager statsManager = mContext.getSystemService(StatsManager.class); 344 statsManager.setPullAtomCallback(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY, 345 PULL_ATOM_METADATA, ConcurrentUtils.DIRECT_EXECUTOR, this::onPullAtom); 346 statsManager.setPullAtomCallback(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY, 347 PULL_ATOM_METADATA, ConcurrentUtils.DIRECT_EXECUTOR, this::onPullAtom); 348 }); 349 350 CarUxRestrictionsManagerService carUxRestrictionsManagerService = 351 CarLocalServices.getService(CarUxRestrictionsManagerService.class); 352 CarUxRestrictions uxRestrictions = 353 carUxRestrictionsManagerService.getCurrentUxRestrictions(); 354 synchronized (mLock) { 355 mCurrentUxRestrictions = uxRestrictions; 356 applyCurrentUxRestrictionsLocked(); 357 syncDisabledUserPackagesLocked(); 358 } 359 carUxRestrictionsManagerService.registerUxRestrictionsChangeListener( 360 mCarUxRestrictionsChangeListener, Display.DEFAULT_DISPLAY); 361 362 if (DEBUG) { 363 Slogf.d(TAG, "WatchdogPerfHandler is initialized"); 364 } 365 } 366 367 /** Releases resources. */ release()368 public void release() { 369 CarLocalServices.getService(CarUxRestrictionsManagerService.class) 370 .unregisterUxRestrictionsChangeListener(mCarUxRestrictionsChangeListener); 371 } 372 373 /** Dumps its state. */ 374 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)375 public void dump(IndentingPrintWriter writer) { 376 /* 377 * TODO(b/183436216): Implement this method. 378 */ 379 synchronized (mLock) { 380 writer.println("Current UX state: " + toUxStateString(mCurrentUxState)); 381 writer.println("List of disabled packages per user due to resource overuse: " 382 + mDisabledUserPackagesByUserId); 383 } 384 mOveruseConfigurationCache.dump(writer); 385 } 386 387 /** Retries any pending requests on re-connecting to the daemon */ onDaemonConnectionChange(boolean isConnected)388 public void onDaemonConnectionChange(boolean isConnected) { 389 boolean hasPendingRequest; 390 synchronized (mLock) { 391 mIsConnectedToDaemon = isConnected; 392 hasPendingRequest = mPendingSetResourceOveruseConfigurationsRequest != null; 393 } 394 if (isConnected) { 395 if (hasPendingRequest) { 396 /* 397 * Retry pending set resource overuse configuration request before processing any 398 * new set/get requests. Thus notify the waiting requests only after the retry 399 * completes. 400 */ 401 retryPendingSetResourceOveruseConfigurations(); 402 } else { 403 /* Start fetch/sync configs only when there are no pending set requests because the 404 * above retry starts fetch/sync configs on success. If the retry fails, the daemon 405 * has crashed and shouldn't start fetchAndSyncResourceOveruseConfigurations. 406 */ 407 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations); 408 } 409 } 410 synchronized (mLock) { 411 mLock.notifyAll(); 412 } 413 } 414 415 /** Updates the current UX state based on the display state. */ onDisplayStateChanged(boolean isEnabled)416 public void onDisplayStateChanged(boolean isEnabled) { 417 synchronized (mLock) { 418 if (isEnabled) { 419 mCurrentUxState = UX_STATE_NO_DISTRACTION; 420 applyCurrentUxRestrictionsLocked(); 421 } else { 422 mCurrentUxState = UX_STATE_NO_INTERACTION; 423 performOveruseHandlingLocked(); 424 } 425 } 426 } 427 428 /** Handles garage mode change. */ onGarageModeChange(@arageMode int garageMode)429 public void onGarageModeChange(@GarageMode int garageMode) { 430 synchronized (mLock) { 431 mCurrentGarageMode = garageMode; 432 if (mCurrentGarageMode == GarageMode.GARAGE_MODE_ON) { 433 mCurrentUxState = UX_STATE_NO_INTERACTION; 434 performOveruseHandlingLocked(); 435 } 436 } 437 } 438 439 /** Returns resource overuse stats for the calling package. */ 440 @NonNull getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)441 public ResourceOveruseStats getResourceOveruseStats( 442 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 443 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 444 Preconditions.checkArgument((resourceOveruseFlag > 0), 445 "Must provide valid resource overuse flag"); 446 Preconditions.checkArgument((maxStatsPeriod > 0), 447 "Must provide valid maximum stats period"); 448 // When more resource stats are added, make this as optional. 449 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0, 450 "Must provide resource I/O overuse flag"); 451 int callingUid = Binder.getCallingUid(); 452 UserHandle callingUserHandle = Binder.getCallingUserHandle(); 453 int callingUserId = callingUserHandle.getIdentifier(); 454 String genericPackageName = 455 mPackageInfoHandler.getNamesForUids(new int[]{callingUid}) 456 .get(callingUid, null); 457 if (genericPackageName == null) { 458 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid); 459 return new ResourceOveruseStats.Builder("", callingUserHandle).build(); 460 } 461 ResourceOveruseStats.Builder statsBuilder = 462 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle); 463 statsBuilder.setIoOveruseStats( 464 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod)); 465 if (DEBUG) { 466 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and " 467 + "package '%s']", callingUid, callingUserId, genericPackageName); 468 } 469 return statsBuilder.build(); 470 } 471 472 /** Returns resource overuse stats for all packages. */ 473 @NonNull getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)474 public List<ResourceOveruseStats> getAllResourceOveruseStats( 475 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 476 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, 477 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 478 Preconditions.checkArgument((resourceOveruseFlag > 0), 479 "Must provide valid resource overuse flag"); 480 Preconditions.checkArgument((maxStatsPeriod > 0), 481 "Must provide valid maximum stats period"); 482 // When more resource types are added, make this as optional. 483 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0, 484 "Must provide resource I/O overuse flag"); 485 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag); 486 List<ResourceOveruseStats> allStats = new ArrayList<>(); 487 synchronized (mLock) { 488 for (int i = 0; i < mUsageByUserPackage.size(); ++i) { 489 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i); 490 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder(); 491 IoOveruseStats ioOveruseStats = 492 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod); 493 if (ioOveruseStats == null) { 494 continue; 495 } 496 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build()); 497 } 498 } 499 if (DEBUG) { 500 Slogf.d(TAG, "Returning all resource overuse stats"); 501 } 502 return allStats; 503 } 504 505 /** Returns resource overuse stats for the specified user package. */ 506 @NonNull getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)507 public ResourceOveruseStats getResourceOveruseStatsForUserPackage( 508 @NonNull String packageName, @NonNull UserHandle userHandle, 509 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 510 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 511 Objects.requireNonNull(packageName, "Package name must be non-null"); 512 Objects.requireNonNull(userHandle, "User handle must be non-null"); 513 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL), 514 "Must provide the user handle for a specific user"); 515 Preconditions.checkArgument((resourceOveruseFlag > 0), 516 "Must provide valid resource overuse flag"); 517 Preconditions.checkArgument((maxStatsPeriod > 0), 518 "Must provide valid maximum stats period"); 519 // When more resource types are added, make this as optional. 520 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0, 521 "Must provide resource I/O overuse flag"); 522 String genericPackageName = 523 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier()); 524 if (genericPackageName == null) { 525 throw new IllegalArgumentException("Package '" + packageName + "' not found"); 526 } 527 ResourceOveruseStats.Builder statsBuilder = 528 new ResourceOveruseStats.Builder(genericPackageName, userHandle); 529 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(), 530 genericPackageName, maxStatsPeriod)); 531 if (DEBUG) { 532 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', " 533 + "generic package '%s'", userHandle.getIdentifier(), packageName, 534 genericPackageName); 535 } 536 return statsBuilder.build(); 537 } 538 539 /** Adds the resource overuse listener. */ addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)540 public void addResourceOveruseListener( 541 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 542 @NonNull IResourceOveruseListener listener) { 543 Objects.requireNonNull(listener, "Listener must be non-null"); 544 Preconditions.checkArgument((resourceOveruseFlag > 0), 545 "Must provide valid resource overuse flag"); 546 synchronized (mLock) { 547 addResourceOveruseListenerLocked(resourceOveruseFlag, listener, 548 mOveruseListenerInfosByUid); 549 } 550 } 551 552 /** Removes the previously added resource overuse listener. */ removeResourceOveruseListener(@onNull IResourceOveruseListener listener)553 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) { 554 Objects.requireNonNull(listener, "Listener must be non-null"); 555 synchronized (mLock) { 556 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid); 557 } 558 } 559 560 /** Adds the resource overuse system listener. */ addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)561 public void addResourceOveruseListenerForSystem( 562 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 563 @NonNull IResourceOveruseListener listener) { 564 Objects.requireNonNull(listener, "Listener must be non-null"); 565 Preconditions.checkArgument((resourceOveruseFlag > 0), 566 "Must provide valid resource overuse flag"); 567 synchronized (mLock) { 568 addResourceOveruseListenerLocked(resourceOveruseFlag, listener, 569 mOveruseSystemListenerInfosByUid); 570 } 571 } 572 573 /** Removes the previously added resource overuse system listener. */ removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)574 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) { 575 Objects.requireNonNull(listener, "Listener must be non-null"); 576 synchronized (mLock) { 577 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid); 578 } 579 } 580 581 /** Sets whether or not a package is killable on resource overuse. */ setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)582 public void setKillablePackageAsUser(String packageName, UserHandle userHandle, 583 boolean isKillable) { 584 Objects.requireNonNull(packageName, "Package name must be non-null"); 585 Objects.requireNonNull(userHandle, "User handle must be non-null"); 586 587 if (userHandle.equals(UserHandle.ALL)) { 588 setPackageKillableStateForAllUsers(packageName, isKillable); 589 return; 590 } 591 int userId = userHandle.getIdentifier(); 592 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId); 593 if (genericPackageName == null) { 594 throw new IllegalArgumentException("Package '" + packageName + "' not found"); 595 } 596 String key = getUserPackageUniqueId(userId, genericPackageName); 597 PackageResourceUsage usage; 598 synchronized (mLock) { 599 // When the queried package is not cached in {@link mUsageByUserPackage}, the set API 600 // will update the killable state even when the package should never be killed. 601 // But the get API will return the correct killable state. This behavior is tolerable 602 // because in production the set API should be called only after the get API. 603 // For instance, when this case happens by mistake and the package overuses resource 604 // between the set and the get API calls, the daemon will provide correct killable 605 // state when pushing the latest stats. Ergo, the invalid killable state doesn't have 606 // any effect. 607 usage = mUsageByUserPackage.get(key); 608 if (usage == null) { 609 usage = new PackageResourceUsage(userId, genericPackageName, 610 getDefaultKillableStateLocked(genericPackageName)); 611 } 612 if (!usage.verifyAndSetKillableState(isKillable)) { 613 Slogf.e(TAG, "User %d cannot set killable state for package '%s'", 614 userHandle.getIdentifier(), genericPackageName); 615 throw new IllegalArgumentException("Package killable state is not updatable"); 616 } 617 mUsageByUserPackage.put(key, usage); 618 } 619 if (!isKillable) { 620 int uid = getOrFetchUid(usage, packageName); 621 enablePackageForUser(uid, usage.genericPackageName); 622 } 623 mWatchdogStorage.markDirty(); 624 if (DEBUG) { 625 Slogf.d(TAG, "Successfully set killable package state for user %d", userId); 626 } 627 } 628 setPackageKillableStateForAllUsers(String packageName, boolean isKillable)629 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) { 630 int[] userIds = getAliveUserIds(); 631 String genericPackageName = null; 632 List<PackageResourceUsage> updatedUsages = new ArrayList<>(userIds.length); 633 synchronized (mLock) { 634 for (int i = 0; i < userIds.length; i++) { 635 int userId = userIds[i]; 636 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId); 637 if (name == null) { 638 continue; 639 } 640 genericPackageName = name; 641 String key = getUserPackageUniqueId(userId, genericPackageName); 642 PackageResourceUsage usage = mUsageByUserPackage.get(key); 643 if (usage == null) { 644 continue; 645 } 646 if (!usage.verifyAndSetKillableState(isKillable)) { 647 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName); 648 throw new IllegalArgumentException( 649 "Package killable state is not updatable"); 650 } 651 updatedUsages.add(usage); 652 } 653 if (genericPackageName != null) { 654 if (!isKillable) { 655 mDefaultNotKillableGenericPackages.add(genericPackageName); 656 } else { 657 mDefaultNotKillableGenericPackages.remove(genericPackageName); 658 } 659 mWatchdogStorage.markDirty(); 660 } 661 } 662 // Enabling user packages requires accessing package manager which requires making binder 663 // calls. Binder calls should not be made while holding a lock, given it might lead to 664 // deadlock. Hence, enabling packages after the synchronized block. 665 if (!isKillable) { 666 for (int i = 0; i < updatedUsages.size(); i++) { 667 PackageResourceUsage usage = updatedUsages.get(i); 668 int uid = getOrFetchUid(usage, packageName); 669 enablePackageForUser(uid, usage.genericPackageName); 670 } 671 } 672 if (DEBUG) { 673 Slogf.d(TAG, "Successfully set killable package state for all users"); 674 } 675 } 676 677 /** Returns the list of package killable states on resource overuse for the user. */ 678 @NonNull getPackageKillableStatesAsUser(UserHandle userHandle)679 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) { 680 Objects.requireNonNull(userHandle, "User handle must be non-null"); 681 PackageManager pm = mContext.getPackageManager(); 682 if (!userHandle.equals(UserHandle.ALL)) { 683 if (DEBUG) { 684 Slogf.d(TAG, "Returning all package killable states for user %d", 685 userHandle.getIdentifier()); 686 } 687 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm); 688 } 689 List<PackageKillableState> packageKillableStates = new ArrayList<>(); 690 int[] userIds = getAliveUserIds(); 691 for (int i = 0; i < userIds.length; ++i) { 692 packageKillableStates.addAll( 693 getPackageKillableStatesForUserId(userIds[i], pm)); 694 } 695 if (DEBUG) { 696 Slogf.d(TAG, "Returning all package killable states for all users"); 697 } 698 return packageKillableStates; 699 } 700 getPackageKillableStatesForUserId(int userId, PackageManager pm)701 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId, 702 PackageManager pm) { 703 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId); 704 List<PackageKillableState> states = new ArrayList<>(); 705 synchronized (mLock) { 706 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage = 707 new ArrayMap<>(); 708 for (int i = 0; i < packageInfos.size(); ++i) { 709 PackageInfo packageInfo = packageInfos.get(i); 710 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo); 711 if (packageInfo.sharedUserId == null) { 712 int componentType = mPackageInfoHandler.getComponentType( 713 packageInfo.applicationInfo); 714 int killableState = getPackageKillableStateForUserPackageLocked( 715 userId, genericPackageName, componentType, 716 mOveruseConfigurationCache.isSafeToKill( 717 genericPackageName, componentType, /* sharedPackages= */null)); 718 states.add(new PackageKillableState(packageInfo.packageName, userId, 719 killableState)); 720 continue; 721 } 722 List<ApplicationInfo> applicationInfos = 723 applicationInfosBySharedPackage.get(genericPackageName); 724 if (applicationInfos == null) { 725 applicationInfos = new ArrayList<>(); 726 } 727 applicationInfos.add(packageInfo.applicationInfo); 728 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos); 729 } 730 for (Map.Entry<String, List<ApplicationInfo>> entry : 731 applicationInfosBySharedPackage.entrySet()) { 732 String genericPackageName = entry.getKey(); 733 List<ApplicationInfo> applicationInfos = entry.getValue(); 734 int componentType = mPackageInfoHandler.getSharedComponentType( 735 applicationInfos, genericPackageName); 736 List<String> packageNames = new ArrayList<>(applicationInfos.size()); 737 for (int i = 0; i < applicationInfos.size(); ++i) { 738 packageNames.add(applicationInfos.get(i).packageName); 739 } 740 int killableState = getPackageKillableStateForUserPackageLocked( 741 userId, genericPackageName, componentType, 742 mOveruseConfigurationCache.isSafeToKill( 743 genericPackageName, componentType, packageNames)); 744 for (int i = 0; i < applicationInfos.size(); ++i) { 745 states.add(new PackageKillableState( 746 applicationInfos.get(i).packageName, userId, killableState)); 747 } 748 } 749 } 750 if (DEBUG) { 751 Slogf.d(TAG, "Returning the package killable states for user packages"); 752 } 753 return states; 754 } 755 756 /** Sets the given resource overuse configurations. */ 757 @CarWatchdogManager.ReturnCode setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)758 public int setResourceOveruseConfigurations( 759 List<ResourceOveruseConfiguration> configurations, 760 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) 761 throws RemoteException { 762 Objects.requireNonNull(configurations, "Configurations must be non-null"); 763 Preconditions.checkArgument((configurations.size() > 0), 764 "Must provide at least one configuration"); 765 Preconditions.checkArgument((resourceOveruseFlag > 0), 766 "Must provide valid resource overuse flag"); 767 checkResourceOveruseConfigs(configurations, resourceOveruseFlag); 768 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs = 769 new ArrayList<>(); 770 for (int i = 0; i < configurations.size(); ++i) { 771 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i), 772 resourceOveruseFlag)); 773 } 774 synchronized (mLock) { 775 if (!mIsConnectedToDaemon) { 776 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs); 777 return CarWatchdogManager.RETURN_CODE_SUCCESS; 778 } 779 /* Verify no pending request in progress. */ 780 setPendingSetResourceOveruseConfigurationsRequestLocked(null); 781 } 782 return setResourceOveruseConfigurationsInternal(internalConfigs, 783 /* isPendingRequest= */ false); 784 } 785 786 /** Returns the available resource overuse configurations. */ 787 @NonNull getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)788 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( 789 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 790 Preconditions.checkArgument((resourceOveruseFlag > 0), 791 "Must provide valid resource overuse flag"); 792 if (!isConnectedToDaemon()) { 793 throw new IllegalStateException("Car watchdog daemon is not connected"); 794 } 795 synchronized (mLock) { 796 /* Verify no pending request in progress. */ 797 setPendingSetResourceOveruseConfigurationsRequestLocked(null); 798 } 799 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs = 800 new ArrayList<>(); 801 try { 802 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations(); 803 } catch (RemoteException | RuntimeException e) { 804 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations"); 805 throw new IllegalStateException(e); 806 } 807 List<ResourceOveruseConfiguration> configs = new ArrayList<>(); 808 for (int i = 0; i < internalConfigs.size(); ++i) { 809 configs.add( 810 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag)); 811 } 812 if (DEBUG) { 813 Slogf.d(TAG, "Returning the resource overuse configuration"); 814 } 815 return configs; 816 } 817 818 /** Processes the latest I/O overuse stats */ latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)819 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) { 820 // Long running operation, such as DB operations, must not be performed on binder threads, 821 // even if they are one way binder call, because it may block other one way binder threads. 822 // Hence, we handle the latest I/O overuse stats on the service handler thread. 823 mServiceHandler.post(() -> latestIoOveruseStatsInternal(packageIoOveruseStats)); 824 } 825 latestIoOveruseStatsInternal(List<PackageIoOveruseStats> packageIoOveruseStats)826 private void latestIoOveruseStatsInternal(List<PackageIoOveruseStats> packageIoOveruseStats) { 827 int[] uids = new int[packageIoOveruseStats.size()]; 828 for (int i = 0; i < packageIoOveruseStats.size(); ++i) { 829 uids[i] = packageIoOveruseStats.get(i).uid; 830 } 831 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids); 832 ArraySet<String> overusingUserPackageKeys = new ArraySet<>(); 833 checkAndHandleDateChange(); 834 if (genericPackageNamesByUid.size() > 0) { 835 mWatchdogStorage.markDirty(); 836 } 837 synchronized (mLock) { 838 for (int i = 0; i < packageIoOveruseStats.size(); ++i) { 839 PackageIoOveruseStats stats = packageIoOveruseStats.get(i); 840 String genericPackageName = genericPackageNamesByUid.get(stats.uid); 841 if (genericPackageName == null) { 842 continue; 843 } 844 PackageResourceUsage usage = cacheAndFetchUsageLocked(stats.uid, genericPackageName, 845 stats.ioOveruseStats, stats.forgivenWriteBytes); 846 if (stats.shouldNotify) { 847 /* 848 * Packages that exceed the warn threshold percentage should be notified as well 849 * and only the daemon is aware of such packages. Thus the flag is used to 850 * indicate which packages should be notified. 851 */ 852 ResourceOveruseStats resourceOveruseStats = 853 usage.getResourceOveruseStatsBuilder().setIoOveruseStats( 854 usage.getIoOveruseStats()).build(); 855 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats); 856 } 857 if (!usage.ioUsage.exceedsThreshold()) { 858 continue; 859 } 860 overusingUserPackageKeys.add(usage.getUniqueId()); 861 if (usage.getKillableState() == KILLABLE_STATE_NEVER) { 862 continue; 863 } 864 if (usage.ioUsage.getNotForgivenOveruses() > mRecurringOveruseTimes) { 865 String id = usage.getUniqueId(); 866 mActionableUserPackages.add(id); 867 mUserNotifiablePackages.add(id); 868 usage.ioUsage.forgiveOveruses(); 869 } 870 } 871 if ((mCurrentUxState != UX_STATE_NO_DISTRACTION && !mUserNotifiablePackages.isEmpty()) 872 // TODO(b/200599130): When resource overusing background apps are killed 873 // immediately, update the below check to allow posting 874 // {@code performOveruseHandlingLocked} immediately. 875 || (mCurrentUxState == UX_STATE_NO_INTERACTION 876 && !mActionableUserPackages.isEmpty())) { 877 mMainHandler.postDelayed(() -> { 878 synchronized (mLock) { 879 performOveruseHandlingLocked(); 880 }}, mOveruseHandlingDelayMills); 881 } 882 } 883 if (!overusingUserPackageKeys.isEmpty()) { 884 pushIoOveruseMetrics(overusingUserPackageKeys); 885 } 886 if (DEBUG) { 887 Slogf.d(TAG, "Processed latest I/O overuse stats"); 888 } 889 } 890 891 /** Resets the resource overuse settings and stats for the given generic package names. */ resetResourceOveruseStats(Set<String> genericPackageNames)892 public void resetResourceOveruseStats(Set<String> genericPackageNames) { 893 synchronized (mLock) { 894 mIsHeadsUpNotificationSent = false; 895 for (int i = 0; i < mUsageByUserPackage.size(); ++i) { 896 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i); 897 if (!genericPackageNames.contains(usage.genericPackageName)) { 898 continue; 899 } 900 usage.resetStats(); 901 usage.verifyAndSetKillableState(/* isKillable= */ true); 902 mWatchdogStorage.deleteUserPackage(usage.userId, usage.genericPackageName); 903 mActionableUserPackages.remove(usage.getUniqueId()); 904 Slogf.i(TAG, 905 "Reset resource overuse settings and stats for user '%d' package '%s'", 906 usage.userId, usage.genericPackageName); 907 if (usage.isSharedPackage() && usage.getUid() == INVALID_UID) { 908 // Only enable packages that were disabled by the watchdog service. Ergo, if 909 // the usage doesn't have a valid UID, the package was not recently disabled 910 // by the watchdog service (unless the service crashed) and can be safely 911 // skipped. 912 Slogf.e(TAG, "Skipping enabling user %d's package %s", usage.userId, 913 usage.genericPackageName); 914 continue; 915 } 916 enablePackageForUser(usage.getUid(), usage.genericPackageName); 917 } 918 } 919 } 920 921 /** Returns today's I/O usage stats for all packages collected during the previous boot. */ getTodayIoUsageStats()922 public List<UserPackageIoUsageStats> getTodayIoUsageStats() { 923 List<UserPackageIoUsageStats> userPackageIoUsageStats = new ArrayList<>(); 924 List<WatchdogStorage.IoUsageStatsEntry> entries = mWatchdogStorage.getTodayIoUsageStats(); 925 for (int i = 0; i < entries.size(); ++i) { 926 WatchdogStorage.IoUsageStatsEntry entry = entries.get(i); 927 UserPackageIoUsageStats stats = new UserPackageIoUsageStats(); 928 stats.userId = entry.userId; 929 stats.packageName = entry.packageName; 930 stats.ioUsageStats = new IoUsageStats(); 931 android.automotive.watchdog.IoOveruseStats internalIoUsage = 932 entry.ioUsage.getInternalIoOveruseStats(); 933 stats.ioUsageStats.writtenBytes = internalIoUsage.writtenBytes; 934 stats.ioUsageStats.forgivenWriteBytes = entry.ioUsage.getForgivenWriteBytes(); 935 stats.ioUsageStats.totalOveruses = internalIoUsage.totalOveruses; 936 userPackageIoUsageStats.add(stats); 937 } 938 return userPackageIoUsageStats; 939 } 940 941 /** Deletes all data for specific user. */ deleteUser(@serIdInt int userId)942 public void deleteUser(@UserIdInt int userId) { 943 synchronized (mLock) { 944 for (int i = mUsageByUserPackage.size() - 1; i >= 0; --i) { 945 if (userId == mUsageByUserPackage.valueAt(i).userId) { 946 mUsageByUserPackage.removeAt(i); 947 } 948 } 949 mWatchdogStorage.syncUsers(getAliveUserIds()); 950 } 951 if (DEBUG) { 952 Slogf.d(TAG, "Resource usage for user id: %d was deleted.", userId); 953 } 954 } 955 956 /** Handles intents from user notification actions. */ processUserNotificationIntent(Intent intent)957 public void processUserNotificationIntent(Intent intent) { 958 String action = intent.getAction(); 959 String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); 960 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 961 int notificationId = intent.getIntExtra(INTENT_EXTRA_NOTIFICATION_ID, -1); 962 if (packageName == null || packageName.isEmpty() || userHandle == null 963 || userHandle.getIdentifier() < 0) { 964 Slogf.w(TAG, "Invalid package '%s' or userHandle '%s' received in the intent", 965 packageName, userHandle); 966 return; 967 } 968 switch (action) { 969 case ACTION_RESOURCE_OVERUSE_DISABLE_APP: 970 disablePackageForUser(packageName, userHandle.getIdentifier()); 971 if (DEBUG) { 972 Slogf.d(TAG, 973 "Handled user notification action to disable package %s for user %s", 974 packageName, userHandle); 975 } 976 break; 977 case ACTION_LAUNCH_APP_SETTINGS: 978 Intent settingsIntent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS) 979 .setData(Uri.parse("package:" + packageName)) 980 .setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK); 981 mBuiltinPackageContext.startActivityAsUser(settingsIntent, userHandle); 982 if (DEBUG) { 983 Slogf.d(TAG, "Handled user notification action to launch settings app for " 984 + "package %s and user %s", packageName, userHandle); 985 } 986 break; 987 case ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION: 988 break; 989 default: 990 Slogf.e(TAG, "Skipping invalid user notification intent action: %s", action); 991 return; 992 } 993 994 if (notificationId == -1) { 995 Slogf.e(TAG, "Didn't received user notification id in action %s", action); 996 return; 997 } 998 999 int maxNotificationId = 1000 mResourceOveruseNotificationBaseId + mResourceOveruseNotificationMaxOffset - 1; 1001 if (notificationId < mResourceOveruseNotificationBaseId 1002 || notificationId > maxNotificationId) { 1003 Slogf.e(TAG, "Notification id (%d) outside of reserved IDs (%d - %d) for car watchdog.", 1004 notificationId, mResourceOveruseNotificationBaseId, maxNotificationId); 1005 return; 1006 } 1007 1008 synchronized (mLock) { 1009 String uniqueUserPackageId = mActiveUserNotificationsByNotificationId.get( 1010 notificationId); 1011 if (uniqueUserPackageId != null 1012 && uniqueUserPackageId.equals(getUserPackageUniqueId(userHandle.getIdentifier(), 1013 packageName))) { 1014 mActiveUserNotificationsByNotificationId.remove(notificationId); 1015 mActiveUserNotifications.remove(uniqueUserPackageId); 1016 } 1017 } 1018 1019 cancelNotificationAsUser(notificationId, userHandle); 1020 if (DEBUG) { 1021 Slogf.d(TAG, "Successfully canceled notification id %d for user %s and package %s", 1022 notificationId, userHandle, packageName); 1023 } 1024 } 1025 1026 /** Handles when system broadcast package changed action */ processPackageChangedIntent(Intent intent)1027 public void processPackageChangedIntent(Intent intent) { 1028 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); 1029 if (userId == USER_NULL) { 1030 Slogf.w(TAG, "Skipping package changed action with USER_NULL user"); 1031 return; 1032 } 1033 String packageName = intent.getData().getSchemeSpecificPart(); 1034 try { 1035 if (PackageManagerHelper.getApplicationEnabledSettingForUser(packageName, userId) 1036 != COMPONENT_ENABLED_STATE_ENABLED) { 1037 return; 1038 } 1039 } catch (Exception e) { 1040 // Catch IllegalArgumentException thrown by PackageManager when the package 1041 // is not found. CarWatchdogService shouldn't crash when the package 1042 // no longer exists when the {@link ACTION_PACKAGE_CHANGED} broadcast is 1043 // handled. 1044 Slogf.e(TAG, e, 1045 "Failed to verify enabled setting for user %d, package '%s'", 1046 userId, packageName); 1047 return; 1048 } 1049 synchronized (mLock) { 1050 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId); 1051 if (disabledPackages == null || !disabledPackages.contains(packageName)) { 1052 return; 1053 } 1054 removeFromDisabledPackagesSettingsStringLocked(packageName, userId); 1055 disabledPackages.remove(packageName); 1056 if (disabledPackages.isEmpty()) { 1057 mDisabledUserPackagesByUserId.remove(userId); 1058 } 1059 } 1060 if (DEBUG) { 1061 Slogf.d(TAG, "Successfully enabled package due to package changed action"); 1062 } 1063 } 1064 1065 /** Disables a package for specific user until used. */ disablePackageForUser(String packageName, @UserIdInt int userId)1066 public boolean disablePackageForUser(String packageName, @UserIdInt int userId) { 1067 synchronized (mLock) { 1068 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId); 1069 if (disabledPackages != null && disabledPackages.contains(packageName)) { 1070 return true; 1071 } 1072 } 1073 try { 1074 int currentEnabledState = 1075 PackageManagerHelper.getApplicationEnabledSettingForUser(packageName, userId); 1076 switch (currentEnabledState) { 1077 case COMPONENT_ENABLED_STATE_DISABLED: 1078 case COMPONENT_ENABLED_STATE_DISABLED_USER: 1079 case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 1080 Slogf.w(TAG, "Unable to disable application for user %d, package '%s' as the " 1081 + "current enabled state is %s", userId, packageName, 1082 toEnabledStateString(currentEnabledState)); 1083 return false; 1084 } 1085 PackageManagerHelper.setApplicationEnabledSettingForUser(packageName, 1086 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId, 1087 mContext.getPackageName()); 1088 synchronized (mLock) { 1089 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId); 1090 if (disabledPackages == null) { 1091 disabledPackages = new ArraySet<>(1); 1092 } 1093 appendToDisabledPackagesSettingsString(packageName, userId); 1094 disabledPackages.add(packageName); 1095 mDisabledUserPackagesByUserId.put(userId, disabledPackages); 1096 } 1097 Slogf.i(TAG, "Disabled package '%s' on user %d until used due to resource overuse", 1098 packageName, userId); 1099 } catch (Exception e) { 1100 Slogf.e(TAG, e, "Failed to disable application for user %d, package '%s'", userId, 1101 packageName); 1102 return false; 1103 } 1104 return true; 1105 } 1106 1107 /** 1108 * Sets the delay to handle resource overuse after the package is notified of resource overuse. 1109 */ setOveruseHandlingDelay(long millis)1110 public void setOveruseHandlingDelay(long millis) { 1111 synchronized (mLock) { 1112 mOveruseHandlingDelayMills = millis; 1113 } 1114 } 1115 1116 /** Writes to watchdog metadata file. */ writeMetadataFile()1117 public void writeMetadataFile() { 1118 ZonedDateTime systemIoUsageSummaryReportDate; 1119 ZonedDateTime uidIoUsageSummaryReportDate; 1120 synchronized (mLock) { 1121 if (mLastSystemIoUsageSummaryReportedDate == null 1122 && mLastUidIoUsageSummaryReportedDate == null) { 1123 return; 1124 } 1125 systemIoUsageSummaryReportDate = mLastSystemIoUsageSummaryReportedDate; 1126 uidIoUsageSummaryReportDate = mLastUidIoUsageSummaryReportedDate; 1127 } 1128 File file = getWatchdogMetadataFile(); 1129 AtomicFile atomicFile = new AtomicFile(file); 1130 FileOutputStream fos = null; 1131 try { 1132 fos = atomicFile.startWrite(); 1133 try (JsonWriter jsonWriter = 1134 new JsonWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) { 1135 jsonWriter.beginObject(); 1136 if (systemIoUsageSummaryReportDate != null) { 1137 jsonWriter.name(SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE) 1138 .value(systemIoUsageSummaryReportDate 1139 .format(DateTimeFormatter.ISO_DATE_TIME)); 1140 } 1141 if (uidIoUsageSummaryReportDate != null) { 1142 jsonWriter.name(UID_IO_USAGE_SUMMARY_REPORTED_DATE) 1143 .value(uidIoUsageSummaryReportDate 1144 .format(DateTimeFormatter.ISO_DATE_TIME)); 1145 } 1146 jsonWriter.endObject(); 1147 } 1148 atomicFile.finishWrite(fos); 1149 if (DEBUG) { 1150 Slogf.e(TAG, "Successfully wrote watchdog metadata file '%s'", 1151 file.getAbsoluteFile()); 1152 } 1153 } catch (IOException e) { 1154 Slogf.e(TAG, e, "Failed to write watchdog metadata file '%s'", file.getAbsoluteFile()); 1155 atomicFile.failWrite(fos); 1156 } 1157 } 1158 1159 /** Fetches and syncs the resource overuse configurations from watchdog daemon. */ fetchAndSyncResourceOveruseConfigurations()1160 private void fetchAndSyncResourceOveruseConfigurations() { 1161 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs; 1162 try { 1163 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations(); 1164 } catch (RemoteException | RuntimeException e) { 1165 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations"); 1166 return; 1167 } 1168 if (internalConfigs.isEmpty()) { 1169 Slogf.e(TAG, "Fetched resource overuse configurations are empty"); 1170 return; 1171 } 1172 mOveruseConfigurationCache.set(internalConfigs); 1173 mPackageInfoHandler.setVendorPackagePrefixes( 1174 mOveruseConfigurationCache.getVendorPackagePrefixes()); 1175 if (DEBUG) { 1176 Slogf.d(TAG, "Fetched and synced resource overuse configs."); 1177 } 1178 } 1179 readFromDatabase()1180 private void readFromDatabase() { 1181 mWatchdogStorage.syncUsers(getAliveUserIds()); 1182 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = 1183 mWatchdogStorage.getUserPackageSettings(); 1184 Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size()); 1185 // Get date before |WatchdogStorage.getTodayIoUsageStats| such that if date changes between 1186 // call to database and caching of the date, future calls to |latestIoOveruseStats| will 1187 // catch the change and sync the database with the in-memory cache. 1188 ZonedDateTime curReportDate = mTimeSource.getCurrentDate(); 1189 List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries = 1190 mWatchdogStorage.getTodayIoUsageStats(); 1191 Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size()); 1192 synchronized (mLock) { 1193 for (int i = 0; i < settingsEntries.size(); ++i) { 1194 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i); 1195 if (entry.userId == UserHandle.ALL.getIdentifier()) { 1196 if (entry.killableState != KILLABLE_STATE_YES) { 1197 mDefaultNotKillableGenericPackages.add(entry.packageName); 1198 } 1199 continue; 1200 } 1201 String key = getUserPackageUniqueId(entry.userId, entry.packageName); 1202 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1203 if (usage == null) { 1204 usage = new PackageResourceUsage(entry.userId, entry.packageName, 1205 getDefaultKillableStateLocked(entry.packageName)); 1206 } 1207 usage.setKillableState(entry.killableState); 1208 mUsageByUserPackage.put(key, usage); 1209 } 1210 for (int i = 0; i < ioStatsEntries.size(); ++i) { 1211 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i); 1212 String key = getUserPackageUniqueId(entry.userId, entry.packageName); 1213 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1214 if (usage == null) { 1215 usage = new PackageResourceUsage(entry.userId, entry.packageName, 1216 getDefaultKillableStateLocked(entry.packageName)); 1217 } 1218 /* Overwrite in memory cache as the stats will be merged on the daemon side and 1219 * pushed on the next latestIoOveruseStats call. This is tolerable because the next 1220 * push should happen soon. 1221 */ 1222 usage.ioUsage.overwrite(entry.ioUsage); 1223 mUsageByUserPackage.put(key, usage); 1224 } 1225 mLatestStatsReportDate = curReportDate; 1226 } 1227 syncHistoricalNotForgivenOveruses(); 1228 } 1229 1230 /** Fetches all historical not forgiven overuses and syncs them with package I/O usages. */ syncHistoricalNotForgivenOveruses()1231 private void syncHistoricalNotForgivenOveruses() { 1232 List<WatchdogStorage.NotForgivenOverusesEntry> notForgivenOverusesEntries = 1233 mWatchdogStorage.getNotForgivenHistoricalIoOveruses(mRecurringOverusePeriodInDays); 1234 Slogf.i(TAG, "Read %d not forgiven overuse stats from database", 1235 notForgivenOverusesEntries.size()); 1236 synchronized (mLock) { 1237 for (int i = 0; i < notForgivenOverusesEntries.size(); i++) { 1238 WatchdogStorage.NotForgivenOverusesEntry entry = notForgivenOverusesEntries.get(i); 1239 String key = getUserPackageUniqueId(entry.userId, entry.packageName); 1240 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1241 if (usage == null) { 1242 usage = new PackageResourceUsage(entry.userId, entry.packageName, 1243 getDefaultKillableStateLocked(entry.packageName)); 1244 } 1245 usage.ioUsage.setHistoricalNotForgivenOveruses(entry.notForgivenOveruses); 1246 mUsageByUserPackage.put(key, usage); 1247 } 1248 } 1249 } 1250 1251 /** 1252 * Writes user package settings and stats to database. If database is marked as clean, 1253 * no writing is executed. 1254 */ writeToDatabase()1255 public void writeToDatabase() { 1256 if (!mWatchdogStorage.startWrite()) { 1257 return; 1258 } 1259 try { 1260 List<WatchdogStorage.UserPackageSettingsEntry> userPackageSettingsEntries = 1261 new ArrayList<>(); 1262 List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = new ArrayList<>(); 1263 SparseArray<List<String>> forgivePackagesByUserId = new SparseArray<>(); 1264 synchronized (mLock) { 1265 for (int i = 0; i < mUsageByUserPackage.size(); i++) { 1266 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i); 1267 userPackageSettingsEntries.add(new WatchdogStorage.UserPackageSettingsEntry( 1268 usage.userId, usage.genericPackageName, usage.getKillableState())); 1269 if (!usage.ioUsage.hasUsage()) { 1270 continue; 1271 } 1272 if (usage.ioUsage.shouldForgiveHistoricalOveruses()) { 1273 List<String> packagesToForgive = forgivePackagesByUserId.get(usage.userId); 1274 if (packagesToForgive == null) { 1275 packagesToForgive = new ArrayList<>(); 1276 } 1277 packagesToForgive.add(usage.genericPackageName); 1278 forgivePackagesByUserId.put(usage.userId, packagesToForgive); 1279 } 1280 ioUsageStatsEntries.add(new WatchdogStorage.IoUsageStatsEntry(usage.userId, 1281 usage.genericPackageName, usage.ioUsage)); 1282 } 1283 for (String packageName : mDefaultNotKillableGenericPackages) { 1284 userPackageSettingsEntries.add(new WatchdogStorage.UserPackageSettingsEntry( 1285 UserHandle.ALL.getIdentifier(), packageName, KILLABLE_STATE_NO)); 1286 } 1287 } 1288 boolean userPackageSettingResult = 1289 mWatchdogStorage.saveUserPackageSettings(userPackageSettingsEntries); 1290 if (!userPackageSettingResult) { 1291 Slogf.e(TAG, "Failed to write user package settings to database"); 1292 } else { 1293 Slogf.i(TAG, "Successfully saved %d user package settings to database", 1294 userPackageSettingsEntries.size()); 1295 } 1296 if (writeStats(ioUsageStatsEntries, forgivePackagesByUserId) 1297 && userPackageSettingResult) { 1298 mWatchdogStorage.markWriteSuccessful(); 1299 } 1300 } finally { 1301 mWatchdogStorage.endWrite(); 1302 } 1303 } 1304 1305 @GuardedBy("mLock") getDefaultKillableStateLocked(String genericPackageName)1306 private @KillableState int getDefaultKillableStateLocked(String genericPackageName) { 1307 return mDefaultNotKillableGenericPackages.contains(genericPackageName) 1308 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES; 1309 } 1310 writeStats(List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries, SparseArray<List<String>> forgivePackagesByUserId)1311 private boolean writeStats(List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries, 1312 SparseArray<List<String>> forgivePackagesByUserId) { 1313 // Forgive historical overuses before writing the latest stats to disk to avoid forgiving 1314 // the latest stats when the write is triggered after date change. 1315 if (forgivePackagesByUserId.size() != 0) { 1316 mWatchdogStorage.forgiveHistoricalOveruses(forgivePackagesByUserId, 1317 mRecurringOverusePeriodInDays); 1318 Slogf.i(TAG, "Attempted to forgive historical overuses for %d users.", 1319 forgivePackagesByUserId.size()); 1320 } 1321 if (ioUsageStatsEntries.isEmpty()) { 1322 return true; 1323 } 1324 int result = mWatchdogStorage.saveIoUsageStats(ioUsageStatsEntries); 1325 if (result == WatchdogStorage.FAILED_TRANSACTION) { 1326 Slogf.e(TAG, "Failed to write %d I/O overuse stats to database", 1327 ioUsageStatsEntries.size()); 1328 } else { 1329 Slogf.i(TAG, "Successfully saved %d/%d I/O overuse stats to database", 1330 result, ioUsageStatsEntries.size()); 1331 } 1332 return result != WatchdogStorage.FAILED_TRANSACTION; 1333 } 1334 1335 @GuardedBy("mLock") applyCurrentUxRestrictionsLocked()1336 private void applyCurrentUxRestrictionsLocked() { 1337 if (mCurrentUxRestrictions == null 1338 || mCurrentUxRestrictions.isRequiresDistractionOptimization()) { 1339 mCurrentUxState = UX_STATE_NO_DISTRACTION; 1340 return; 1341 } 1342 if (mCurrentUxState == UX_STATE_NO_INTERACTION) { 1343 return; 1344 } 1345 mCurrentUxState = UX_STATE_USER_NOTIFICATION; 1346 performOveruseHandlingLocked(); 1347 } 1348 1349 @GuardedBy("mLock") getPackageKillableStateForUserPackageLocked( int userId, String genericPackageName, int componentType, boolean isSafeToKill)1350 private int getPackageKillableStateForUserPackageLocked( 1351 int userId, String genericPackageName, int componentType, boolean isSafeToKill) { 1352 String key = getUserPackageUniqueId(userId, genericPackageName); 1353 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1354 int defaultKillableState = getDefaultKillableStateLocked(genericPackageName); 1355 if (usage == null) { 1356 usage = new PackageResourceUsage(userId, genericPackageName, defaultKillableState); 1357 } 1358 int killableState = usage.syncAndFetchKillableState( 1359 componentType, isSafeToKill, defaultKillableState); 1360 mUsageByUserPackage.put(key, usage); 1361 mWatchdogStorage.markDirty(); 1362 return killableState; 1363 } 1364 1365 @GuardedBy("mLock") notifyResourceOveruseStatsLocked(int uid, ResourceOveruseStats resourceOveruseStats)1366 private void notifyResourceOveruseStatsLocked(int uid, 1367 ResourceOveruseStats resourceOveruseStats) { 1368 String genericPackageName = resourceOveruseStats.getPackageName(); 1369 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid); 1370 if (listenerInfos != null) { 1371 for (int i = 0; i < listenerInfos.size(); ++i) { 1372 listenerInfos.get(i).notifyListener( 1373 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats); 1374 } 1375 } 1376 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) { 1377 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos = 1378 mOveruseSystemListenerInfosByUid.valueAt(i); 1379 for (int j = 0; j < systemListenerInfos.size(); ++j) { 1380 systemListenerInfos.get(j).notifyListener( 1381 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats); 1382 } 1383 } 1384 if (DEBUG) { 1385 Slogf.d(TAG, "Notified resource overuse stats to listening applications"); 1386 } 1387 } 1388 checkAndHandleDateChange()1389 private void checkAndHandleDateChange() { 1390 synchronized (mLock) { 1391 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 1392 if (currentDate.equals(mLatestStatsReportDate)) { 1393 return; 1394 } 1395 mLatestStatsReportDate = currentDate; 1396 } 1397 writeToDatabase(); 1398 synchronized (mLock) { 1399 for (int i = 0; i < mUsageByUserPackage.size(); i++) { 1400 mUsageByUserPackage.valueAt(i).resetStats(); 1401 } 1402 } 1403 syncHistoricalNotForgivenOveruses(); 1404 if (DEBUG) { 1405 Slogf.d(TAG, "Handled date change successfully"); 1406 } 1407 } 1408 1409 @GuardedBy("mLock") cacheAndFetchUsageLocked(int uid, String genericPackageName, android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes)1410 private PackageResourceUsage cacheAndFetchUsageLocked(int uid, String genericPackageName, 1411 android.automotive.watchdog.IoOveruseStats internalStats, 1412 android.automotive.watchdog.PerStateBytes forgivenWriteBytes) { 1413 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1414 String key = getUserPackageUniqueId(userId, genericPackageName); 1415 int defaultKillableState = getDefaultKillableStateLocked(genericPackageName); 1416 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1417 if (usage == null) { 1418 usage = new PackageResourceUsage(userId, genericPackageName, defaultKillableState); 1419 } 1420 usage.update(uid, internalStats, forgivenWriteBytes, defaultKillableState); 1421 mUsageByUserPackage.put(key, usage); 1422 return usage; 1423 } 1424 getIoOveruseStatsForPeriod(int userId, String genericPackageName, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)1425 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName, 1426 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 1427 synchronized (mLock) { 1428 String key = getUserPackageUniqueId(userId, genericPackageName); 1429 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1430 if (usage == null) { 1431 return null; 1432 } 1433 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod); 1434 } 1435 } 1436 1437 @GuardedBy("mLock") getIoOveruseStatsLocked(PackageResourceUsage usage, long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)1438 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage, 1439 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 1440 if (!usage.ioUsage.hasUsage()) { 1441 /* Return I/O overuse stats only when the package has usage for the current day. 1442 * Without the current day usage, the returned stats will contain zero remaining 1443 * bytes, which is incorrect. 1444 */ 1445 return null; 1446 } 1447 IoOveruseStats currentStats = usage.getIoOveruseStats(); 1448 long totalBytesWritten = currentStats.getTotalBytesWritten(); 1449 int numDays = toNumDays(maxStatsPeriod); 1450 IoOveruseStats historyStats = null; 1451 if (numDays > 0) { 1452 historyStats = mWatchdogStorage.getHistoricalIoOveruseStats( 1453 usage.userId, usage.genericPackageName, numDays - 1); 1454 totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0; 1455 } 1456 if (totalBytesWritten < minimumBytesWritten) { 1457 return null; 1458 } 1459 if (historyStats == null) { 1460 return currentStats; 1461 } 1462 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder( 1463 historyStats.getStartTime(), 1464 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds()); 1465 statsBuilder.setTotalTimesKilled( 1466 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled()); 1467 statsBuilder.setTotalOveruses( 1468 historyStats.getTotalOveruses() + currentStats.getTotalOveruses()); 1469 statsBuilder.setTotalBytesWritten( 1470 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten()); 1471 statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse()); 1472 statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes()); 1473 return statsBuilder.build(); 1474 } 1475 1476 @GuardedBy("mLock") addResourceOveruseListenerLocked( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener, SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid)1477 private void addResourceOveruseListenerLocked( 1478 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 1479 @NonNull IResourceOveruseListener listener, 1480 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) { 1481 int callingPid = Binder.getCallingPid(); 1482 int callingUid = Binder.getCallingUid(); 1483 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid; 1484 String listenerType = isListenerForSystem ? "resource overuse listener for system" : 1485 "resource overuse listener"; 1486 1487 IBinder binder = listener.asBinder(); 1488 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid); 1489 if (listenerInfos == null) { 1490 listenerInfos = new ArrayList<>(); 1491 listenerInfosByUid.put(callingUid, listenerInfos); 1492 } 1493 for (int i = 0; i < listenerInfos.size(); ++i) { 1494 if (listenerInfos.get(i).listener.asBinder() == binder) { 1495 throw new IllegalStateException( 1496 "Cannot add " + listenerType + " as it is already added"); 1497 } 1498 } 1499 1500 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener, 1501 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem); 1502 try { 1503 listenerInfo.linkToDeath(); 1504 } catch (RemoteException e) { 1505 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType); 1506 return; 1507 } 1508 listenerInfos.add(listenerInfo); 1509 if (DEBUG) { 1510 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType, 1511 callingPid, callingUid); 1512 } 1513 } 1514 1515 @GuardedBy("mLock") removeResourceOveruseListenerLocked(@onNull IResourceOveruseListener listener, SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid)1516 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener, 1517 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) { 1518 int callingUid = Binder.getCallingUid(); 1519 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid 1520 ? "resource overuse system listener" : "resource overuse listener"; 1521 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid); 1522 if (listenerInfos == null) { 1523 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType); 1524 return; 1525 } 1526 IBinder binder = listener.asBinder(); 1527 ResourceOveruseListenerInfo cachedListenerInfo = null; 1528 for (int i = 0; i < listenerInfos.size(); ++i) { 1529 if (listenerInfos.get(i).listener.asBinder() == binder) { 1530 cachedListenerInfo = listenerInfos.get(i); 1531 break; 1532 } 1533 } 1534 if (cachedListenerInfo == null) { 1535 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType); 1536 return; 1537 } 1538 cachedListenerInfo.unlinkToDeath(); 1539 listenerInfos.remove(cachedListenerInfo); 1540 if (listenerInfos.isEmpty()) { 1541 listenerInfosByUid.remove(callingUid); 1542 } 1543 if (DEBUG) { 1544 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType, 1545 cachedListenerInfo.pid, cachedListenerInfo.uid); 1546 } 1547 } 1548 1549 @GuardedBy("mLock") setPendingSetResourceOveruseConfigurationsRequestLocked( List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs)1550 private void setPendingSetResourceOveruseConfigurationsRequestLocked( 1551 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) { 1552 if (mPendingSetResourceOveruseConfigurationsRequest != null) { 1553 if (mPendingSetResourceOveruseConfigurationsRequest == configs) { 1554 return; 1555 } 1556 throw new IllegalStateException( 1557 "Pending setResourceOveruseConfigurations request in progress"); 1558 } 1559 mPendingSetResourceOveruseConfigurationsRequest = configs; 1560 } 1561 retryPendingSetResourceOveruseConfigurations()1562 private void retryPendingSetResourceOveruseConfigurations() { 1563 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs; 1564 synchronized (mLock) { 1565 if (mPendingSetResourceOveruseConfigurationsRequest == null) { 1566 return; 1567 } 1568 configs = mPendingSetResourceOveruseConfigurationsRequest; 1569 } 1570 try { 1571 int result = setResourceOveruseConfigurationsInternal(configs, 1572 /* isPendingRequest= */ true); 1573 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) { 1574 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code " 1575 + "%d", result); 1576 } 1577 } catch (Exception e) { 1578 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations"); 1579 } 1580 } 1581 setResourceOveruseConfigurationsInternal( List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs, boolean isPendingRequest)1582 private int setResourceOveruseConfigurationsInternal( 1583 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs, 1584 boolean isPendingRequest) throws RemoteException { 1585 boolean doClearPendingRequest = isPendingRequest; 1586 try { 1587 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs); 1588 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations); 1589 } catch (RemoteException e) { 1590 if (e instanceof TransactionTooLargeException) { 1591 throw e; 1592 } 1593 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration"); 1594 synchronized (mLock) { 1595 setPendingSetResourceOveruseConfigurationsRequestLocked(configs); 1596 } 1597 doClearPendingRequest = false; 1598 return CarWatchdogManager.RETURN_CODE_SUCCESS; 1599 } finally { 1600 if (doClearPendingRequest) { 1601 synchronized (mLock) { 1602 mPendingSetResourceOveruseConfigurationsRequest = null; 1603 } 1604 } 1605 } 1606 if (DEBUG) { 1607 Slogf.d(TAG, "Set the resource overuse configuration successfully"); 1608 } 1609 return CarWatchdogManager.RETURN_CODE_SUCCESS; 1610 } 1611 isConnectedToDaemon()1612 private boolean isConnectedToDaemon() { 1613 synchronized (mLock) { 1614 long startTimeMillis = SystemClock.uptimeMillis(); 1615 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis; 1616 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) { 1617 try { 1618 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis); 1619 } catch (InterruptedException e) { 1620 Thread.currentThread().interrupt(); 1621 continue; 1622 } finally { 1623 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis; 1624 } 1625 break; 1626 } 1627 return mIsConnectedToDaemon; 1628 } 1629 } 1630 getAliveUserIds()1631 private int[] getAliveUserIds() { 1632 UserManager userManager = mContext.getSystemService(UserManager.class); 1633 List<UserHandle> aliveUsers = userManager.getUserHandles(/* excludeDying= */ true); 1634 int userSize = aliveUsers.size(); 1635 int[] userIds = new int[userSize]; 1636 for (int i = 0; i < userSize; ++i) { 1637 userIds[i] = aliveUsers.get(i).getIdentifier(); 1638 } 1639 return userIds; 1640 } 1641 1642 @GuardedBy("mLock") performOveruseHandlingLocked()1643 private void performOveruseHandlingLocked() { 1644 if (mCurrentUxState == UX_STATE_NO_DISTRACTION) { 1645 return; 1646 } 1647 if (!mUserNotifiablePackages.isEmpty()) { 1648 // Notifications are presented asynchronously, therefore the delay added by posting 1649 // to the handler should not affect the system behavior. 1650 mServiceHandler.post(this::notifyUserOnOveruse); 1651 } 1652 if (mActionableUserPackages.isEmpty() || mCurrentUxState != UX_STATE_NO_INTERACTION) { 1653 return; 1654 } 1655 ArraySet<String> killedUserPackageKeys = new ArraySet<>(); 1656 for (int i = 0; i < mActionableUserPackages.size(); ++i) { 1657 PackageResourceUsage usage = 1658 mUsageByUserPackage.get(mActionableUserPackages.valueAt(i)); 1659 if (usage == null) { 1660 continue; 1661 } 1662 // Between detecting and handling the overuse, either the package killable state or 1663 // the resource overuse configuration was updated. So, verify the killable state 1664 // before proceeding. 1665 int killableState = usage.getKillableState(); 1666 if (killableState != KILLABLE_STATE_YES) { 1667 continue; 1668 } 1669 List<String> packages; 1670 if (usage.isSharedPackage()) { 1671 packages = mPackageInfoHandler.getPackagesForUid(usage.getUid(), 1672 usage.genericPackageName); 1673 } else { 1674 packages = Collections.singletonList(usage.genericPackageName); 1675 } 1676 boolean isKilled = false; 1677 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) { 1678 String packageName = packages.get(pkgIdx); 1679 isKilled |= disablePackageForUser(packageName, usage.userId); 1680 } 1681 if (isKilled) { 1682 usage.ioUsage.killed(); 1683 killedUserPackageKeys.add(usage.getUniqueId()); 1684 } 1685 } 1686 pushIoOveruseKillMetrics(killedUserPackageKeys); 1687 mActionableUserPackages.clear(); 1688 } 1689 notifyUserOnOveruse()1690 private void notifyUserOnOveruse() { 1691 SparseArray<String> headsUpNotificationPackagesByNotificationId = new SparseArray<>(); 1692 SparseArray<String> notificationCenterPackagesByNotificationId = new SparseArray<>(); 1693 int currentUserId = ActivityManager.getCurrentUser(); 1694 synchronized (mLock) { 1695 for (int i = mUserNotifiablePackages.size() - 1; i >= 0; i--) { 1696 String uniqueId = mUserNotifiablePackages.valueAt(i); 1697 PackageResourceUsage usage = mUsageByUserPackage.get(uniqueId); 1698 if (usage == null || (usage.userId == currentUserId 1699 && usage.getKillableState() != KILLABLE_STATE_YES)) { 1700 mUserNotifiablePackages.removeAt(i); 1701 continue; 1702 } 1703 if (usage.userId != currentUserId) { 1704 Slogf.i(TAG, "Skipping notification for user %d and package %s because current" 1705 + " user %d is different", usage.userId, 1706 usage.genericPackageName, currentUserId); 1707 continue; 1708 } 1709 List<String> packages; 1710 if (usage.isSharedPackage()) { 1711 packages = mPackageInfoHandler.getPackagesForUid(usage.getUid(), 1712 usage.genericPackageName); 1713 } else { 1714 packages = Collections.singletonList(usage.genericPackageName); 1715 } 1716 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) { 1717 String packageName = packages.get(pkgIdx); 1718 String userPackageUniqueId = getUserPackageUniqueId(currentUserId, packageName); 1719 if (mActiveUserNotifications.contains(userPackageUniqueId)) { 1720 Slogf.e(TAG, "Dropping notification for user %d and package %s as it has " 1721 + "an active notification", currentUserId, packageName); 1722 continue; 1723 } 1724 int notificationId = mResourceOveruseNotificationBaseId 1725 + mCurrentOveruseNotificationIdOffset; 1726 if (mCurrentUxState == UX_STATE_NO_INTERACTION || mIsHeadsUpNotificationSent) { 1727 notificationCenterPackagesByNotificationId.put(notificationId, packageName); 1728 } else { 1729 headsUpNotificationPackagesByNotificationId.put(notificationId, 1730 packageName); 1731 mIsHeadsUpNotificationSent = true; 1732 } 1733 if (mActiveUserNotificationsByNotificationId.contains(notificationId)) { 1734 mActiveUserNotifications.remove( 1735 mActiveUserNotificationsByNotificationId.get(notificationId)); 1736 } 1737 mActiveUserNotifications.add(userPackageUniqueId); 1738 mActiveUserNotificationsByNotificationId.put(notificationId, 1739 userPackageUniqueId); 1740 mCurrentOveruseNotificationIdOffset = ++mCurrentOveruseNotificationIdOffset 1741 % mResourceOveruseNotificationMaxOffset; 1742 } 1743 mUserNotifiablePackages.removeAt(i); 1744 } 1745 } 1746 sendResourceOveruseNotificationsAsUser(currentUserId, 1747 headsUpNotificationPackagesByNotificationId, 1748 notificationCenterPackagesByNotificationId); 1749 if (DEBUG) { 1750 Slogf.d(TAG, "Sent %d resource overuse notifications successfully", 1751 headsUpNotificationPackagesByNotificationId.size() 1752 + notificationCenterPackagesByNotificationId.size()); 1753 } 1754 } 1755 enablePackageForUser(int uid, String genericPackageName)1756 private void enablePackageForUser(int uid, String genericPackageName) { 1757 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1758 synchronized (mLock) { 1759 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId); 1760 if (disabledPackages == null) { 1761 return; 1762 } 1763 } 1764 List<String> packages; 1765 if (isSharedPackage(genericPackageName)) { 1766 packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName); 1767 } else { 1768 packages = Collections.singletonList(genericPackageName); 1769 } 1770 for (int i = 0; i < packages.size(); i++) { 1771 String packageName = packages.get(i); 1772 try { 1773 if (PackageManagerHelper.getApplicationEnabledSettingForUser(packageName, 1774 userId) != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { 1775 continue; 1776 } 1777 synchronized (mLock) { 1778 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId); 1779 if (disabledPackages == null || !disabledPackages.contains(packageName)) { 1780 continue; 1781 } 1782 removeFromDisabledPackagesSettingsStringLocked(packageName, userId); 1783 disabledPackages.remove(packageName); 1784 if (disabledPackages.isEmpty()) { 1785 mDisabledUserPackagesByUserId.remove(userId); 1786 } 1787 } 1788 PackageManagerHelper.setApplicationEnabledSettingForUser( 1789 packageName, COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0, userId, 1790 mContext.getPackageName()); 1791 Slogf.i(TAG, "Enabled user '%d' package '%s'", userId, packageName); 1792 } catch (RemoteException | IllegalArgumentException e) { 1793 Slogf.e(TAG, e, "Failed to verify and enable user %d, package '%s'", userId, 1794 packageName); 1795 } 1796 } 1797 } 1798 sendResourceOveruseNotificationsAsUser(@serIdInt int userId, SparseArray<String> headsUpNotificationPackagesById, SparseArray<String> notificationCenterPackagesById)1799 private void sendResourceOveruseNotificationsAsUser(@UserIdInt int userId, 1800 SparseArray<String> headsUpNotificationPackagesById, 1801 SparseArray<String> notificationCenterPackagesById) { 1802 if (headsUpNotificationPackagesById.size() == 0 1803 && notificationCenterPackagesById.size() == 0) { 1804 return; 1805 } 1806 BuiltinPackageDependency.createNotificationHelper(mBuiltinPackageContext) 1807 .showResourceOveruseNotificationsAsUser( 1808 UserHandle.of(userId), 1809 headsUpNotificationPackagesById, notificationCenterPackagesById); 1810 } 1811 cancelNotificationAsUser(int notificationId, UserHandle userHandle)1812 private void cancelNotificationAsUser(int notificationId, UserHandle userHandle) { 1813 BuiltinPackageDependency.createNotificationHelper(mBuiltinPackageContext) 1814 .cancelNotificationAsUser(userHandle, notificationId); 1815 } 1816 appendToDisabledPackagesSettingsString(String packageName, @UserIdInt int userId)1817 private void appendToDisabledPackagesSettingsString(String packageName, @UserIdInt int userId) { 1818 ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId); 1819 // Appending and removing package names to/from the settings string 1820 // KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize 1821 // these operations using the class wide lock. 1822 synchronized (mLock) { 1823 ArraySet<String> packages = extractPackages( 1824 Settings.Secure.getString(contentResolverForUser, 1825 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)); 1826 if (!packages.add(packageName)) { 1827 return; 1828 } 1829 String settingsString = constructSettingsString(packages); 1830 Settings.Secure.putString(contentResolverForUser, 1831 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString); 1832 if (DEBUG) { 1833 Slogf.d(TAG, "Appended %s to %s. New value is '%s'", packageName, 1834 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString); 1835 } 1836 } 1837 } 1838 1839 /** 1840 * Removes {@code packageName} from {@link KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE} 1841 * {@code Settings} of the given user. 1842 * 1843 * <p> Appending and removing package names to/from the settings string 1844 * KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize 1845 * these operations using the class wide lock. 1846 */ 1847 @GuardedBy("mLock") removeFromDisabledPackagesSettingsStringLocked(String packageName, @UserIdInt int userId)1848 private void removeFromDisabledPackagesSettingsStringLocked(String packageName, 1849 @UserIdInt int userId) { 1850 ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId); 1851 ArraySet<String> packages = extractPackages( 1852 Settings.Secure.getString(contentResolverForUser, 1853 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)); 1854 if (!packages.remove(packageName)) { 1855 return; 1856 } 1857 String settingsString = constructSettingsString(packages); 1858 Settings.Secure.putString(contentResolverForUser, 1859 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString); 1860 if (DEBUG) { 1861 Slogf.d(TAG, "Removed %s from %s. New value is '%s'", packageName, 1862 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString); 1863 } 1864 } 1865 1866 /** 1867 * Syncs the {@link KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE} {@code Settings} of all users 1868 * with the internal cache. 1869 * 1870 * <p> Appending and removing package names to/from the settings string 1871 * KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize 1872 * these operations using the class wide lock. 1873 */ 1874 @GuardedBy("mLock") syncDisabledUserPackagesLocked()1875 private void syncDisabledUserPackagesLocked() { 1876 int[] userIds = getAliveUserIds(); 1877 for (int i = 0; i < userIds.length; i++) { 1878 int userId = userIds[i]; 1879 ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId); 1880 ArraySet<String> packages = extractPackages( 1881 Settings.Secure.getString(contentResolverForUser, 1882 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)); 1883 if (packages.isEmpty()) { 1884 continue; 1885 } 1886 mDisabledUserPackagesByUserId.put(userId, packages); 1887 } 1888 if (DEBUG) { 1889 Slogf.d(TAG, "Synced the %s settings to the disabled user packages cache.", 1890 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE); 1891 } 1892 } 1893 extractPackages(String settingsString)1894 private static ArraySet<String> extractPackages(String settingsString) { 1895 return TextUtils.isEmpty(settingsString) ? new ArraySet<>() 1896 : new ArraySet<>(Arrays.asList(settingsString.split( 1897 PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR))); 1898 } 1899 1900 @Nullable constructSettingsString(ArraySet<String> packages)1901 private static String constructSettingsString(ArraySet<String> packages) { 1902 return packages.isEmpty() ? null : 1903 TextUtils.join(PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR, packages); 1904 } 1905 pushIoOveruseMetrics(ArraySet<String> userPackageKeys)1906 private void pushIoOveruseMetrics(ArraySet<String> userPackageKeys) { 1907 SparseArray<AtomsProto.CarWatchdogIoOveruseStats> statsByUid = new SparseArray<>(); 1908 synchronized (mLock) { 1909 for (int i = 0; i < userPackageKeys.size(); ++i) { 1910 String key = userPackageKeys.valueAt(i); 1911 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1912 if (usage == null) { 1913 Slogf.w(TAG, "Missing usage stats for user package key %s", key); 1914 continue; 1915 } 1916 statsByUid.put(usage.getUid(), constructCarWatchdogIoOveruseStatsLocked(usage)); 1917 } 1918 } 1919 for (int i = 0; i < statsByUid.size(); ++i) { 1920 CarStatsLog.write(CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED, statsByUid.keyAt(i), 1921 statsByUid.valueAt(i).toByteArray()); 1922 } 1923 } 1924 pushIoOveruseKillMetrics(ArraySet<String> userPackageKeys)1925 private void pushIoOveruseKillMetrics(ArraySet<String> userPackageKeys) { 1926 int systemState; 1927 SparseArray<AtomsProto.CarWatchdogIoOveruseStats> statsByUid = new SparseArray<>(); 1928 synchronized (mLock) { 1929 systemState = inferSystemStateLocked(); 1930 for (int i = 0; i < userPackageKeys.size(); ++i) { 1931 String key = userPackageKeys.valueAt(i); 1932 PackageResourceUsage usage = mUsageByUserPackage.get(key); 1933 if (usage == null) { 1934 Slogf.w(TAG, "Missing usage stats for user package key %s", key); 1935 continue; 1936 } 1937 statsByUid.put(usage.getUid(), constructCarWatchdogIoOveruseStatsLocked(usage)); 1938 } 1939 } 1940 for (int i = 0; i < statsByUid.size(); ++i) { 1941 // TODO(b/200598815): After watchdog can classify foreground vs background apps, 1942 // report the correct uid state. 1943 CarStatsLog.write(CAR_WATCHDOG_KILL_STATS_REPORTED, statsByUid.keyAt(i), 1944 CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE, 1945 systemState, 1946 CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE, 1947 /* arg5= */ null, statsByUid.valueAt(i).toByteArray()); 1948 } 1949 } 1950 1951 @GuardedBy("mLock") inferSystemStateLocked()1952 private int inferSystemStateLocked() { 1953 if (mCurrentGarageMode == GarageMode.GARAGE_MODE_ON) { 1954 return CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE; 1955 } 1956 return mCurrentUxState == UX_STATE_NO_INTERACTION 1957 ? CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE 1958 : CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_INTERACTION_MODE; 1959 } 1960 1961 @GuardedBy("mLock") constructCarWatchdogIoOveruseStatsLocked( PackageResourceUsage usage)1962 private AtomsProto.CarWatchdogIoOveruseStats constructCarWatchdogIoOveruseStatsLocked( 1963 PackageResourceUsage usage) { 1964 @ComponentType int componentType = mPackageInfoHandler.getComponentType( 1965 usage.getUid(), usage.genericPackageName); 1966 android.automotive.watchdog.PerStateBytes threshold = 1967 mOveruseConfigurationCache.fetchThreshold(usage.genericPackageName, componentType); 1968 android.automotive.watchdog.PerStateBytes writtenBytes = 1969 usage.ioUsage.getInternalIoOveruseStats().writtenBytes; 1970 return constructCarWatchdogIoOveruseStats( 1971 AtomsProto.CarWatchdogIoOveruseStats.Period.DAILY, 1972 constructCarWatchdogPerStateBytes(threshold.foregroundBytes, 1973 threshold.backgroundBytes, threshold.garageModeBytes), 1974 constructCarWatchdogPerStateBytes(writtenBytes.foregroundBytes, 1975 writtenBytes.backgroundBytes, writtenBytes.garageModeBytes)); 1976 } 1977 onPullAtom(int atomTag, List<StatsEvent> data)1978 private int onPullAtom(int atomTag, List<StatsEvent> data) { 1979 if (atomTag != CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY 1980 && atomTag != CAR_WATCHDOG_UID_IO_USAGE_SUMMARY) { 1981 Slogf.e(TAG, "Unexpected atom tag: %d", atomTag); 1982 return PULL_SKIP; 1983 } 1984 synchronized (mLock) { 1985 if (mLastSystemIoUsageSummaryReportedDate == null 1986 || mLastUidIoUsageSummaryReportedDate == null) { 1987 readMetadataFileLocked(); 1988 } 1989 } 1990 ZonedDateTime reportDate; 1991 switch (atomTag) { 1992 case CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY: 1993 synchronized (mLock) { 1994 reportDate = mLastSystemIoUsageSummaryReportedDate; 1995 } 1996 pullAtomsForWeeklyPeriodsSinceReportedDate(reportDate, data, 1997 this::pullSystemIoUsageSummaryStatsEvents); 1998 synchronized (mLock) { 1999 mLastSystemIoUsageSummaryReportedDate = mTimeSource.getCurrentDate(); 2000 } 2001 break; 2002 case CAR_WATCHDOG_UID_IO_USAGE_SUMMARY: 2003 synchronized (mLock) { 2004 reportDate = mLastUidIoUsageSummaryReportedDate; 2005 } 2006 pullAtomsForWeeklyPeriodsSinceReportedDate(reportDate, data, 2007 this::pullUidIoUsageSummaryStatsEvents); 2008 synchronized (mLock) { 2009 mLastUidIoUsageSummaryReportedDate = mTimeSource.getCurrentDate(); 2010 } 2011 break; 2012 } 2013 return PULL_SUCCESS; 2014 } 2015 2016 @GuardedBy("mLock") readMetadataFileLocked()2017 private void readMetadataFileLocked() { 2018 mLastSystemIoUsageSummaryReportedDate = mLastUidIoUsageSummaryReportedDate = 2019 mTimeSource.getCurrentDate().minus(RETENTION_PERIOD); 2020 File file = getWatchdogMetadataFile(); 2021 if (!file.exists()) { 2022 Slogf.e(TAG, "Watchdog metadata file '%s' doesn't exist", file.getAbsoluteFile()); 2023 return; 2024 } 2025 AtomicFile atomicFile = new AtomicFile(file); 2026 try (FileInputStream fis = atomicFile.openRead()) { 2027 JsonReader reader = new JsonReader(new InputStreamReader(fis, StandardCharsets.UTF_8)); 2028 reader.beginObject(); 2029 while (reader.hasNext()) { 2030 String name = reader.nextName(); 2031 switch (name) { 2032 case SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE: 2033 mLastSystemIoUsageSummaryReportedDate = 2034 ZonedDateTime.parse(reader.nextString(), 2035 DateTimeFormatter.ISO_DATE_TIME.withZone(ZONE_OFFSET)); 2036 break; 2037 case UID_IO_USAGE_SUMMARY_REPORTED_DATE: 2038 mLastUidIoUsageSummaryReportedDate = 2039 ZonedDateTime.parse(reader.nextString(), 2040 DateTimeFormatter.ISO_DATE_TIME.withZone(ZONE_OFFSET)); 2041 break; 2042 default: 2043 Slogf.w(TAG, "Unrecognized key: %s", name); 2044 reader.skipValue(); 2045 } 2046 } 2047 reader.endObject(); 2048 if (DEBUG) { 2049 Slogf.e(TAG, "Successfully read watchdog metadata file '%s'", 2050 file.getAbsoluteFile()); 2051 } 2052 } catch (IOException e) { 2053 Slogf.e(TAG, e, "Failed to read watchdog metadata file '%s'", file.getAbsoluteFile()); 2054 } catch (NumberFormatException | IllegalStateException | DateTimeParseException e) { 2055 Slogf.e(TAG, e, "Unexpected format in watchdog metadata file '%s'", 2056 file.getAbsoluteFile()); 2057 } 2058 } 2059 pullAtomsForWeeklyPeriodsSinceReportedDate(ZonedDateTime reportedDate, List<StatsEvent> data, BiConsumer<Pair<ZonedDateTime, ZonedDateTime>, List<StatsEvent>> pullAtomCallback)2060 private void pullAtomsForWeeklyPeriodsSinceReportedDate(ZonedDateTime reportedDate, 2061 List<StatsEvent> data, BiConsumer<Pair<ZonedDateTime, ZonedDateTime>, 2062 List<StatsEvent>> pullAtomCallback) { 2063 ZonedDateTime now = mTimeSource.getCurrentDate(); 2064 ZonedDateTime nextReportWeekStartDate = reportedDate.with(ChronoField.DAY_OF_WEEK, 1) 2065 .truncatedTo(ChronoUnit.DAYS); 2066 while (ChronoUnit.WEEKS.between(nextReportWeekStartDate, now) > 0) { 2067 pullAtomCallback.accept( 2068 new Pair<>(nextReportWeekStartDate, nextReportWeekStartDate.plusWeeks(1)), 2069 data); 2070 nextReportWeekStartDate = nextReportWeekStartDate.plusWeeks(1); 2071 } 2072 } 2073 pullSystemIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, List<StatsEvent> data)2074 private void pullSystemIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, 2075 List<StatsEvent> data) { 2076 List<AtomsProto.CarWatchdogDailyIoUsageSummary> dailyIoUsageSummaries = 2077 mWatchdogStorage.getDailySystemIoUsageSummaries( 2078 mIoUsageSummaryMinSystemTotalWrittenBytes, period.first.toEpochSecond(), 2079 period.second.toEpochSecond()); 2080 if (dailyIoUsageSummaries == null) { 2081 Slogf.i(TAG, "No system I/O usage summary stats available to pull"); 2082 return; 2083 } 2084 2085 AtomsProto.CarWatchdogEventTimePeriod evenTimePeriod = 2086 AtomsProto.CarWatchdogEventTimePeriod.newBuilder() 2087 .setPeriod(AtomsProto.CarWatchdogEventTimePeriod.Period.WEEKLY).build(); 2088 data.add(CarStatsLog.buildStatsEvent(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY, 2089 AtomsProto.CarWatchdogIoUsageSummary.newBuilder() 2090 .setEventTimePeriod(evenTimePeriod) 2091 .addAllDailyIoUsageSummary(dailyIoUsageSummaries).build() 2092 .toByteArray(), 2093 period.first.toEpochSecond() * 1000)); 2094 2095 Slogf.i(TAG, "Successfully pulled system I/O usage summary stats"); 2096 } 2097 pullUidIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, List<StatsEvent> data)2098 private void pullUidIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, 2099 List<StatsEvent> data) { 2100 // Fetch summaries for twice the top N user packages because if the UID cannot be resolved 2101 // for some user packages, the fetched summaries will still contain enough entries to pull. 2102 List<WatchdogStorage.UserPackageDailySummaries> topUsersDailyIoUsageSummaries = 2103 mWatchdogStorage.getTopUsersDailyIoUsageSummaries(mUidIoUsageSummaryTopCount * 2, 2104 mIoUsageSummaryMinSystemTotalWrittenBytes, 2105 period.first.toEpochSecond(), period.second.toEpochSecond()); 2106 if (topUsersDailyIoUsageSummaries == null) { 2107 Slogf.i(TAG, "No top users' I/O usage summary stats available to pull"); 2108 return; 2109 } 2110 2111 SparseArray<List<String>> genericPackageNamesByUserId = new SparseArray<>(); 2112 for (int i = 0; i < topUsersDailyIoUsageSummaries.size(); ++i) { 2113 WatchdogStorage.UserPackageDailySummaries entry = 2114 topUsersDailyIoUsageSummaries.get(i); 2115 List<String> genericPackageNames = genericPackageNamesByUserId.get(entry.userId); 2116 if (genericPackageNames == null) { 2117 genericPackageNames = new ArrayList<>(); 2118 } 2119 genericPackageNames.add(entry.packageName); 2120 genericPackageNamesByUserId.put(entry.userId, genericPackageNames); 2121 } 2122 2123 SparseArray<Map<String, Integer>> packageUidsByUserId = 2124 getPackageUidsForUsers(genericPackageNamesByUserId); 2125 2126 AtomsProto.CarWatchdogEventTimePeriod.Builder evenTimePeriodBuilder = 2127 AtomsProto.CarWatchdogEventTimePeriod.newBuilder() 2128 .setPeriod(AtomsProto.CarWatchdogEventTimePeriod.Period.WEEKLY); 2129 2130 long startEpochMillis = period.first.toEpochSecond() * 1000; 2131 int numPulledUidSummaryStats = 0; 2132 for (int i = 0; i < topUsersDailyIoUsageSummaries.size() 2133 && numPulledUidSummaryStats < mUidIoUsageSummaryTopCount; ++i) { 2134 WatchdogStorage.UserPackageDailySummaries entry = topUsersDailyIoUsageSummaries.get(i); 2135 Map<String, Integer> uidsByGenericPackageName = packageUidsByUserId.get(entry.userId); 2136 if (uidsByGenericPackageName == null 2137 || !uidsByGenericPackageName.containsKey(entry.packageName)) { 2138 Slogf.e(TAG, "Failed to fetch uid for package %s and user %d. So, skipping " 2139 + "reporting stats for this user package", entry.packageName, entry.userId); 2140 continue; 2141 } 2142 data.add(CarStatsLog.buildStatsEvent(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY, 2143 uidsByGenericPackageName.get(entry.packageName), 2144 AtomsProto.CarWatchdogIoUsageSummary.newBuilder() 2145 .setEventTimePeriod(evenTimePeriodBuilder) 2146 .addAllDailyIoUsageSummary(entry.dailyIoUsageSummaries).build() 2147 .toByteArray(), 2148 startEpochMillis)); 2149 ++numPulledUidSummaryStats; 2150 } 2151 2152 Slogf.e(TAG, "Successfully pulled top %d users' I/O usage summary stats", 2153 numPulledUidSummaryStats); 2154 } 2155 2156 /** 2157 * Gets the UID for the resource usage's user package. 2158 * 2159 * <p>If the package resource usage's UID is not valid, fetches the UID for the user package 2160 * from the package manager. Returns {@code INVALID_UID} if the package manager cannot find the 2161 * package. </p> 2162 */ getOrFetchUid(PackageResourceUsage usage, String packageName)2163 private int getOrFetchUid(PackageResourceUsage usage, String packageName) { 2164 int uid = usage.getUid(); 2165 if (uid == INVALID_UID) { 2166 uid = getPackageUidAsUser(mContext.getPackageManager(), packageName, 2167 usage.userId); 2168 } 2169 return uid; 2170 } 2171 getPackageUidsForUsers( SparseArray<List<String>> genericPackageNamesByUserId)2172 private SparseArray<Map<String, Integer>> getPackageUidsForUsers( 2173 SparseArray<List<String>> genericPackageNamesByUserId) { 2174 PackageManager pm = mContext.getPackageManager(); 2175 SparseArray<Map<String, Integer>> packageUidsByUserId = new SparseArray<>(); 2176 for (int i = 0; i < genericPackageNamesByUserId.size(); ++i) { 2177 int userId = genericPackageNamesByUserId.keyAt(i); 2178 Map<String, Integer> uidsByGenericPackageName = getPackageUidsForUser(pm, 2179 genericPackageNamesByUserId.valueAt(i), userId); 2180 if (!uidsByGenericPackageName.isEmpty()) { 2181 packageUidsByUserId.put(userId, uidsByGenericPackageName); 2182 } 2183 } 2184 return packageUidsByUserId; 2185 } 2186 2187 /** 2188 * Returns UIDs for the given generic package names belonging to the given user. 2189 * 2190 * <p>{@code pm.getInstalledPackagesAsUser} call is expensive as it fetches all installed 2191 * packages for the given user. Thus this method should be called for all packages that requires 2192 * the UIDs to be resolved in a single call. 2193 */ getPackageUidsForUser(PackageManager pm, List<String> genericPackageNames, int userId)2194 private Map<String, Integer> getPackageUidsForUser(PackageManager pm, 2195 List<String> genericPackageNames, int userId) { 2196 Map<String, Integer> uidsByGenericPackageNames = new ArrayMap<>(); 2197 Set<String> resolveSharedUserIds = new ArraySet<>(); 2198 for (int i = 0; i < genericPackageNames.size(); ++i) { 2199 String genericPackageName = genericPackageNames.get(i); 2200 PackageResourceUsage usage; 2201 synchronized (mLock) { 2202 usage = mUsageByUserPackage.get(getUserPackageUniqueId(userId, 2203 genericPackageName)); 2204 } 2205 if (usage != null && usage.getUid() != INVALID_UID) { 2206 uidsByGenericPackageNames.put(genericPackageName, usage.getUid()); 2207 continue; 2208 } 2209 if (isSharedPackage(genericPackageName)) { 2210 resolveSharedUserIds.add( 2211 genericPackageName.substring(SHARED_PACKAGE_PREFIX.length())); 2212 continue; 2213 } 2214 int uid = getPackageUidAsUser(pm, genericPackageName, userId); 2215 if (uid != INVALID_UID) { 2216 uidsByGenericPackageNames.put(genericPackageName, uid); 2217 } 2218 } 2219 if (resolveSharedUserIds.isEmpty()) { 2220 return uidsByGenericPackageNames; 2221 } 2222 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId); 2223 for (int i = 0; i < packageInfos.size() && !resolveSharedUserIds.isEmpty(); ++i) { 2224 PackageInfo packageInfo = packageInfos.get(i); 2225 if (packageInfo.sharedUserId == null 2226 || !resolveSharedUserIds.contains(packageInfo.sharedUserId)) { 2227 continue; 2228 } 2229 int uid = getPackageUidAsUser(pm, packageInfo.packageName, userId); 2230 if (uid != INVALID_UID) { 2231 uidsByGenericPackageNames.put(SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId, 2232 uid); 2233 } 2234 resolveSharedUserIds.remove(packageInfo.sharedUserId); 2235 } 2236 return uidsByGenericPackageNames; 2237 } 2238 getPackageUidAsUser(PackageManager pm, String packageName, @UserIdInt int userId)2239 private int getPackageUidAsUser(PackageManager pm, String packageName, @UserIdInt int userId) { 2240 try { 2241 return PackageManagerHelper.getPackageUidAsUser(pm, packageName, userId); 2242 } catch (PackageManager.NameNotFoundException e) { 2243 Slogf.e(TAG, "Package %s for user %d is not found", packageName, userId); 2244 return INVALID_UID; 2245 } 2246 } 2247 getWatchdogMetadataFile()2248 private static File getWatchdogMetadataFile() { 2249 return new File(CarWatchdogService.getWatchdogDirFile(), METADATA_FILENAME); 2250 } 2251 getUserPackageUniqueId(@serIdInt int userId, String genericPackageName)2252 private static String getUserPackageUniqueId(@UserIdInt int userId, String genericPackageName) { 2253 return userId + USER_PACKAGE_SEPARATOR + genericPackageName; 2254 } 2255 2256 @VisibleForTesting toIoOveruseStatsBuilder( android.automotive.watchdog.IoOveruseStats internalStats, int totalTimesKilled, boolean isKillableOnOveruses)2257 static IoOveruseStats.Builder toIoOveruseStatsBuilder( 2258 android.automotive.watchdog.IoOveruseStats internalStats, 2259 int totalTimesKilled, boolean isKillableOnOveruses) { 2260 return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds) 2261 .setTotalOveruses(internalStats.totalOveruses) 2262 .setTotalTimesKilled(totalTimesKilled) 2263 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes)) 2264 .setKillableOnOveruse(isKillableOnOveruses) 2265 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes)); 2266 } 2267 toPerStateBytes( android.automotive.watchdog.PerStateBytes internalPerStateBytes)2268 private static PerStateBytes toPerStateBytes( 2269 android.automotive.watchdog.PerStateBytes internalPerStateBytes) { 2270 return new PerStateBytes(internalPerStateBytes.foregroundBytes, 2271 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes); 2272 } 2273 totalPerStateBytes( android.automotive.watchdog.PerStateBytes internalPerStateBytes)2274 private static long totalPerStateBytes( 2275 android.automotive.watchdog.PerStateBytes internalPerStateBytes) { 2276 BiFunction<Long, Long, Long> sum = (l, r) -> { 2277 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE; 2278 }; 2279 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes, 2280 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes); 2281 } 2282 getMinimumBytesWritten( @arWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag)2283 private static long getMinimumBytesWritten( 2284 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) { 2285 switch (minimumStatsIoFlag) { 2286 case 0: 2287 return 0; 2288 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB: 2289 return 1024 * 1024; 2290 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB: 2291 return 100 * 1024 * 1024; 2292 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB: 2293 return 1024 * 1024 * 1024; 2294 default: 2295 throw new IllegalArgumentException( 2296 "Must provide valid minimum stats flag for I/O resource"); 2297 } 2298 } 2299 2300 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2301 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config, 2302 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 2303 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig = 2304 new android.automotive.watchdog.internal.ResourceOveruseConfiguration(); 2305 internalConfig.componentType = config.getComponentType(); 2306 internalConfig.safeToKillPackages = config.getSafeToKillPackages(); 2307 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes(); 2308 internalConfig.packageMetadata = new ArrayList<>(); 2309 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) { 2310 if (entry.getKey().isEmpty()) { 2311 continue; 2312 } 2313 PackageMetadata metadata = new PackageMetadata(); 2314 metadata.packageName = entry.getKey(); 2315 switch(entry.getValue()) { 2316 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS: 2317 metadata.appCategoryType = ApplicationCategoryType.MAPS; 2318 break; 2319 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA: 2320 metadata.appCategoryType = ApplicationCategoryType.MEDIA; 2321 break; 2322 default: 2323 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s", 2324 entry.getValue(), metadata.packageName); 2325 continue; 2326 } 2327 internalConfig.packageMetadata.add(metadata); 2328 } 2329 internalConfig.resourceSpecificConfigurations = new ArrayList<>(); 2330 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0 2331 && config.getIoOveruseConfiguration() != null) { 2332 internalConfig.resourceSpecificConfigurations.add( 2333 toResourceSpecificConfiguration(config.getComponentType(), 2334 config.getIoOveruseConfiguration())); 2335 } 2336 return internalConfig; 2337 } 2338 2339 private static ResourceSpecificConfiguration toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config)2340 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) { 2341 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig = 2342 new android.automotive.watchdog.internal.IoOveruseConfiguration(); 2343 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold( 2344 toComponentTypeStr(componentType), config.getComponentLevelThresholds()); 2345 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds( 2346 config.getPackageSpecificThresholds()); 2347 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds( 2348 config.getAppCategorySpecificThresholds()); 2349 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) { 2350 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i); 2351 switch(threshold.name) { 2352 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS: 2353 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS; 2354 break; 2355 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA: 2356 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA; 2357 break; 2358 default: 2359 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN; 2360 } 2361 } 2362 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds( 2363 config.getSystemWideThresholds()); 2364 2365 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration(); 2366 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig); 2367 return resourceSpecificConfig; 2368 } 2369 2370 @VisibleForTesting toComponentTypeStr(int componentType)2371 static String toComponentTypeStr(int componentType) { 2372 switch(componentType) { 2373 case ComponentType.SYSTEM: 2374 return "SYSTEM"; 2375 case ComponentType.VENDOR: 2376 return "VENDOR"; 2377 case ComponentType.THIRD_PARTY: 2378 return "THIRD_PARTY"; 2379 default: 2380 return "UNKNOWN"; 2381 } 2382 } 2383 toPerStateIoOveruseThresholds( Map<String, PerStateBytes> thresholds)2384 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds( 2385 Map<String, PerStateBytes> thresholds) { 2386 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>(); 2387 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) { 2388 if (!thresholds.isEmpty()) { 2389 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(), 2390 entry.getValue())); 2391 } 2392 } 2393 return internalThresholds; 2394 } 2395 toPerStateIoOveruseThreshold(String name, PerStateBytes perStateBytes)2396 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name, 2397 PerStateBytes perStateBytes) { 2398 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold(); 2399 threshold.name = name; 2400 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes(); 2401 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes(); 2402 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes(); 2403 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes(); 2404 return threshold; 2405 } 2406 2407 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds)2408 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) { 2409 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds = 2410 new ArrayList<>(); 2411 for (int i = 0; i < thresholds.size(); ++i) { 2412 if (thresholds.get(i).getDurationInSeconds() == 0 2413 || thresholds.get(i).getWrittenBytesPerSecond() == 0) { 2414 continue; 2415 } 2416 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold = 2417 new android.automotive.watchdog.internal.IoOveruseAlertThreshold(); 2418 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds(); 2419 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond(); 2420 internalThresholds.add(internalThreshold); 2421 } 2422 return internalThresholds; 2423 } 2424 toResourceOveruseConfiguration( android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2425 private static ResourceOveruseConfiguration toResourceOveruseConfiguration( 2426 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig, 2427 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 2428 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>(); 2429 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) { 2430 String categoryTypeStr; 2431 switch (internalConfig.packageMetadata.get(i).appCategoryType) { 2432 case ApplicationCategoryType.MAPS: 2433 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS; 2434 break; 2435 case ApplicationCategoryType.MEDIA: 2436 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA; 2437 break; 2438 default: 2439 continue; 2440 } 2441 packagesToAppCategoryTypes.put( 2442 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr); 2443 } 2444 ResourceOveruseConfiguration.Builder configBuilder = 2445 new ResourceOveruseConfiguration.Builder( 2446 internalConfig.componentType, 2447 internalConfig.safeToKillPackages, 2448 internalConfig.vendorPackagePrefixes, 2449 packagesToAppCategoryTypes); 2450 for (ResourceSpecificConfiguration resourceSpecificConfig : 2451 internalConfig.resourceSpecificConfigurations) { 2452 if (resourceSpecificConfig.getTag() 2453 == ResourceSpecificConfiguration.ioOveruseConfiguration 2454 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) { 2455 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration( 2456 resourceSpecificConfig.getIoOveruseConfiguration())); 2457 } 2458 } 2459 return configBuilder.build(); 2460 } 2461 toIoOveruseConfiguration( android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig)2462 private static IoOveruseConfiguration toIoOveruseConfiguration( 2463 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) { 2464 PerStateBytes componentLevelThresholds = 2465 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes); 2466 ArrayMap<String, PerStateBytes> packageSpecificThresholds = 2467 toPerStateBytesMap(internalConfig.packageSpecificThresholds); 2468 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds = 2469 toPerStateBytesMap(internalConfig.categorySpecificThresholds); 2470 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS, 2471 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS); 2472 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA, 2473 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA); 2474 List<IoOveruseAlertThreshold> systemWideThresholds = 2475 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds); 2476 2477 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder( 2478 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds, 2479 systemWideThresholds); 2480 return configBuilder.build(); 2481 } 2482 toPerStateBytesMap( List<PerStateIoOveruseThreshold> thresholds)2483 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap( 2484 List<PerStateIoOveruseThreshold> thresholds) { 2485 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>(); 2486 for (int i = 0; i < thresholds.size(); ++i) { 2487 thresholdsMap.put( 2488 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes)); 2489 } 2490 return thresholdsMap; 2491 } 2492 toIoOveruseAlertThresholds( List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds)2493 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds( 2494 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) { 2495 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>(); 2496 for (int i = 0; i < internalThresholds.size(); ++i) { 2497 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds, 2498 internalThresholds.get(i).writtenBytesPerSecond)); 2499 } 2500 return thresholds; 2501 } 2502 checkResourceOveruseConfigs( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2503 private static void checkResourceOveruseConfigs( 2504 List<ResourceOveruseConfiguration> configurations, 2505 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 2506 ArraySet<Integer> seenComponentTypes = new ArraySet<>(); 2507 for (int i = 0; i < configurations.size(); ++i) { 2508 ResourceOveruseConfiguration config = configurations.get(i); 2509 if (seenComponentTypes.contains(config.getComponentType())) { 2510 throw new IllegalArgumentException( 2511 "Cannot provide duplicate configurations for the same component type"); 2512 } 2513 checkResourceOveruseConfig(config, resourceOveruseFlag); 2514 seenComponentTypes.add(config.getComponentType()); 2515 } 2516 } 2517 checkResourceOveruseConfig(ResourceOveruseConfiguration config, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2518 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config, 2519 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 2520 int componentType = config.getComponentType(); 2521 if (toComponentTypeStr(componentType).equals("UNKNOWN")) { 2522 throw new IllegalArgumentException( 2523 "Invalid component type in the configuration: " + componentType); 2524 } 2525 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0 2526 && config.getIoOveruseConfiguration() == null) { 2527 throw new IllegalArgumentException("Must provide I/O overuse configuration"); 2528 } 2529 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType); 2530 } 2531 checkIoOveruseConfig(IoOveruseConfiguration config, int componentType)2532 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) { 2533 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0 2534 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0 2535 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) { 2536 throw new IllegalArgumentException( 2537 "For component: " + toComponentTypeStr(componentType) 2538 + " some thresholds are zero for: " 2539 + config.getComponentLevelThresholds().toString()); 2540 } 2541 if (componentType == ComponentType.SYSTEM) { 2542 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds(); 2543 if (systemThresholds.isEmpty()) { 2544 throw new IllegalArgumentException( 2545 "Empty system-wide alert thresholds provided in " 2546 + toComponentTypeStr(componentType) 2547 + " config."); 2548 } 2549 for (int i = 0; i < systemThresholds.size(); i++) { 2550 checkIoOveruseAlertThreshold(systemThresholds.get(i)); 2551 } 2552 } 2553 } 2554 checkIoOveruseAlertThreshold( IoOveruseAlertThreshold ioOveruseAlertThreshold)2555 private static void checkIoOveruseAlertThreshold( 2556 IoOveruseAlertThreshold ioOveruseAlertThreshold) { 2557 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) { 2558 throw new IllegalArgumentException( 2559 "System wide threshold duration must be greater than zero for: " 2560 + ioOveruseAlertThreshold); 2561 } 2562 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) { 2563 throw new IllegalArgumentException( 2564 "System wide threshold written bytes per second must be greater than zero for: " 2565 + ioOveruseAlertThreshold); 2566 } 2567 } 2568 isSharedPackage(String genericPackageName)2569 private static boolean isSharedPackage(String genericPackageName) { 2570 return genericPackageName.startsWith(SHARED_PACKAGE_PREFIX); 2571 } 2572 replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey)2573 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) { 2574 PerStateBytes perStateBytes = map.get(oldKey); 2575 if (perStateBytes != null) { 2576 map.put(newKey, perStateBytes); 2577 map.remove(oldKey); 2578 } 2579 } 2580 toNumDays(@arWatchdogManager.StatsPeriod int maxStatsPeriod)2581 private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 2582 switch(maxStatsPeriod) { 2583 case STATS_PERIOD_CURRENT_DAY: 2584 return 0; 2585 case STATS_PERIOD_PAST_3_DAYS: 2586 return 3; 2587 case STATS_PERIOD_PAST_7_DAYS: 2588 return 7; 2589 case STATS_PERIOD_PAST_15_DAYS: 2590 return 15; 2591 case STATS_PERIOD_PAST_30_DAYS: 2592 return 30; 2593 default: 2594 throw new IllegalArgumentException( 2595 "Invalid max stats period provided: " + maxStatsPeriod); 2596 } 2597 } 2598 2599 @VisibleForTesting constructCarWatchdogIoOveruseStats( AtomsProto.CarWatchdogIoOveruseStats.Period period, AtomsProto.CarWatchdogPerStateBytes threshold, AtomsProto.CarWatchdogPerStateBytes writtenBytes)2600 static AtomsProto.CarWatchdogIoOveruseStats constructCarWatchdogIoOveruseStats( 2601 AtomsProto.CarWatchdogIoOveruseStats.Period period, 2602 AtomsProto.CarWatchdogPerStateBytes threshold, 2603 AtomsProto.CarWatchdogPerStateBytes writtenBytes) { 2604 // TODO(b/184310189): Report uptime once daemon pushes it to CarService. 2605 return AtomsProto.CarWatchdogIoOveruseStats.newBuilder() 2606 .setPeriod(period) 2607 .setThreshold(threshold) 2608 .setWrittenBytes(writtenBytes).build(); 2609 } 2610 2611 @VisibleForTesting constructCarWatchdogPerStateBytes( long foregroundBytes, long backgroundBytes, long garageModeBytes)2612 static AtomsProto.CarWatchdogPerStateBytes constructCarWatchdogPerStateBytes( 2613 long foregroundBytes, long backgroundBytes, long garageModeBytes) { 2614 AtomsProto.CarWatchdogPerStateBytes.Builder perStateBytesBuilder = 2615 AtomsProto.CarWatchdogPerStateBytes.newBuilder(); 2616 if (foregroundBytes != 0) { 2617 perStateBytesBuilder.setForegroundBytes(foregroundBytes); 2618 } 2619 if (backgroundBytes != 0) { 2620 perStateBytesBuilder.setBackgroundBytes(backgroundBytes); 2621 } 2622 if (garageModeBytes != 0) { 2623 perStateBytesBuilder.setGarageModeBytes(garageModeBytes); 2624 } 2625 return perStateBytesBuilder.build(); 2626 } 2627 toEnabledStateString(int enabledState)2628 private static String toEnabledStateString(int enabledState) { 2629 switch (enabledState) { 2630 case COMPONENT_ENABLED_STATE_DEFAULT: 2631 return "COMPONENT_ENABLED_STATE_DEFAULT"; 2632 case COMPONENT_ENABLED_STATE_ENABLED: 2633 return "COMPONENT_ENABLED_STATE_ENABLED"; 2634 case COMPONENT_ENABLED_STATE_DISABLED: 2635 return "COMPONENT_ENABLED_STATE_DISABLED"; 2636 case COMPONENT_ENABLED_STATE_DISABLED_USER: 2637 return "COMPONENT_ENABLED_STATE_DISABLED_USER"; 2638 case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 2639 return "COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED"; 2640 default: 2641 return "UNKNOWN COMPONENT ENABLED STATE"; 2642 } 2643 } 2644 toUxStateString(@xStateType int uxState)2645 private static String toUxStateString(@UxStateType int uxState) { 2646 switch (uxState) { 2647 case UX_STATE_NO_DISTRACTION: 2648 return "UX_STATE_NO_DISTRACTION"; 2649 case UX_STATE_USER_NOTIFICATION: 2650 return "UX_STATE_USER_NOTIFICATION"; 2651 case UX_STATE_NO_INTERACTION: 2652 return "UX_STATE_NO_INTERACTION"; 2653 default: 2654 return "UNKNOWN UX STATE"; 2655 } 2656 } 2657 2658 private final class PackageResourceUsage { 2659 public final String genericPackageName; 2660 public @UserIdInt final int userId; 2661 public final PackageIoUsage ioUsage = new PackageIoUsage(); 2662 private @KillableState int mKillableState; 2663 private int mUid; 2664 2665 /** Must be called only after acquiring {@link mLock} */ PackageResourceUsage(@serIdInt int userId, String genericPackageName, @KillableState int defaultKillableState)2666 PackageResourceUsage(@UserIdInt int userId, String genericPackageName, 2667 @KillableState int defaultKillableState) { 2668 this.genericPackageName = genericPackageName; 2669 this.userId = userId; 2670 this.mKillableState = defaultKillableState; 2671 this.mUid = INVALID_UID; 2672 } 2673 isSharedPackage()2674 public boolean isSharedPackage() { 2675 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX); 2676 } 2677 getUniqueId()2678 public String getUniqueId() { 2679 return getUserPackageUniqueId(userId, genericPackageName); 2680 } 2681 getUid()2682 public int getUid() { 2683 return mUid; 2684 } 2685 update(int uid, android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes, @KillableState int defaultKillableState)2686 public void update(int uid, android.automotive.watchdog.IoOveruseStats internalStats, 2687 android.automotive.watchdog.PerStateBytes forgivenWriteBytes, 2688 @KillableState int defaultKillableState) { 2689 // Package UID would change if it was re-installed, so keep it up-to-date. 2690 mUid = uid; 2691 if (!internalStats.killableOnOveruse) { 2692 /* 2693 * Killable value specified in the internal stats is provided by the native daemon. 2694 * This value reflects whether or not an application is safe-to-kill on overuse. 2695 * This setting is from the I/O overuse configuration specified by the system and 2696 * vendor services and doesn't reflect the user choices. Thus if the internal stats 2697 * specify the application is not killable, the application is not safe-to-kill. 2698 */ 2699 mKillableState = KILLABLE_STATE_NEVER; 2700 } else if (mKillableState == KILLABLE_STATE_NEVER) { 2701 /* 2702 * This case happens when a previously unsafe to kill system/vendor package was 2703 * recently marked as safe-to-kill so update the old state to the default value. 2704 */ 2705 mKillableState = defaultKillableState; 2706 } 2707 ioUsage.update(internalStats, forgivenWriteBytes); 2708 } 2709 getResourceOveruseStatsBuilder()2710 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() { 2711 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId)); 2712 } 2713 2714 getIoOveruseStats()2715 public IoOveruseStats getIoOveruseStats() { 2716 if (!ioUsage.hasUsage()) { 2717 return null; 2718 } 2719 return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER); 2720 } 2721 getKillableState()2722 public @KillableState int getKillableState() { 2723 return mKillableState; 2724 } 2725 setKillableState(@illableState int killableState)2726 public void setKillableState(@KillableState int killableState) { 2727 mKillableState = killableState; 2728 } 2729 verifyAndSetKillableState(boolean isKillable)2730 public boolean verifyAndSetKillableState(boolean isKillable) { 2731 if (mKillableState == KILLABLE_STATE_NEVER) { 2732 return false; 2733 } 2734 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO; 2735 return true; 2736 } 2737 syncAndFetchKillableState(int myComponentType, boolean isSafeToKill, @KillableState int defaultKillableState)2738 public int syncAndFetchKillableState(int myComponentType, boolean isSafeToKill, 2739 @KillableState int defaultKillableState) { 2740 /* 2741 * The killable state goes out-of-sync: 2742 * 1. When the on-device safe-to-kill list was recently updated and the user package 2743 * didn't have any resource usage so the native daemon didn't update the killable state. 2744 * 2. When a package has no resource usage and is initialized outside of processing the 2745 * latest resource usage stats. 2746 */ 2747 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) { 2748 mKillableState = KILLABLE_STATE_NEVER; 2749 } else if (mKillableState == KILLABLE_STATE_NEVER) { 2750 mKillableState = defaultKillableState; 2751 } 2752 return mKillableState; 2753 } 2754 resetStats()2755 public void resetStats() { 2756 ioUsage.resetStats(); 2757 } 2758 } 2759 2760 /** Defines I/O usage fields for a package. */ 2761 public static final class PackageIoUsage { 2762 private static final android.automotive.watchdog.PerStateBytes DEFAULT_PER_STATE_BYTES = 2763 new android.automotive.watchdog.PerStateBytes(); 2764 private static final int MISSING_VALUE = -1; 2765 2766 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats; 2767 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes; 2768 private int mForgivenOveruses; 2769 private int mHistoricalNotForgivenOveruses; 2770 private int mTotalTimesKilled; 2771 PackageIoUsage()2772 private PackageIoUsage() { 2773 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES; 2774 mForgivenOveruses = 0; 2775 mHistoricalNotForgivenOveruses = MISSING_VALUE; 2776 mTotalTimesKilled = 0; 2777 } 2778 PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes, int forgivenOveruses, int totalTimesKilled)2779 public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats, 2780 android.automotive.watchdog.PerStateBytes forgivenWriteBytes, int forgivenOveruses, 2781 int totalTimesKilled) { 2782 mIoOveruseStats = ioOveruseStats; 2783 mForgivenWriteBytes = forgivenWriteBytes; 2784 mForgivenOveruses = forgivenOveruses; 2785 mTotalTimesKilled = totalTimesKilled; 2786 mHistoricalNotForgivenOveruses = MISSING_VALUE; 2787 } 2788 2789 /** Returns the I/O overuse stats related to the package. */ getInternalIoOveruseStats()2790 public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() { 2791 return mIoOveruseStats; 2792 } 2793 2794 /** Returns the forgiven write bytes. */ getForgivenWriteBytes()2795 public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() { 2796 return mForgivenWriteBytes; 2797 } 2798 2799 /** Returns the number of forgiven overuses today. */ getForgivenOveruses()2800 public int getForgivenOveruses() { 2801 return mForgivenOveruses; 2802 } 2803 2804 /** 2805 * Returns the number of not forgiven overuses. These are overuses that have not been 2806 * attributed previously to a package's recurring overuse. 2807 */ getNotForgivenOveruses()2808 public int getNotForgivenOveruses() { 2809 if (!hasUsage()) { 2810 return 0; 2811 } 2812 int historicalNotForgivenOveruses = 2813 mHistoricalNotForgivenOveruses != MISSING_VALUE 2814 ? mHistoricalNotForgivenOveruses : 0; 2815 return (mIoOveruseStats.totalOveruses - mForgivenOveruses) 2816 + historicalNotForgivenOveruses; 2817 } 2818 2819 /** Sets historical not forgiven overuses. */ setHistoricalNotForgivenOveruses(int historicalNotForgivenOveruses)2820 public void setHistoricalNotForgivenOveruses(int historicalNotForgivenOveruses) { 2821 mHistoricalNotForgivenOveruses = historicalNotForgivenOveruses; 2822 } 2823 2824 /** Forgives all the I/O overuse stats' overuses. */ forgiveOveruses()2825 public void forgiveOveruses() { 2826 if (!hasUsage()) { 2827 return; 2828 } 2829 mForgivenOveruses = mIoOveruseStats.totalOveruses; 2830 mHistoricalNotForgivenOveruses = 0; 2831 } 2832 2833 /** Returns the total number of times the package was killed. */ getTotalTimesKilled()2834 public int getTotalTimesKilled() { 2835 return mTotalTimesKilled; 2836 } 2837 shouldForgiveHistoricalOveruses()2838 boolean shouldForgiveHistoricalOveruses() { 2839 return mHistoricalNotForgivenOveruses != MISSING_VALUE; 2840 } 2841 hasUsage()2842 boolean hasUsage() { 2843 return mIoOveruseStats != null; 2844 } 2845 overwrite(PackageIoUsage ioUsage)2846 void overwrite(PackageIoUsage ioUsage) { 2847 mIoOveruseStats = ioUsage.mIoOveruseStats; 2848 mForgivenWriteBytes = ioUsage.mForgivenWriteBytes; 2849 mTotalTimesKilled = ioUsage.mTotalTimesKilled; 2850 mHistoricalNotForgivenOveruses = ioUsage.mHistoricalNotForgivenOveruses; 2851 } 2852 update(android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes)2853 void update(android.automotive.watchdog.IoOveruseStats internalStats, 2854 android.automotive.watchdog.PerStateBytes forgivenWriteBytes) { 2855 mIoOveruseStats = internalStats; 2856 mForgivenWriteBytes = forgivenWriteBytes; 2857 } 2858 getIoOveruseStats(boolean isKillable)2859 IoOveruseStats getIoOveruseStats(boolean isKillable) { 2860 return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build(); 2861 } 2862 exceedsThreshold()2863 boolean exceedsThreshold() { 2864 if (!hasUsage()) { 2865 return false; 2866 } 2867 android.automotive.watchdog.PerStateBytes remaining = 2868 mIoOveruseStats.remainingWriteBytes; 2869 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0 2870 || remaining.garageModeBytes == 0; 2871 } 2872 killed()2873 void killed() { 2874 ++mTotalTimesKilled; 2875 } 2876 resetStats()2877 void resetStats() { 2878 mIoOveruseStats = null; 2879 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES; 2880 mForgivenOveruses = 0; 2881 mHistoricalNotForgivenOveruses = MISSING_VALUE; 2882 mTotalTimesKilled = 0; 2883 } 2884 } 2885 2886 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient { 2887 public final IResourceOveruseListener listener; 2888 public final @CarWatchdogManager.ResourceOveruseFlag int flag; 2889 public final int pid; 2890 public final int uid; 2891 public final boolean isListenerForSystem; 2892 ResourceOveruseListenerInfo(IResourceOveruseListener listener, @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid, boolean isListenerForSystem)2893 ResourceOveruseListenerInfo(IResourceOveruseListener listener, 2894 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid, 2895 boolean isListenerForSystem) { 2896 this.listener = listener; 2897 this.flag = flag; 2898 this.pid = pid; 2899 this.uid = uid; 2900 this.isListenerForSystem = isListenerForSystem; 2901 } 2902 2903 @Override binderDied()2904 public void binderDied() { 2905 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died", 2906 isListenerForSystem ? " for system" : "", pid); 2907 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo = 2908 listenerInfosByUid -> { 2909 ArrayList<ResourceOveruseListenerInfo> listenerInfos = 2910 listenerInfosByUid.get(uid); 2911 if (listenerInfos == null) { 2912 return; 2913 } 2914 listenerInfos.remove(this); 2915 if (listenerInfos.isEmpty()) { 2916 listenerInfosByUid.remove(uid); 2917 } 2918 }; 2919 synchronized (mLock) { 2920 if (isListenerForSystem) { 2921 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid); 2922 } else { 2923 removeListenerInfo.accept(mOveruseListenerInfosByUid); 2924 } 2925 } 2926 unlinkToDeath(); 2927 } 2928 notifyListener(@arWatchdogManager.ResourceOveruseFlag int resourceType, int overusingUid, String overusingGenericPackageName, ResourceOveruseStats resourceOveruseStats)2929 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType, 2930 int overusingUid, String overusingGenericPackageName, 2931 ResourceOveruseStats resourceOveruseStats) { 2932 if ((flag & resourceType) == 0) { 2933 return; 2934 } 2935 try { 2936 listener.onOveruse(resourceOveruseStats); 2937 } catch (RemoteException e) { 2938 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by " 2939 + "package(uid %d, generic package name '%s'): %s", 2940 (isListenerForSystem ? "system listener" : "listener"), uid, pid, 2941 overusingUid, overusingGenericPackageName, e); 2942 } 2943 } 2944 linkToDeath()2945 private void linkToDeath() throws RemoteException { 2946 listener.asBinder().linkToDeath(this, 0); 2947 } 2948 unlinkToDeath()2949 private void unlinkToDeath() { 2950 listener.asBinder().unlinkToDeath(this, 0); 2951 } 2952 } 2953 } 2954