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