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