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