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_STARTING; 20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED; 21 import static android.content.Intent.ACTION_USER_REMOVED; 22 23 import static com.android.car.CarLog.TAG_WATCHDOG; 24 25 import android.annotation.NonNull; 26 import android.automotive.watchdog.internal.GarageMode; 27 import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem; 28 import android.automotive.watchdog.internal.PackageInfo; 29 import android.automotive.watchdog.internal.PackageIoOveruseStats; 30 import android.automotive.watchdog.internal.PowerCycle; 31 import android.automotive.watchdog.internal.StateType; 32 import android.automotive.watchdog.internal.UserPackageIoUsageStats; 33 import android.automotive.watchdog.internal.UserState; 34 import android.car.Car; 35 import android.car.hardware.power.CarPowerManager.CarPowerStateListener; 36 import android.car.hardware.power.CarPowerPolicy; 37 import android.car.hardware.power.CarPowerPolicyFilter; 38 import android.car.hardware.power.ICarPowerPolicyListener; 39 import android.car.hardware.power.ICarPowerStateListener; 40 import android.car.hardware.power.PowerComponent; 41 import android.car.watchdog.CarWatchdogManager; 42 import android.car.watchdog.ICarWatchdogService; 43 import android.car.watchdog.ICarWatchdogServiceCallback; 44 import android.car.watchdog.IResourceOveruseListener; 45 import android.car.watchdog.PackageKillableState; 46 import android.car.watchdog.ResourceOveruseConfiguration; 47 import android.car.watchdog.ResourceOveruseStats; 48 import android.car.watchdoglib.CarWatchdogDaemonHelper; 49 import android.content.BroadcastReceiver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.IntentFilter; 53 import android.content.pm.UserInfo; 54 import android.os.RemoteException; 55 import android.os.UserHandle; 56 import android.os.UserManager; 57 import android.util.ArraySet; 58 import android.util.IndentingPrintWriter; 59 60 import com.android.car.CarLocalServices; 61 import com.android.car.CarLog; 62 import com.android.car.CarServiceBase; 63 import com.android.car.CarServiceUtils; 64 import com.android.car.ICarImpl; 65 import com.android.car.power.CarPowerManagementService; 66 import com.android.car.systeminterface.SystemInterface; 67 import com.android.car.user.CarUserService; 68 import com.android.internal.annotations.GuardedBy; 69 import com.android.internal.annotations.VisibleForTesting; 70 import com.android.internal.util.ArrayUtils; 71 import com.android.server.utils.Slogf; 72 73 import java.io.File; 74 import java.lang.ref.WeakReference; 75 import java.time.Instant; 76 import java.util.List; 77 78 /** 79 * Service to implement CarWatchdogManager API. 80 */ 81 public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase { 82 static final boolean DEBUG = false; // STOPSHIP if true 83 static final String TAG = CarLog.tagFor(CarWatchdogService.class); 84 static final String ACTION_GARAGE_MODE_ON = 85 "com.android.server.jobscheduler.GARAGE_MODE_ON"; 86 static final String ACTION_GARAGE_MODE_OFF = 87 "com.android.server.jobscheduler.GARAGE_MODE_OFF"; 88 static final String ACTION_LAUNCH_APP_SETTINGS = 89 "com.android.car.watchdog.ACTION_LAUNCH_APP_SETTINGS"; 90 static final String ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION = 91 "com.android.car.watchdog.ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION"; 92 static final String ACTION_RESOURCE_OVERUSE_DISABLE_APP = 93 "com.android.car.watchdog.ACTION_RESOURCE_OVERUSE_DISABLE_APP"; 94 95 @VisibleForTesting 96 static final int MISSING_ARG_VALUE = -1; 97 98 private static final String FALLBACK_DATA_SYSTEM_CAR_DIR_PATH = "/data/system/car"; 99 private static final String WATCHDOG_DIR_NAME = "watchdog"; 100 101 private static final TimeSource SYSTEM_INSTANCE = new TimeSource() { 102 @Override 103 public Instant now() { 104 return Instant.now(); 105 } 106 107 @Override 108 public String toString() { 109 return "System time instance"; 110 } 111 }; 112 113 private final Context mContext; 114 private final ICarWatchdogServiceForSystemImpl mWatchdogServiceForSystem; 115 private final PackageInfoHandler mPackageInfoHandler; 116 private final WatchdogStorage mWatchdogStorage; 117 private final WatchdogProcessHandler mWatchdogProcessHandler; 118 private final WatchdogPerfHandler mWatchdogPerfHandler; 119 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper; 120 private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener; 121 /* 122 * TODO(b/192481350): Listen for GarageMode change notification rather than depending on the 123 * system_server broadcast when the CarService internal API for listening GarageMode change is 124 * implemented. 125 */ 126 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 127 @Override 128 public void onReceive(Context context, Intent intent) { 129 String action = intent.getAction(); 130 switch (action) { 131 case ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION: 132 case ACTION_LAUNCH_APP_SETTINGS: 133 case ACTION_RESOURCE_OVERUSE_DISABLE_APP: 134 mWatchdogPerfHandler.handleIntent(context, intent); 135 break; 136 case ACTION_GARAGE_MODE_ON: 137 case ACTION_GARAGE_MODE_OFF: 138 int garageMode; 139 synchronized (mLock) { 140 garageMode = mCurrentGarageMode = action.equals(ACTION_GARAGE_MODE_ON) 141 ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF; 142 } 143 mWatchdogPerfHandler.onGarageModeChange(garageMode); 144 if (garageMode == GarageMode.GARAGE_MODE_ON) { 145 mWatchdogStorage.shrinkDatabase(); 146 } 147 notifyGarageModeChange(garageMode); 148 return; 149 case ACTION_USER_REMOVED: 150 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 151 int userId = userHandle.getIdentifier(); 152 try { 153 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, 154 userId, UserState.USER_STATE_REMOVED); 155 if (DEBUG) { 156 Slogf.d(TAG, "Notified car watchdog daemon of removed user %d", 157 userId); 158 } 159 } catch (RemoteException e) { 160 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of removed user %d", 161 userId); 162 } 163 mWatchdogPerfHandler.deleteUser(userId); 164 return; 165 } 166 } 167 }; 168 169 private final ICarPowerStateListener mCarPowerStateListener = 170 new ICarPowerStateListener.Stub() { 171 @Override 172 public void onStateChanged(int state) { 173 CarPowerManagementService powerService = 174 CarLocalServices.getService(CarPowerManagementService.class); 175 if (powerService == null) { 176 return; 177 } 178 int powerCycle = carPowerStateToPowerCycle(powerService.getPowerState()); 179 switch (powerCycle) { 180 case PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE: 181 // Perform time consuming disk I/O operation during shutdown prepare to avoid 182 // incomplete I/O. 183 mWatchdogPerfHandler.writeMetadataFile(); 184 break; 185 case PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER: 186 // Watchdog service and daemon performs garage mode monitoring so delay writing 187 // to database until after shutdown enter. 188 mWatchdogPerfHandler.writeToDatabase(); 189 break; 190 // ON covers resume. 191 case PowerCycle.POWER_CYCLE_RESUME: 192 // There might be outdated & incorrect info. We should reset them before 193 // starting to do health check. 194 mWatchdogProcessHandler.prepareHealthCheck(); 195 break; 196 default: 197 return; 198 } 199 notifyPowerCycleChange(powerCycle); 200 } 201 }; 202 203 private final ICarPowerPolicyListener mCarDisplayPowerPolicyListener = 204 new ICarPowerPolicyListener.Stub() { 205 @Override 206 public void onPolicyChanged(CarPowerPolicy appliedPolicy, 207 CarPowerPolicy accumulatedPolicy) { 208 boolean isDisplayEnabled = 209 appliedPolicy.isComponentEnabled(PowerComponent.DISPLAY); 210 boolean didStateChange = false; 211 synchronized (mLock) { 212 didStateChange = mIsDisplayEnabled != isDisplayEnabled; 213 mIsDisplayEnabled = isDisplayEnabled; 214 } 215 if (didStateChange) { 216 mWatchdogPerfHandler.onDisplayStateChanged(isDisplayEnabled); 217 } 218 } 219 }; 220 221 private final Object mLock = new Object(); 222 @GuardedBy("mLock") 223 private boolean mReadyToRespond; 224 @GuardedBy("mLock") 225 private boolean mIsConnected; 226 @GuardedBy("mLock") 227 private @GarageMode int mCurrentGarageMode; 228 @GuardedBy("mLock") 229 private boolean mIsDisplayEnabled; 230 CarWatchdogService(Context context)231 public CarWatchdogService(Context context) { 232 this(context, new WatchdogStorage(context, SYSTEM_INSTANCE), 233 new UserNotificationHelper(context), SYSTEM_INSTANCE); 234 } 235 236 @VisibleForTesting CarWatchdogService(Context context, WatchdogStorage watchdogStorage, UserNotificationHelper userNotificationHelper, TimeSource timeSource)237 CarWatchdogService(Context context, WatchdogStorage watchdogStorage, 238 UserNotificationHelper userNotificationHelper, TimeSource timeSource) { 239 mContext = context; 240 mWatchdogStorage = watchdogStorage; 241 mPackageInfoHandler = new PackageInfoHandler(mContext.getPackageManager()); 242 mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG); 243 mWatchdogServiceForSystem = new ICarWatchdogServiceForSystemImpl(this); 244 mWatchdogProcessHandler = new WatchdogProcessHandler(mWatchdogServiceForSystem, 245 mCarWatchdogDaemonHelper); 246 mWatchdogPerfHandler = new WatchdogPerfHandler(mContext, mCarWatchdogDaemonHelper, 247 mPackageInfoHandler, mWatchdogStorage, userNotificationHelper, timeSource); 248 mConnectionListener = (isConnected) -> { 249 mWatchdogPerfHandler.onDaemonConnectionChange(isConnected); 250 synchronized (mLock) { 251 mIsConnected = isConnected; 252 } 253 registerToDaemon(); 254 }; 255 mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF; 256 mIsDisplayEnabled = true; 257 } 258 259 @Override init()260 public void init() { 261 mWatchdogProcessHandler.init(); 262 mWatchdogPerfHandler.init(); 263 subscribePowerManagementService(); 264 subscribeUserStateChange(); 265 subscribeBroadcastReceiver(); 266 mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener); 267 mCarWatchdogDaemonHelper.connect(); 268 // To make sure the main handler is ready for responding to car watchdog daemon, registering 269 // to the daemon is done through the main handler. Once the registration is completed, we 270 // can assume that the main handler is not too busy handling other stuffs. 271 postRegisterToDaemonMessage(); 272 if (DEBUG) { 273 Slogf.d(TAG, "CarWatchdogService is initialized"); 274 } 275 } 276 277 @Override release()278 public void release() { 279 mContext.unregisterReceiver(mBroadcastReceiver); 280 unsubscribePowerManagementService(); 281 mWatchdogPerfHandler.release(); 282 mWatchdogStorage.release(); 283 unregisterFromDaemon(); 284 mCarWatchdogDaemonHelper.disconnect(); 285 } 286 287 @Override dump(IndentingPrintWriter writer)288 public void dump(IndentingPrintWriter writer) { 289 writer.println("*" + getClass().getSimpleName() + "*"); 290 writer.increaseIndent(); 291 synchronized (mLock) { 292 writer.println("Current garage mode: " + toGarageModeString(mCurrentGarageMode)); 293 } 294 mWatchdogProcessHandler.dump(writer); 295 mWatchdogPerfHandler.dump(writer); 296 writer.decreaseIndent(); 297 } 298 299 /** 300 * Registers {@link android.car.watchdog.ICarWatchdogServiceCallback} to 301 * {@link CarWatchdogService}. 302 */ 303 @Override registerClient(ICarWatchdogServiceCallback client, int timeout)304 public void registerClient(ICarWatchdogServiceCallback client, int timeout) { 305 ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 306 mWatchdogProcessHandler.registerClient(client, timeout); 307 } 308 309 /** 310 * Unregisters {@link android.car.watchdog.ICarWatchdogServiceCallback} from 311 * {@link CarWatchdogService}. 312 */ 313 @Override unregisterClient(ICarWatchdogServiceCallback client)314 public void unregisterClient(ICarWatchdogServiceCallback client) { 315 ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 316 mWatchdogProcessHandler.unregisterClient(client); 317 } 318 319 /** 320 * Tells {@link CarWatchdogService} that the client is alive. 321 */ 322 @Override tellClientAlive(ICarWatchdogServiceCallback client, int sessionId)323 public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) { 324 ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 325 mWatchdogProcessHandler.tellClientAlive(client, sessionId); 326 } 327 328 /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the calling package. */ 329 @Override 330 @NonNull getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)331 public ResourceOveruseStats getResourceOveruseStats( 332 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 333 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 334 return mWatchdogPerfHandler.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod); 335 } 336 337 /** 338 * Returns {@link android.car.watchdog.ResourceOveruseStats} for all packages for the maximum 339 * specified period, and the specified resource types with stats greater than or equal to the 340 * minimum specified stats. 341 */ 342 @Override 343 @NonNull getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)344 public List<ResourceOveruseStats> getAllResourceOveruseStats( 345 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 346 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, 347 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 348 ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 349 return mWatchdogPerfHandler.getAllResourceOveruseStats(resourceOveruseFlag, 350 minimumStatsFlag, maxStatsPeriod); 351 } 352 353 /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the specified user package. */ 354 @Override 355 @NonNull getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)356 public ResourceOveruseStats getResourceOveruseStatsForUserPackage( 357 @NonNull String packageName, @NonNull UserHandle userHandle, 358 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 359 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { 360 ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 361 return mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(packageName, userHandle, 362 resourceOveruseFlag, maxStatsPeriod); 363 } 364 365 /** 366 * Adds {@link android.car.watchdog.IResourceOveruseListener} for the calling package's resource 367 * overuse notifications. 368 */ 369 @Override addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)370 public void addResourceOveruseListener( 371 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 372 @NonNull IResourceOveruseListener listener) { 373 mWatchdogPerfHandler.addResourceOveruseListener(resourceOveruseFlag, listener); 374 } 375 376 /** 377 * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for the 378 * calling package's resource overuse notifications. 379 */ 380 @Override removeResourceOveruseListener(@onNull IResourceOveruseListener listener)381 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) { 382 mWatchdogPerfHandler.removeResourceOveruseListener(listener); 383 } 384 385 /** 386 * Adds {@link android.car.watchdog.IResourceOveruseListener} for all packages' resource overuse 387 * notifications. 388 */ 389 @Override addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)390 public void addResourceOveruseListenerForSystem( 391 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, 392 @NonNull IResourceOveruseListener listener) { 393 ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 394 mWatchdogPerfHandler.addResourceOveruseListenerForSystem(resourceOveruseFlag, listener); 395 } 396 397 /** 398 * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for all 399 * packages' resource overuse notifications. 400 */ 401 @Override removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)402 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) { 403 ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 404 mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(listener); 405 } 406 407 /** Sets whether or not a user package is killable on resource overuse. */ 408 @Override setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)409 public void setKillablePackageAsUser(String packageName, UserHandle userHandle, 410 boolean isKillable) { 411 ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 412 mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle, isKillable); 413 } 414 415 /** 416 * Returns all {@link android.car.watchdog.PackageKillableState} on resource overuse for 417 * the specified user. 418 */ 419 @Override 420 @NonNull getPackageKillableStatesAsUser(UserHandle userHandle)421 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) { 422 ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 423 return mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle); 424 } 425 426 /** 427 * Sets {@link android.car.watchdog.ResourceOveruseConfiguration} for the specified resources. 428 */ 429 @Override 430 @CarWatchdogManager.ReturnCode setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)431 public int setResourceOveruseConfigurations( 432 List<ResourceOveruseConfiguration> configurations, 433 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) 434 throws RemoteException { 435 ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); 436 return mWatchdogPerfHandler.setResourceOveruseConfigurations(configurations, 437 resourceOveruseFlag); 438 } 439 440 /** Returns the available {@link android.car.watchdog.ResourceOveruseConfiguration}. */ 441 @Override 442 @NonNull getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)443 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( 444 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { 445 ICarImpl.assertAnyPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, 446 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); 447 return mWatchdogPerfHandler.getResourceOveruseConfigurations(resourceOveruseFlag); 448 } 449 450 /** 451 * Enables/disables the watchdog daemon client health check process. 452 */ controlProcessHealthCheck(boolean disable)453 public void controlProcessHealthCheck(boolean disable) { 454 ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); 455 mWatchdogProcessHandler.controlProcessHealthCheck(disable); 456 } 457 458 @VisibleForTesting getClientCount(int timeout)459 int getClientCount(int timeout) { 460 return mWatchdogProcessHandler.getClientCount(timeout); 461 } 462 463 @VisibleForTesting setOveruseHandlingDelay(long millis)464 void setOveruseHandlingDelay(long millis) { 465 mWatchdogPerfHandler.setOveruseHandlingDelay(millis); 466 } 467 468 @VisibleForTesting setUidIoUsageSummaryTopCount(int uidIoUsageSummaryTopCount)469 void setUidIoUsageSummaryTopCount(int uidIoUsageSummaryTopCount) { 470 mWatchdogPerfHandler.setUidIoUsageSummaryTopCount(uidIoUsageSummaryTopCount); 471 } 472 getWatchdogDirFile()473 static File getWatchdogDirFile() { 474 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class); 475 String systemCarDirPath = systemInterface == null ? FALLBACK_DATA_SYSTEM_CAR_DIR_PATH 476 : systemInterface.getSystemCarDir().getAbsolutePath(); 477 return new File(systemCarDirPath, WATCHDOG_DIR_NAME); 478 } 479 notifyAllUserStates()480 private void notifyAllUserStates() { 481 UserManager userManager = UserManager.get(mContext); 482 List<UserInfo> users = userManager.getUsers(); 483 try { 484 // TODO(b/152780162): reduce the number of RPC calls(isUserRunning). 485 for (int i = 0; i < users.size(); ++i) { 486 UserInfo info = users.get(i); 487 int userState = userManager.isUserRunning(info.id) 488 ? UserState.USER_STATE_STARTED 489 : UserState.USER_STATE_STOPPED; 490 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, info.id, 491 userState); 492 mWatchdogProcessHandler.updateUserState(info.id, 493 userState == UserState.USER_STATE_STOPPED); 494 } 495 if (DEBUG) { 496 Slogf.d(TAG, "Notified car watchdog daemon of user states"); 497 } 498 } catch (RemoteException | RuntimeException e) { 499 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 500 // throws IllegalStateException. Catch the exception to avoid crashing the process. 501 Slogf.w(TAG, e, "Notifying latest user states failed"); 502 } 503 } 504 notifyPowerCycleChange(@owerCycle int powerCycle)505 private void notifyPowerCycleChange(@PowerCycle int powerCycle) { 506 if (powerCycle == PowerCycle.NUM_POWER_CYLES) { 507 Slogf.e(TAG, "Skipping notifying invalid power cycle (%d)", powerCycle); 508 return; 509 } 510 try { 511 mCarWatchdogDaemonHelper.notifySystemStateChange( 512 StateType.POWER_CYCLE, powerCycle, MISSING_ARG_VALUE); 513 if (DEBUG) { 514 Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle); 515 } 516 } catch (RemoteException | RuntimeException e) { 517 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 518 // throws IllegalStateException. Catch the exception to avoid crashing the process. 519 Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle); 520 } 521 } 522 notifyGarageModeChange(@arageMode int garageMode)523 private void notifyGarageModeChange(@GarageMode int garageMode) { 524 try { 525 mCarWatchdogDaemonHelper.notifySystemStateChange( 526 StateType.GARAGE_MODE, garageMode, MISSING_ARG_VALUE); 527 if (DEBUG) { 528 Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode); 529 } 530 } catch (RemoteException | RuntimeException e) { 531 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 532 // throws IllegalStateException. Catch the exception to avoid crashing the process. 533 Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode); 534 } 535 } 536 postRegisterToDaemonMessage()537 private void postRegisterToDaemonMessage() { 538 CarServiceUtils.runOnMain(() -> { 539 synchronized (mLock) { 540 mReadyToRespond = true; 541 } 542 registerToDaemon(); 543 }); 544 } 545 registerToDaemon()546 private void registerToDaemon() { 547 synchronized (mLock) { 548 if (!mIsConnected || !mReadyToRespond) { 549 return; 550 } 551 } 552 try { 553 mCarWatchdogDaemonHelper.registerCarWatchdogService(mWatchdogServiceForSystem); 554 if (DEBUG) { 555 Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon"); 556 } 557 } catch (RemoteException | RuntimeException e) { 558 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 559 // throws IllegalStateException. Catch the exception to avoid crashing the process. 560 Slogf.w(TAG, e, "Cannot register to car watchdog daemon"); 561 } 562 notifyAllUserStates(); 563 CarPowerManagementService powerService = 564 CarLocalServices.getService(CarPowerManagementService.class); 565 if (powerService != null) { 566 int powerState = powerService.getPowerState(); 567 int powerCycle = carPowerStateToPowerCycle(powerState); 568 if (powerCycle != PowerCycle.NUM_POWER_CYLES) { 569 notifyPowerCycleChange(powerCycle); 570 } else { 571 Slogf.i(TAG, "Skipping notifying %d power state", powerState); 572 } 573 } 574 int garageMode; 575 synchronized (mLock) { 576 // To avoid race condition, fetch {@link mCurrentGarageMode} just before 577 // the {@link notifyGarageModeChange} call. For instance, if {@code mCurrentGarageMode} 578 // changes before the above {@link notifyPowerCycleChange} call returns, 579 // the {@link garageMode}'s value will be out of date. 580 garageMode = mCurrentGarageMode; 581 } 582 notifyGarageModeChange(garageMode); 583 } 584 unregisterFromDaemon()585 private void unregisterFromDaemon() { 586 try { 587 mCarWatchdogDaemonHelper.unregisterCarWatchdogService(mWatchdogServiceForSystem); 588 if (DEBUG) { 589 Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon"); 590 } 591 } catch (RemoteException | RuntimeException e) { 592 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper} 593 // throws IllegalStateException. Catch the exception to avoid crashing the process. 594 Slogf.w(TAG, e, "Cannot unregister from car watchdog daemon"); 595 } 596 } 597 subscribePowerManagementService()598 private void subscribePowerManagementService() { 599 CarPowerManagementService powerService = 600 CarLocalServices.getService(CarPowerManagementService.class); 601 if (powerService == null) { 602 Slogf.w(TAG, "Cannot get CarPowerManagementService"); 603 return; 604 } 605 powerService.registerListener(mCarPowerStateListener); 606 powerService.addPowerPolicyListener( 607 new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(), 608 mCarDisplayPowerPolicyListener); 609 } 610 unsubscribePowerManagementService()611 private void unsubscribePowerManagementService() { 612 CarPowerManagementService powerService = 613 CarLocalServices.getService(CarPowerManagementService.class); 614 if (powerService == null) { 615 Slogf.w(TAG, "Cannot get CarPowerManagementService"); 616 return; 617 } 618 powerService.unregisterListener(mCarPowerStateListener); 619 powerService.removePowerPolicyListener(mCarDisplayPowerPolicyListener); 620 } 621 subscribeUserStateChange()622 private void subscribeUserStateChange() { 623 CarUserService userService = CarLocalServices.getService(CarUserService.class); 624 if (userService == null) { 625 Slogf.w(TAG, "Cannot get CarUserService"); 626 return; 627 } 628 userService.addUserLifecycleListener((event) -> { 629 int userId = event.getUserHandle().getIdentifier(); 630 int userState; 631 String userStateDesc; 632 switch (event.getEventType()) { 633 case USER_LIFECYCLE_EVENT_TYPE_STARTING: 634 mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ false); 635 userState = UserState.USER_STATE_STARTED; 636 userStateDesc = "STARTING"; 637 break; 638 case USER_LIFECYCLE_EVENT_TYPE_STOPPED: 639 mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ true); 640 userState = UserState.USER_STATE_STOPPED; 641 userStateDesc = "STOPPING"; 642 break; 643 default: 644 return; 645 } 646 try { 647 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId, 648 userState); 649 if (DEBUG) { 650 Slogf.d(TAG, "Notified car watchdog daemon user %d's user state, %s", 651 userId, userStateDesc); 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, "Notifying user state change failed"); 657 } 658 }); 659 } 660 subscribeBroadcastReceiver()661 private void subscribeBroadcastReceiver() { 662 IntentFilter filter = new IntentFilter(); 663 filter.addAction(ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION); 664 filter.addAction(ACTION_GARAGE_MODE_ON); 665 filter.addAction(ACTION_GARAGE_MODE_OFF); 666 filter.addAction(ACTION_LAUNCH_APP_SETTINGS); 667 filter.addAction(ACTION_RESOURCE_OVERUSE_DISABLE_APP); 668 filter.addAction(ACTION_USER_REMOVED); 669 670 mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter, 671 Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, /* scheduler= */ null); 672 } 673 carPowerStateToPowerCycle(int powerState)674 private static @PowerCycle int carPowerStateToPowerCycle(int powerState) { 675 switch (powerState) { 676 // SHUTDOWN_PREPARE covers suspend and shutdown. 677 case CarPowerStateListener.SHUTDOWN_PREPARE: 678 return PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE; 679 case CarPowerStateListener.SHUTDOWN_ENTER: 680 case CarPowerStateListener.SUSPEND_ENTER: 681 return PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER; 682 // ON covers resume. 683 case CarPowerStateListener.ON: 684 return PowerCycle.POWER_CYCLE_RESUME; 685 } 686 return PowerCycle.NUM_POWER_CYLES; 687 } 688 toGarageModeString(@arageMode int garageMode)689 private static String toGarageModeString(@GarageMode int garageMode) { 690 switch (garageMode) { 691 case GarageMode.GARAGE_MODE_OFF: 692 return "GARAGE_MODE_OFF"; 693 case GarageMode.GARAGE_MODE_ON: 694 return "GARAGE_MODE_ON"; 695 } 696 return "INVALID"; 697 } 698 699 private static final class ICarWatchdogServiceForSystemImpl 700 extends ICarWatchdogServiceForSystem.Stub { 701 private final WeakReference<CarWatchdogService> mService; 702 ICarWatchdogServiceForSystemImpl(CarWatchdogService service)703 ICarWatchdogServiceForSystemImpl(CarWatchdogService service) { 704 mService = new WeakReference<>(service); 705 } 706 707 @Override checkIfAlive(int sessionId, int timeout)708 public void checkIfAlive(int sessionId, int timeout) { 709 CarWatchdogService service = mService.get(); 710 if (service == null) { 711 Slogf.w(TAG, "CarWatchdogService is not available"); 712 return; 713 } 714 service.mWatchdogProcessHandler.postHealthCheckMessage(sessionId); 715 } 716 717 @Override prepareProcessTermination()718 public void prepareProcessTermination() { 719 Slogf.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon"); 720 } 721 722 @Override getPackageInfosForUids( int[] uids, List<String> vendorPackagePrefixes)723 public List<PackageInfo> getPackageInfosForUids( 724 int[] uids, List<String> vendorPackagePrefixes) { 725 if (ArrayUtils.isEmpty(uids)) { 726 Slogf.w(TAG, "UID list is empty"); 727 return null; 728 } 729 CarWatchdogService service = mService.get(); 730 if (service == null) { 731 Slogf.w(TAG, "CarWatchdogService is not available"); 732 return null; 733 } 734 return service.mPackageInfoHandler.getPackageInfosForUids(uids, vendorPackagePrefixes); 735 } 736 737 @Override latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)738 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) { 739 if (packageIoOveruseStats.isEmpty()) { 740 Slogf.w(TAG, "Latest I/O overuse stats is empty"); 741 return; 742 } 743 CarWatchdogService service = mService.get(); 744 if (service == null) { 745 Slogf.w(TAG, "CarWatchdogService is not available"); 746 return; 747 } 748 service.mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats); 749 } 750 751 @Override resetResourceOveruseStats(List<String> packageNames)752 public void resetResourceOveruseStats(List<String> packageNames) { 753 if (packageNames.isEmpty()) { 754 Slogf.w(TAG, "Provided an empty package name to reset resource overuse stats"); 755 return; 756 } 757 CarWatchdogService service = mService.get(); 758 if (service == null) { 759 Slogf.w(TAG, "CarWatchdogService is not available"); 760 return; 761 } 762 service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames)); 763 } 764 765 @Override getTodayIoUsageStats()766 public List<UserPackageIoUsageStats> getTodayIoUsageStats() { 767 CarWatchdogService service = mService.get(); 768 if (service == null) { 769 Slogf.w(TAG, "CarWatchdogService is not available"); 770 return null; 771 } 772 return service.mWatchdogPerfHandler.getTodayIoUsageStats(); 773 } 774 } 775 } 776