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