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