1 /* 2 * Copyright (C) 2020 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.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED; 20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING; 21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED; 22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING; 24 import static android.content.Intent.ACTION_PACKAGE_CHANGED; 25 import static android.content.Intent.ACTION_REBOOT; 26 import static android.content.Intent.ACTION_SHUTDOWN; 27 import static android.content.Intent.ACTION_USER_REMOVED; 28 29 import static com.android.car.CarLog.TAG_WATCHDOG; 30 import static com.android.car.CarServiceUtils.assertAnyPermission; 31 import static com.android.car.CarServiceUtils.assertPermission; 32 import static com.android.car.CarServiceUtils.isEventAnyOfTypes; 33 import static com.android.car.CarServiceUtils.runOnMain; 34 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 35 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION; 36 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS; 37 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP; 38 39 import android.annotation.NonNull; 40 import android.annotation.UserIdInt; 41 import android.automotive.watchdog.internal.GarageMode; 42 import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem; 43 import android.automotive.watchdog.internal.PackageInfo; 44 import android.automotive.watchdog.internal.PackageIoOveruseStats; 45 import android.automotive.watchdog.internal.PowerCycle; 46 import android.automotive.watchdog.internal.ResourceStats; 47 import android.automotive.watchdog.internal.StateType; 48 import android.automotive.watchdog.internal.UserPackageIoUsageStats; 49 import android.automotive.watchdog.internal.UserState; 50 import android.car.Car; 51 import android.car.builtin.util.Slogf; 52 import android.car.hardware.power.CarPowerManager; 53 import android.car.hardware.power.CarPowerPolicy; 54 import android.car.hardware.power.CarPowerPolicyFilter; 55 import android.car.hardware.power.ICarPowerPolicyListener; 56 import android.car.hardware.power.ICarPowerStateListener; 57 import android.car.hardware.power.PowerComponent; 58 import android.car.user.UserLifecycleEventFilter; 59 import android.car.watchdog.CarWatchdogManager; 60 import android.car.watchdog.ICarWatchdogService; 61 import android.car.watchdog.ICarWatchdogServiceCallback; 62 import android.car.watchdog.IResourceOveruseListener; 63 import android.car.watchdog.PackageKillableState; 64 import android.car.watchdog.ResourceOveruseConfiguration; 65 import android.car.watchdog.ResourceOveruseStats; 66 import android.car.watchdoglib.CarWatchdogDaemonHelper; 67 import android.content.BroadcastReceiver; 68 import android.content.Context; 69 import android.content.Intent; 70 import android.content.IntentFilter; 71 import android.os.RemoteException; 72 import android.os.ServiceSpecificException; 73 import android.os.UserHandle; 74 import android.os.UserManager; 75 import android.util.ArraySet; 76 import android.util.Log; 77 import android.util.proto.ProtoOutputStream; 78 79 import com.android.car.CarLocalServices; 80 import com.android.car.CarLog; 81 import com.android.car.CarServiceBase; 82 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 83 import com.android.car.internal.dep.Trace; 84 import com.android.car.internal.util.ArrayUtils; 85 import com.android.car.internal.util.IndentingPrintWriter; 86 import com.android.car.power.CarPowerManagementService; 87 import com.android.car.systeminterface.SystemInterface; 88 import com.android.car.user.CarUserService; 89 import com.android.internal.annotations.GuardedBy; 90 import com.android.internal.annotations.VisibleForTesting; 91 92 import java.io.File; 93 import java.lang.ref.WeakReference; 94 import java.time.Instant; 95 import java.util.Collections; 96 import java.util.List; 97 import java.util.Objects; 98 99 /** 100 * Service to implement CarWatchdogManager API. 101 */ 102 public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase { 103 static final String TAG = CarLog.tagFor(CarWatchdogService.class); 104 static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG); 105 static final String ACTION_GARAGE_MODE_ON = 106 "com.android.server.jobscheduler.GARAGE_MODE_ON"; 107 static final String ACTION_GARAGE_MODE_OFF = 108 "com.android.server.jobscheduler.GARAGE_MODE_OFF"; 109 110 @VisibleForTesting 111 static final int MISSING_ARG_VALUE = -1; 112 113 private static final String FALLBACK_DATA_SYSTEM_CAR_DIR_PATH = "/data/system/car"; 114 private static final String WATCHDOG_DIR_NAME = "watchdog"; 115 116 private static final TimeSource SYSTEM_INSTANCE = new TimeSource() { 117 @Override 118 public Instant now() { 119 return Instant.now(); 120 } 121 122 @Override 123 public String toString() { 124 return "System time instance"; 125 } 126 }; 127 128 private final Context mContext; 129 private final ICarWatchdogServiceForSystemImpl mWatchdogServiceForSystem; 130 private final PackageInfoHandler mPackageInfoHandler; 131 private final WatchdogStorage mWatchdogStorage; 132 private final WatchdogProcessHandler mWatchdogProcessHandler; 133 private final WatchdogPerfHandler mWatchdogPerfHandler; 134 private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener; 135 136 private CarWatchdogDaemonHelper mCarWatchdogDaemonHelper; 137 138 /* 139 * TODO(b/192481350): Listen for GarageMode change notification rather than depending on the 140 * system_server broadcast when the CarService internal API for listening GarageMode change is 141 * implemented. 142 */ 143 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 144 @Override 145 public void onReceive(Context context, Intent intent) { 146 String action = intent.getAction(); 147 Trace.beginSection("CarWatchdogService-broadcast-" + action); 148 switch (action) { 149 case CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION: 150 case CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS: 151 case CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP: 152 mWatchdogPerfHandler.processUserNotificationIntent(intent); 153 break; 154 case ACTION_GARAGE_MODE_ON: 155 case ACTION_GARAGE_MODE_OFF: 156 int garageMode; 157 synchronized (mLock) { 158 garageMode = mCurrentGarageMode = Objects.equals(action, 159 ACTION_GARAGE_MODE_ON) 160 ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF; 161 } 162 mWatchdogPerfHandler.onGarageModeChange(garageMode); 163 if (garageMode == GarageMode.GARAGE_MODE_ON) { 164 mWatchdogStorage.shrinkDatabase(); 165 } 166 notifyGarageModeChange(garageMode); 167 break; 168 case ACTION_REBOOT: 169 case ACTION_SHUTDOWN: 170 // FLAG_RECEIVER_FOREGROUND is checked to ignore the intent from UserController 171 // when a user is stopped. 172 if ((intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) == 0) { 173 break; 174 } 175 int powerCycle = PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER; 176 try { 177 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE, 178 powerCycle, /* arg2= */ 0); 179 if (DEBUG) { 180 Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", 181 powerCycle); 182 } 183 } catch (Exception e) { 184 Slogf.w(TAG, e, "Notifying power cycle state change failed"); 185 } 186 break; 187 case ACTION_USER_REMOVED: { 188 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 189 int userId = userHandle.getIdentifier(); 190 try { 191 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, 192 userId, UserState.USER_STATE_REMOVED); 193 if (DEBUG) { 194 Slogf.d(TAG, "Notified car watchdog daemon of removed user %d", 195 userId); 196 } 197 } catch (RemoteException e) { 198 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of removed user %d", 199 userId); 200 } 201 mWatchdogPerfHandler.deleteUser(userId); 202 break; 203 } 204 case ACTION_PACKAGE_CHANGED: { 205 mWatchdogPerfHandler.processPackageChangedIntent(intent); 206 break; 207 } 208 default: 209 Slogf.i(TAG, "Ignoring unknown intent %s", intent); 210 } 211 Trace.endSection(); 212 } 213 }; 214 215 private final ICarPowerStateListener mCarPowerStateListener = 216 new ICarPowerStateListener.Stub() { 217 @Override 218 public void onStateChanged(int state, long expirationTimeMs) { 219 CarPowerManagementService powerService = 220 CarLocalServices.getService(CarPowerManagementService.class); 221 if (powerService == null) { 222 return; 223 } 224 int powerState = powerService.getPowerState(); 225 int powerCycle = carPowerStateToPowerCycle(powerState); 226 if (powerCycle < 0) { 227 return; 228 } 229 Trace.beginSection("CarWatchdogService-powerStateChanged-" 230 + CarPowerManagementService.powerStateToString(powerState)); 231 switch (powerCycle) { 232 case PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE: 233 // Perform time consuming disk I/O operation during shutdown prepare to avoid 234 // incomplete I/O. 235 mWatchdogPerfHandler.writeMetadataFile(); 236 break; 237 case PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER: 238 // Watchdog service and daemon performs garage mode monitoring so delay writing 239 // to database until after shutdown enter. 240 mWatchdogPerfHandler.writeToDatabase(); 241 break; 242 case PowerCycle.POWER_CYCLE_SUSPEND_EXIT: 243 break; 244 // ON covers resume. 245 case PowerCycle.POWER_CYCLE_RESUME: 246 // There might be outdated & incorrect info. We should reset them before 247 // starting to do health check. 248 mWatchdogProcessHandler.prepareHealthCheck(); 249 break; 250 default: 251 return; 252 } 253 notifyPowerCycleChange(powerCycle); 254 Trace.endSection(); 255 } 256 }; 257 258 private final ICarPowerPolicyListener mCarDisplayPowerPolicyListener = 259 new ICarPowerPolicyListener.Stub() { 260 @Override 261 public void onPolicyChanged(CarPowerPolicy appliedPolicy, 262 CarPowerPolicy accumulatedPolicy) { 263 Trace.beginSection("CarWatchdogService-carPowerPolicyChanged-" 264 + appliedPolicy.getPolicyId()); 265 boolean isDisplayEnabled = 266 appliedPolicy.isComponentEnabled(PowerComponent.DISPLAY); 267 boolean didStateChange = false; 268 synchronized (mLock) { 269 didStateChange = mIsDisplayEnabled != isDisplayEnabled; 270 mIsDisplayEnabled = isDisplayEnabled; 271 } 272 if (didStateChange) { 273 mWatchdogPerfHandler.onDisplayStateChanged(isDisplayEnabled); 274 } 275 Trace.endSection();; 276 } 277 }; 278 279 private final Object mLock = new Object(); 280 @GuardedBy("mLock") 281 private boolean mReadyToRespond; 282 @GuardedBy("mLock") 283 private boolean mIsConnected; 284 @GuardedBy("mLock") 285 private @GarageMode int mCurrentGarageMode; 286 @GuardedBy("mLock") 287 private boolean mIsDisplayEnabled; 288 CarWatchdogService(Context context, Context carServiceBuiltinPackageContext)289 public CarWatchdogService(Context context, Context carServiceBuiltinPackageContext) { 290 this(context, carServiceBuiltinPackageContext, 291 new WatchdogStorage(context, SYSTEM_INSTANCE), SYSTEM_INSTANCE); 292 } 293 294 @VisibleForTesting CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, WatchdogStorage watchdogStorage, TimeSource timeSource)295 public CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, 296 WatchdogStorage watchdogStorage, TimeSource timeSource) { 297 this(context, carServiceBuiltinPackageContext, watchdogStorage, 298 timeSource, /*watchdogProcessHandler=*/ null, /*watchdogPerfHandler=*/ null); 299 } 300 301 @VisibleForTesting CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, WatchdogStorage watchdogStorage, TimeSource timeSource, WatchdogProcessHandler watchdogProcessHandler, WatchdogPerfHandler watchdogPerfHandler)302 CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, 303 WatchdogStorage watchdogStorage, TimeSource timeSource, 304 WatchdogProcessHandler watchdogProcessHandler, 305 WatchdogPerfHandler watchdogPerfHandler) { 306 mContext = context; 307 mWatchdogStorage = watchdogStorage; 308 mPackageInfoHandler = new PackageInfoHandler(mContext.getPackageManager()); 309 mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG); 310 mWatchdogServiceForSystem = new ICarWatchdogServiceForSystemImpl(this); 311 mWatchdogProcessHandler = watchdogProcessHandler != null ? watchdogProcessHandler 312 : new WatchdogProcessHandler(mWatchdogServiceForSystem, mCarWatchdogDaemonHelper, 313 mPackageInfoHandler); 314 mWatchdogPerfHandler = 315 watchdogPerfHandler != null ? watchdogPerfHandler : new WatchdogPerfHandler( 316 mContext, carServiceBuiltinPackageContext, 317 mCarWatchdogDaemonHelper, mPackageInfoHandler, mWatchdogStorage, 318 timeSource); 319 mConnectionListener = (isConnected) -> { 320 mWatchdogPerfHandler.onDaemonConnectionChange(isConnected); 321 synchronized (mLock) { 322 mIsConnected = isConnected; 323 } 324 registerToDaemon(); 325 }; 326 mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF; 327 mIsDisplayEnabled = true; 328 } 329 330 @VisibleForTesting setCarWatchdogDaemonHelper(CarWatchdogDaemonHelper helper)331 public void setCarWatchdogDaemonHelper(CarWatchdogDaemonHelper helper) { 332 mCarWatchdogDaemonHelper = helper; 333 } 334 335 @Override init()336 public void init() { 337 Trace.beginSection("CarWatchdogService.init"); 338 // TODO(b/266008677): The daemon reads the sendResourceUsageStatsEnabled sysprop at the 339 // moment the CarWatchdogService connects to it. Therefore, the property must be set by 340 // CarWatchdogService before connecting with the CarWatchdog daemon. Set the property to 341 // true to enable the sending of resource usage stats from the daemon. 342 mWatchdogProcessHandler.init(); 343 mWatchdogPerfHandler.init(); 344 subscribePowerManagementService(); 345 subscribeUserStateChange(); 346 subscribeBroadcastReceiver(); 347 mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener); 348 mCarWatchdogDaemonHelper.connect(); 349 // To make sure the main handler is ready for responding to car watchdog daemon, registering 350 // to the daemon is done through the main handler. Once the registration is completed, we 351 // can assume that the main handler is not too busy handling other stuffs. 352 postRegisterToDaemonMessage(); 353 if (DEBUG) { 354 Slogf.d(TAG, "CarWatchdogService is initialized"); 355 } 356 Trace.endSection(); 357 } 358 359 @Override release()360 public void release() { 361 Trace.beginSection("CarWatchdogService.release"); 362 mContext.unregisterReceiver(mBroadcastReceiver); 363 unsubscribePowerManagementService(); 364 mWatchdogPerfHandler.release(); 365 mWatchdogStorage.release(); 366 unregisterFromDaemon(); 367 mCarWatchdogDaemonHelper.disconnect(); 368 Trace.endSection(); 369 } 370 371 @Override 372 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)373 public void dump(IndentingPrintWriter writer) { 374 writer.println("*" + getClass().getSimpleName() + "*"); 375 writer.increaseIndent(); 376 synchronized (mLock) { 377 writer.println("Current garage mode: " + toGarageModeString(mCurrentGarageMode)); 378 } 379 mWatchdogProcessHandler.dump(writer); 380 mWatchdogPerfHandler.dump(writer); 381 writer.decreaseIndent(); 382 } 383 384 @Override 385 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)386 public void dumpProto(ProtoOutputStream proto) { 387 synchronized (mLock) { 388 proto.write(CarWatchdogDumpProto.CURRENT_GARAGE_MODE, mCurrentGarageMode); 389 } 390 mWatchdogProcessHandler.dumpProto(proto); 391 mWatchdogPerfHandler.dumpProto(proto); 392 } 393 394 /** 395 * Registers {@link android.car.watchdog.ICarWatchdogServiceCallback} to 396 * {@link CarWatchdogService}. 397 */ 398 @Override registerClient(ICarWatchdogServiceCallback client, int timeout)399 public void registerClient(ICarWatchdogServiceCallback client, int timeout) { 400 assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 401 mWatchdogProcessHandler.registerClient(client, timeout); 402 } 403 404 /** 405 * Unregisters {@link android.car.watchdog.ICarWatchdogServiceCallback} from 406 * {@link CarWatchdogService}. 407 */ 408 @Override unregisterClient(ICarWatchdogServiceCallback client)409 public void unregisterClient(ICarWatchdogServiceCallback client) { 410 assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 411 mWatchdogProcessHandler.unregisterClient(client); 412 } 413 414 /** 415 * Tells {@link CarWatchdogService} that the client is alive. 416 */ 417 @Override tellClientAlive(ICarWatchdogServiceCallback client, int sessionId)418 public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) { 419 assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 420 mWatchdogProcessHandler.tellClientAlive(client, sessionId); 421 } 422 423 /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the calling package. */ 424 @Override 425 @NonNull getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)426 public ResourceOveruseStats getResourceOveruseStats( 427 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 428 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 429 return mWatchdogPerfHandler.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod); 430 } 431 432 /** 433 * Returns {@link android.car.watchdog.ResourceOveruseStats} for all packages for the maximum 434 * specified period, and the specified resource types with stats greater than or equal to the 435 * minimum specified stats. 436 */ 437 @Override 438 @NonNull getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)439 public List<ResourceOveruseStats> getAllResourceOveruseStats( 440 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 441 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, 442 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 443 assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 444 return mWatchdogPerfHandler.getAllResourceOveruseStats(resourceOveruseFlag, 445 minimumStatsFlag, maxStatsPeriod); 446 } 447 448 /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the specified user package. */ 449 @Override 450 @NonNull getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)451 public ResourceOveruseStats getResourceOveruseStatsForUserPackage( 452 @NonNull String packageName, @NonNull UserHandle userHandle, 453 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 454 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 455 assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 456 return mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(packageName, userHandle, 457 resourceOveruseFlag, maxStatsPeriod); 458 } 459 460 /** 461 * Adds {@link android.car.watchdog.IResourceOveruseListener} for the calling package's resource 462 * overuse notifications. 463 */ 464 @Override addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)465 public void addResourceOveruseListener( 466 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 467 @NonNull IResourceOveruseListener listener) { 468 mWatchdogPerfHandler.addResourceOveruseListener(resourceOveruseFlag, listener); 469 } 470 471 /** 472 * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for the 473 * calling package's resource overuse notifications. 474 */ 475 @Override removeResourceOveruseListener(@onNull IResourceOveruseListener listener)476 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) { 477 mWatchdogPerfHandler.removeResourceOveruseListener(listener); 478 } 479 480 /** 481 * Adds {@link android.car.watchdog.IResourceOveruseListener} for all packages' resource overuse 482 * notifications. 483 */ 484 @Override addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)485 public void addResourceOveruseListenerForSystem( 486 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 487 @NonNull IResourceOveruseListener listener) { 488 assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 489 mWatchdogPerfHandler.addResourceOveruseListenerForSystem(resourceOveruseFlag, listener); 490 } 491 492 /** 493 * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for all 494 * packages' resource overuse notifications. 495 */ 496 @Override removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)497 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) { 498 assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 499 mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(listener); 500 } 501 502 /** Sets whether or not a user package is killable on resource overuse. */ 503 @Override setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)504 public void setKillablePackageAsUser(String packageName, UserHandle userHandle, 505 boolean isKillable) { 506 assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 507 mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle, isKillable); 508 } 509 510 /** 511 * Returns all {@link android.car.watchdog.PackageKillableState} on resource overuse for 512 * the specified user. 513 */ 514 @Override 515 @NonNull getPackageKillableStatesAsUser(UserHandle userHandle)516 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) { 517 assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 518 return mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle); 519 } 520 521 /** 522 * Sets {@link android.car.watchdog.ResourceOveruseConfiguration} for the specified resources. 523 */ 524 @Override 525 @CarWatchdogManager.ReturnCode setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)526 public int setResourceOveruseConfigurations( 527 List<ResourceOveruseConfiguration> configurations, 528 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) 529 throws RemoteException { 530 assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 531 return mWatchdogPerfHandler.setResourceOveruseConfigurations(configurations, 532 resourceOveruseFlag); 533 } 534 535 /** Returns the available {@link android.car.watchdog.ResourceOveruseConfiguration}. */ 536 @Override 537 @NonNull getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)538 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( 539 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 540 assertAnyPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, 541 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 542 return mWatchdogPerfHandler.getResourceOveruseConfigurations(resourceOveruseFlag); 543 } 544 545 /** 546 * Enables/disables the watchdog daemon client health check process. 547 */ controlProcessHealthCheck(boolean enable)548 public void controlProcessHealthCheck(boolean enable) { 549 assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 550 mWatchdogProcessHandler.controlProcessHealthCheck(enable); 551 } 552 553 /** 554 * Kills a specific package for a user due to resource overuse. 555 * 556 * @return whether package was killed 557 */ performResourceOveruseKill(String packageName, @UserIdInt int userId)558 public boolean performResourceOveruseKill(String packageName, @UserIdInt int userId) { 559 assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 560 return mWatchdogPerfHandler.disablePackageForUser(packageName, userId); 561 } 562 563 /** 564 * Sets the thread priority for a specific thread. 565 * 566 * The thread must belong to the calling process. 567 * 568 * @throws IllegalArgumentException If the policy/priority is not valid. 569 * @throws IllegalStateException If the provided tid does not belong to the calling process. 570 * @throws RemoteException If binder error happens. 571 * @throws ServiceSpecificException If car watchdog daemon failed to set the thread priority. 572 * @throws UnsupportedOperationException If the current android release doesn't support the API. 573 */ setThreadPriority(int pid, int tid, int uid, int policy, int priority)574 public void setThreadPriority(int pid, int tid, int uid, int policy, int priority) 575 throws RemoteException { 576 mCarWatchdogDaemonHelper.setThreadPriority(pid, tid, uid, policy, priority); 577 } 578 579 /** 580 * Gets the thread scheduling policy and priority for the specified thread. 581 * 582 * The thread must belong to the calling process. 583 * 584 * @throws IllegalStateException If the provided tid does not belong to the calling process or 585 * car watchdog daemon failed to get the priority. 586 * @throws RemoteException If binder error happens. 587 * @throws UnsupportedOperationException If the current android release doesn't support the API. 588 */ getThreadPriority(int pid, int tid, int uid)589 public int[] getThreadPriority(int pid, int tid, int uid) throws RemoteException { 590 try { 591 return mCarWatchdogDaemonHelper.getThreadPriority(pid, tid, uid); 592 } catch (ServiceSpecificException e) { 593 // Car watchdog daemon failed to get the priority. 594 throw new IllegalStateException(e); 595 } 596 } 597 598 @VisibleForTesting getClientCount(int timeout)599 int getClientCount(int timeout) { 600 return mWatchdogProcessHandler.getClientCount(timeout); 601 } 602 603 @VisibleForTesting setOveruseHandlingDelay(long millis)604 void setOveruseHandlingDelay(long millis) { 605 mWatchdogPerfHandler.setOveruseHandlingDelay(millis); 606 } 607 getWatchdogDirFile()608 static File getWatchdogDirFile() { 609 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class); 610 String systemCarDirPath = systemInterface == null ? FALLBACK_DATA_SYSTEM_CAR_DIR_PATH 611 : systemInterface.getSystemCarDir().getAbsolutePath(); 612 return new File(systemCarDirPath, WATCHDOG_DIR_NAME); 613 } 614 notifyAllUserStates()615 private void notifyAllUserStates() { 616 Trace.beginSection("CarWatchdogService.notifyAllUserStates"); 617 UserManager userManager = mContext.getSystemService(UserManager.class); 618 List<UserHandle> users = userManager.getUserHandles(/* excludeDying= */ false); 619 try { 620 // TODO(b/152780162): reduce the number of RPC calls(isUserRunning). 621 for (int i = 0; i < users.size(); ++i) { 622 UserHandle user = users.get(i); 623 int userState = userManager.isUserRunning(user) 624 ? UserState.USER_STATE_STARTED 625 : UserState.USER_STATE_STOPPED; 626 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, 627 user.getIdentifier(), userState); 628 mWatchdogProcessHandler.updateUserState(user.getIdentifier(), 629 userState == UserState.USER_STATE_STOPPED); 630 } 631 if (DEBUG) { 632 Slogf.d(TAG, "Notified car watchdog daemon of user states"); 633 } 634 } catch (RemoteException | RuntimeException e) { 635 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 636 // throws IllegalStateException. Catch the exception to avoid crashing the process. 637 Slogf.w(TAG, e, "Notifying latest user states failed"); 638 } 639 Trace.endSection(); 640 } 641 notifyPowerCycleChange(@owerCycle int powerCycle)642 private void notifyPowerCycleChange(@PowerCycle int powerCycle) { 643 try { 644 mCarWatchdogDaemonHelper.notifySystemStateChange( 645 StateType.POWER_CYCLE, powerCycle, MISSING_ARG_VALUE); 646 if (DEBUG) { 647 Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle); 648 } 649 } catch (RemoteException | RuntimeException e) { 650 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 651 // throws IllegalStateException. Catch the exception to avoid crashing the process. 652 Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle); 653 } 654 } 655 notifyGarageModeChange(@arageMode int garageMode)656 private void notifyGarageModeChange(@GarageMode int garageMode) { 657 try { 658 mCarWatchdogDaemonHelper.notifySystemStateChange( 659 StateType.GARAGE_MODE, garageMode, MISSING_ARG_VALUE); 660 if (DEBUG) { 661 Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode); 662 } 663 } catch (RemoteException | RuntimeException e) { 664 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 665 // throws IllegalStateException. Catch the exception to avoid crashing the process. 666 Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode); 667 } 668 } 669 postRegisterToDaemonMessage()670 private void postRegisterToDaemonMessage() { 671 runOnMain(() -> { 672 synchronized (mLock) { 673 mReadyToRespond = true; 674 } 675 registerToDaemon(); 676 }); 677 } 678 registerToDaemon()679 private void registerToDaemon() { 680 synchronized (mLock) { 681 if (!mIsConnected || !mReadyToRespond) { 682 return; 683 } 684 } 685 Trace.beginSection("CarWatchdogService.registerToDaemon"); 686 try { 687 mCarWatchdogDaemonHelper.registerCarWatchdogService(mWatchdogServiceForSystem); 688 if (DEBUG) { 689 Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon"); 690 } 691 } catch (RemoteException | RuntimeException e) { 692 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 693 // throws IllegalStateException. Catch the exception to avoid crashing the process. 694 Slogf.w(TAG, e, "Cannot register to car watchdog daemon"); 695 } 696 notifyAllUserStates(); 697 CarPowerManagementService powerService = 698 CarLocalServices.getService(CarPowerManagementService.class); 699 if (powerService != null) { 700 int powerState = powerService.getPowerState(); 701 int powerCycle = carPowerStateToPowerCycle(powerState); 702 if (powerCycle >= 0) { 703 notifyPowerCycleChange(powerCycle); 704 } else { 705 Slogf.i(TAG, "Skipping notifying %d power state", powerState); 706 } 707 } 708 int garageMode; 709 synchronized (mLock) { 710 // To avoid race condition, fetch {@link mCurrentGarageMode} just before 711 // the {@link notifyGarageModeChange} call. For instance, if {@code mCurrentGarageMode} 712 // changes before the above {@link notifyPowerCycleChange} call returns, 713 // the {@link garageMode}'s value will be out of date. 714 garageMode = mCurrentGarageMode; 715 } 716 notifyGarageModeChange(garageMode); 717 Trace.endSection(); 718 } 719 unregisterFromDaemon()720 private void unregisterFromDaemon() { 721 Trace.beginSection("CarWatchdogService.unregisterFromDaemon"); 722 try { 723 mCarWatchdogDaemonHelper.unregisterCarWatchdogService(mWatchdogServiceForSystem); 724 if (DEBUG) { 725 Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon"); 726 } 727 } catch (RemoteException | RuntimeException e) { 728 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 729 // throws IllegalStateException. Catch the exception to avoid crashing the process. 730 Slogf.w(TAG, e, "Cannot unregister from car watchdog daemon"); 731 } 732 Trace.endSection(); 733 } 734 subscribePowerManagementService()735 private void subscribePowerManagementService() { 736 CarPowerManagementService powerService = 737 CarLocalServices.getService(CarPowerManagementService.class); 738 if (powerService == null) { 739 Slogf.w(TAG, "Cannot get CarPowerManagementService"); 740 return; 741 } 742 powerService.registerListener(mCarPowerStateListener); 743 powerService.addPowerPolicyListener( 744 new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(), 745 mCarDisplayPowerPolicyListener); 746 } 747 unsubscribePowerManagementService()748 private void unsubscribePowerManagementService() { 749 CarPowerManagementService powerService = 750 CarLocalServices.getService(CarPowerManagementService.class); 751 if (powerService == null) { 752 Slogf.w(TAG, "Cannot get CarPowerManagementService"); 753 return; 754 } 755 powerService.unregisterListener(mCarPowerStateListener); 756 powerService.removePowerPolicyListener(mCarDisplayPowerPolicyListener); 757 } 758 subscribeUserStateChange()759 private void subscribeUserStateChange() { 760 CarUserService userService = CarLocalServices.getService(CarUserService.class); 761 if (userService == null) { 762 Slogf.w(TAG, "Cannot get CarUserService"); 763 return; 764 } 765 UserLifecycleEventFilter userEventFilter = 766 new UserLifecycleEventFilter.Builder() 767 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STARTING) 768 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING) 769 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING) 770 .addEventType(USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED) 771 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPED).build(); 772 userService.addUserLifecycleListener(userEventFilter, (event) -> { 773 if (!isEventAnyOfTypes(TAG, event, USER_LIFECYCLE_EVENT_TYPE_STARTING, 774 USER_LIFECYCLE_EVENT_TYPE_SWITCHING, USER_LIFECYCLE_EVENT_TYPE_UNLOCKING, 775 USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, USER_LIFECYCLE_EVENT_TYPE_STOPPED)) { 776 return; 777 } 778 779 int userId = event.getUserHandle().getIdentifier(); 780 int userState; 781 String userStateDesc; 782 switch (event.getEventType()) { 783 case USER_LIFECYCLE_EVENT_TYPE_STARTING: 784 mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ false); 785 userState = UserState.USER_STATE_STARTED; 786 userStateDesc = "STARTING"; 787 break; 788 case USER_LIFECYCLE_EVENT_TYPE_SWITCHING: 789 userState = UserState.USER_STATE_SWITCHING; 790 userStateDesc = "SWITCHING"; 791 break; 792 case USER_LIFECYCLE_EVENT_TYPE_UNLOCKING: 793 userState = UserState.USER_STATE_UNLOCKING; 794 userStateDesc = "UNLOCKING"; 795 break; 796 case USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED: 797 userState = UserState.USER_STATE_POST_UNLOCKED; 798 userStateDesc = "POST_UNLOCKED"; 799 break; 800 case USER_LIFECYCLE_EVENT_TYPE_STOPPED: 801 mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ true); 802 userState = UserState.USER_STATE_STOPPED; 803 userStateDesc = "STOPPING"; 804 break; 805 default: 806 return; 807 } 808 try { 809 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId, 810 userState); 811 if (DEBUG) { 812 Slogf.d(TAG, "Notified car watchdog daemon user %d's user state, %s", 813 userId, userStateDesc); 814 } 815 } catch (RemoteException | RuntimeException e) { 816 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 817 // throws IllegalStateException. Catch the exception to avoid crashing the process. 818 Slogf.w(TAG, e, "Notifying user state change failed"); 819 } 820 }); 821 } 822 subscribeBroadcastReceiver()823 private void subscribeBroadcastReceiver() { 824 IntentFilter filter = new IntentFilter(); 825 filter.addAction(CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION); 826 filter.addAction(ACTION_GARAGE_MODE_ON); 827 filter.addAction(ACTION_GARAGE_MODE_OFF); 828 filter.addAction(CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS); 829 filter.addAction(CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP); 830 filter.addAction(ACTION_USER_REMOVED); 831 filter.addAction(ACTION_REBOOT); 832 filter.addAction(ACTION_SHUTDOWN); 833 834 mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter, 835 Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, /* scheduler= */ null, 836 Context.RECEIVER_NOT_EXPORTED); 837 838 // The package data scheme applies only for the ACTION_PACKAGE_CHANGED action. So, add a 839 // filter for this action separately. Otherwise, the broadcast receiver won't receive 840 // notifications for other actions. 841 IntentFilter packageChangedFilter = new IntentFilter(); 842 packageChangedFilter.addAction(ACTION_PACKAGE_CHANGED); 843 packageChangedFilter.addDataScheme("package"); 844 845 mContext.registerReceiverForAllUsers(mBroadcastReceiver, packageChangedFilter, 846 /* broadcastPermission= */ null, /* scheduler= */ null, 847 Context.RECEIVER_NOT_EXPORTED); 848 } 849 carPowerStateToPowerCycle(int powerState)850 private static int carPowerStateToPowerCycle(int powerState) { 851 switch (powerState) { 852 // SHUTDOWN_PREPARE covers suspend and shutdown. 853 case CarPowerManager.STATE_SHUTDOWN_PREPARE: 854 return PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE; 855 case CarPowerManager.STATE_SHUTDOWN_ENTER: 856 case CarPowerManager.STATE_SUSPEND_ENTER: 857 case CarPowerManager.STATE_HIBERNATION_ENTER: 858 return PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER; 859 case CarPowerManager.STATE_SUSPEND_EXIT: 860 case CarPowerManager.STATE_HIBERNATION_EXIT: 861 return PowerCycle.POWER_CYCLE_SUSPEND_EXIT; 862 // ON covers resume. 863 case CarPowerManager.STATE_ON: 864 return PowerCycle.POWER_CYCLE_RESUME; 865 default: 866 Slogf.e(TAG, "Invalid power state: %d", powerState); 867 } 868 return -1; 869 } 870 toGarageModeString(@arageMode int garageMode)871 private static String toGarageModeString(@GarageMode int garageMode) { 872 switch (garageMode) { 873 case GarageMode.GARAGE_MODE_OFF: 874 return "GARAGE_MODE_OFF"; 875 case GarageMode.GARAGE_MODE_ON: 876 return "GARAGE_MODE_ON"; 877 default: 878 Slogf.e(TAG, "Invalid garage mode: %d", garageMode); 879 } 880 return "INVALID"; 881 } 882 883 private static final class ICarWatchdogServiceForSystemImpl 884 extends ICarWatchdogServiceForSystem.Stub { 885 private final WeakReference<CarWatchdogService> mService; 886 ICarWatchdogServiceForSystemImpl(CarWatchdogService service)887 ICarWatchdogServiceForSystemImpl(CarWatchdogService service) { 888 mService = new WeakReference<>(service); 889 } 890 891 @Override checkIfAlive(int sessionId, int timeout)892 public void checkIfAlive(int sessionId, int timeout) { 893 CarWatchdogService service = mService.get(); 894 if (service == null) { 895 Slogf.w(TAG, "CarWatchdogService is not available"); 896 return; 897 } 898 service.mWatchdogProcessHandler.postHealthCheckMessage(sessionId); 899 } 900 901 @Override prepareProcessTermination()902 public void prepareProcessTermination() { 903 Slogf.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon"); 904 } 905 906 @Override getPackageInfosForUids( int[] uids, List<String> vendorPackagePrefixes)907 public List<PackageInfo> getPackageInfosForUids( 908 int[] uids, List<String> vendorPackagePrefixes) { 909 if (ArrayUtils.isEmpty(uids)) { 910 Slogf.w(TAG, "UID list is empty"); 911 return Collections.emptyList(); 912 } 913 CarWatchdogService service = mService.get(); 914 if (service == null) { 915 Slogf.w(TAG, "CarWatchdogService is not available"); 916 return Collections.emptyList(); 917 } 918 return service.mPackageInfoHandler.getPackageInfosForUids(uids, vendorPackagePrefixes); 919 } 920 921 // TODO(b/269191275): This method was replaced by onLatestResourceStats in Android U. 922 // Make method no-op in Android W (N+2 releases). 923 @Override latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)924 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) { 925 if (packageIoOveruseStats.isEmpty()) { 926 Slogf.w(TAG, "Latest I/O overuse stats is empty"); 927 return; 928 } 929 CarWatchdogService service = mService.get(); 930 if (service == null) { 931 Slogf.w(TAG, "CarWatchdogService is not available"); 932 return; 933 } 934 service.mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats); 935 } 936 937 @Override onLatestResourceStats(List<ResourceStats> resourceStats)938 public void onLatestResourceStats(List<ResourceStats> resourceStats) { 939 // TODO(b/266008146): Handle the resourceUsageStats. 940 if (resourceStats.isEmpty()) { 941 Slogf.w(TAG, "Latest resource stats is empty"); 942 return; 943 } 944 CarWatchdogService service = mService.get(); 945 if (service == null) { 946 Slogf.w(TAG, "CarWatchdogService is not available"); 947 return; 948 } 949 for (int i = 0; i < resourceStats.size(); i++) { 950 ResourceStats stats = resourceStats.get(i); 951 if (stats.resourceOveruseStats == null 952 || stats.resourceOveruseStats.packageIoOveruseStats.isEmpty()) { 953 Slogf.w(TAG, "Received latest I/O overuse stats is empty"); 954 continue; 955 } 956 service.mWatchdogPerfHandler.latestIoOveruseStats( 957 stats.resourceOveruseStats.packageIoOveruseStats); 958 } 959 } 960 961 @Override resetResourceOveruseStats(List<String> packageNames)962 public void resetResourceOveruseStats(List<String> packageNames) { 963 if (packageNames.isEmpty()) { 964 Slogf.w(TAG, "Provided an empty package name to reset resource overuse stats"); 965 return; 966 } 967 CarWatchdogService service = mService.get(); 968 if (service == null) { 969 Slogf.w(TAG, "CarWatchdogService is not available"); 970 return; 971 } 972 service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames)); 973 } 974 975 // TODO(b/273354756): This method was replaced by an async request/response pattern 976 // Android U. Requests for the I/O stats are received through the requestTodayIoUsageStats 977 // method. And responses are sent through the carwatchdog daemon via 978 // ICarWatchdog#onTodayIoUsageStats. Make method no-op in Android W (N+2 releases). 979 @Override getTodayIoUsageStats()980 public List<UserPackageIoUsageStats> getTodayIoUsageStats() { 981 CarWatchdogService service = mService.get(); 982 if (service == null) { 983 Slogf.w(TAG, "CarWatchdogService is not available"); 984 return Collections.emptyList(); 985 } 986 return service.mWatchdogPerfHandler.getTodayIoUsageStats(); 987 } 988 989 @Override requestAidlVhalPid()990 public void requestAidlVhalPid() { 991 CarWatchdogService service = mService.get(); 992 if (service == null) { 993 Slogf.w(TAG, "CarWatchdogService is not available"); 994 return; 995 } 996 service.mWatchdogProcessHandler.asyncFetchAidlVhalPid(); 997 } 998 999 @Override requestTodayIoUsageStats()1000 public void requestTodayIoUsageStats() { 1001 CarWatchdogService service = mService.get(); 1002 if (service == null) { 1003 Slogf.w(TAG, "CarWatchdogService is not available"); 1004 return; 1005 } 1006 service.mWatchdogPerfHandler.asyncFetchTodayIoUsageStats(); 1007 } 1008 1009 @Override getInterfaceHash()1010 public String getInterfaceHash() { 1011 return ICarWatchdogServiceForSystemImpl.HASH; 1012 } 1013 1014 @Override getInterfaceVersion()1015 public int getInterfaceVersion() { 1016 return ICarWatchdogServiceForSystemImpl.VERSION; 1017 } 1018 } 1019 } 1020