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 android.car.watchdog; 18 19 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.car.Car; 28 import android.car.CarManagerBase; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.util.Log; 35 import android.util.SparseIntArray; 36 37 import com.android.internal.annotations.GuardedBy; 38 import com.android.internal.util.Preconditions; 39 40 import java.lang.annotation.ElementType; 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 import java.lang.annotation.Target; 44 import java.lang.ref.WeakReference; 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.Objects; 48 import java.util.concurrent.Executor; 49 50 /** 51 * CarWatchdogManager allows applications to collect latest system resource overuse statistics, add 52 * listener for resource overuse notifications, and update resource overuse configurations. 53 */ 54 public final class CarWatchdogManager extends CarManagerBase { 55 56 private static final String TAG = CarWatchdogManager.class.getSimpleName(); 57 private static final boolean DEBUG = false; // STOPSHIP if true 58 private static final int INVALID_SESSION_ID = -1; 59 private static final int NUMBER_OF_CONDITIONS_TO_BE_MET = 2; 60 // Message ID representing main thread activeness checking. 61 private static final int WHAT_CHECK_MAIN_THREAD = 1; 62 63 /** 64 * Timeout for services which should be responsive. The length is 3,000 milliseconds. 65 * 66 * @hide 67 */ 68 @SystemApi 69 public static final int TIMEOUT_CRITICAL = 0; 70 71 /** 72 * Timeout for services which are relatively responsive. The length is 5,000 milliseconds. 73 * 74 * @hide 75 */ 76 @SystemApi 77 public static final int TIMEOUT_MODERATE = 1; 78 79 /** 80 * Timeout for all other services. The length is 10,000 milliseconds. 81 * 82 * @hide 83 */ 84 @SystemApi 85 public static final int TIMEOUT_NORMAL = 2; 86 87 /** @hide */ 88 @Retention(RetentionPolicy.SOURCE) 89 @IntDef(prefix = "TIMEOUT_", value = { 90 TIMEOUT_CRITICAL, 91 TIMEOUT_MODERATE, 92 TIMEOUT_NORMAL, 93 }) 94 @Target({ElementType.TYPE_USE}) 95 public @interface TimeoutLengthEnum {} 96 97 private final ICarWatchdogService mService; 98 private final ICarWatchdogClientImpl mClientImpl; 99 private final IResourceOveruseListenerImpl mResourceOveruseListenerImpl; 100 private final IResourceOveruseListenerImpl mResourceOveruseListenerForSystemImpl; 101 private final Handler mMainHandler = new Handler(Looper.getMainLooper()); 102 103 private final Object mLock = new Object(); 104 @GuardedBy("mLock") 105 private final SessionInfo mSession = new SessionInfo(INVALID_SESSION_ID, INVALID_SESSION_ID); 106 @GuardedBy("mLock") 107 private final List<ResourceOveruseListenerInfo> mResourceOveruseListenerInfos; 108 @GuardedBy("mLock") 109 private final List<ResourceOveruseListenerInfo> mResourceOveruseListenerForSystemInfos; 110 @GuardedBy("mLock") 111 private CarWatchdogClientCallback mRegisteredClient; 112 @GuardedBy("mLock") 113 private Executor mCallbackExecutor; 114 @GuardedBy("mLock") 115 private int mRemainingConditions; 116 117 /** 118 * CarWatchdogClientCallback is implemented by the clients which want to be health-checked by 119 * car watchdog server. Every time onCheckHealthStatus is called, they are expected to 120 * respond by calling {@link #tellClientAlive} within timeout. If they don't 121 * respond, car watchdog server reports the current state and kills them. 122 * 123 * <p>Before car watchdog server kills the client, it calls onPrepareProcessTermination to allow 124 * them to prepare the termination. They will be killed in 1 second. 125 * 126 * @hide 127 */ 128 @SystemApi 129 public abstract static class CarWatchdogClientCallback { 130 /** 131 * Car watchdog server pings the client to check if it is alive. 132 * 133 * <p>The callback method is called at the Executor which is specified in {@link 134 * CarWatchdogManager#registerClient}. 135 * 136 * @param sessionId Unique id to distinguish each health checking. 137 * @param timeout Time duration within which the client should respond. 138 * 139 * @return whether the response is immediately acknowledged. If {@code true}, car watchdog 140 * server considers that the response is acknowledged already. If {@code false}, 141 * the client should call {@link CarWatchdogManager#tellClientAlive} later to tell 142 * that it is alive. 143 */ onCheckHealthStatus(int sessionId, @TimeoutLengthEnum int timeout)144 public boolean onCheckHealthStatus(int sessionId, @TimeoutLengthEnum int timeout) { 145 return false; 146 } 147 148 /** 149 * Car watchdog server notifies the client that it will be terminated in 1 second. 150 * 151 * <p>The callback method is called at the Executor which is specified in {@link 152 * CarWatchdogManager#registerClient}. 153 */ onPrepareProcessTermination()154 public void onPrepareProcessTermination() {} 155 } 156 157 /** @hide */ CarWatchdogManager(Car car, IBinder service)158 public CarWatchdogManager(Car car, IBinder service) { 159 super(car); 160 mService = ICarWatchdogService.Stub.asInterface(service); 161 mClientImpl = new ICarWatchdogClientImpl(this); 162 mResourceOveruseListenerImpl = new IResourceOveruseListenerImpl(this, /* isSystem= */false); 163 mResourceOveruseListenerForSystemImpl = new IResourceOveruseListenerImpl(this, 164 /* isSystem= */true); 165 mResourceOveruseListenerInfos = new ArrayList<>(); 166 mResourceOveruseListenerForSystemInfos = new ArrayList<>(); 167 } 168 169 /** 170 * Registers the car watchdog clients to {@link CarWatchdogManager}. 171 * 172 * <p>It is allowed to register a client from any thread, but only one client can be 173 * registered. If two or more clients are needed, create a new {@link Car} and register a client 174 * to it. 175 * 176 * @param client Watchdog client implementing {@link CarWatchdogClientCallback} interface. 177 * @param timeout The time duration within which the client desires to respond. The actual 178 * timeout is decided by watchdog server. 179 * @throws IllegalStateException if at least one client is already registered. 180 * 181 * @hide 182 */ 183 @SystemApi 184 @RequiresPermission(Car.PERMISSION_USE_CAR_WATCHDOG) registerClient(@onNull @allbackExecutor Executor executor, @NonNull CarWatchdogClientCallback client, @TimeoutLengthEnum int timeout)185 public void registerClient(@NonNull @CallbackExecutor Executor executor, 186 @NonNull CarWatchdogClientCallback client, @TimeoutLengthEnum int timeout) { 187 Objects.requireNonNull(client, "Client must be non-null"); 188 Objects.requireNonNull(executor, "Executor must be non-null"); 189 synchronized (mLock) { 190 if (mRegisteredClient == client) { 191 return; 192 } 193 if (mRegisteredClient != null) { 194 throw new IllegalStateException( 195 "Cannot register the client. Only one client can be registered."); 196 } 197 mRegisteredClient = client; 198 mCallbackExecutor = executor; 199 } 200 try { 201 mService.registerClient(mClientImpl, timeout); 202 if (DEBUG) { 203 Log.d(TAG, "Car watchdog client is successfully registered"); 204 } 205 } catch (RemoteException e) { 206 synchronized (mLock) { 207 mRegisteredClient = null; 208 } 209 handleRemoteExceptionFromCarService(e); 210 } 211 } 212 213 /** 214 * Unregisters the car watchdog client from {@link CarWatchdogManager}. 215 * 216 * @param client Watchdog client implementing {@link CarWatchdogClientCallback} interface. 217 * 218 * @hide 219 */ 220 @SystemApi 221 @RequiresPermission(Car.PERMISSION_USE_CAR_WATCHDOG) unregisterClient(@onNull CarWatchdogClientCallback client)222 public void unregisterClient(@NonNull CarWatchdogClientCallback client) { 223 Objects.requireNonNull(client, "Client must be non-null"); 224 synchronized (mLock) { 225 if (mRegisteredClient != client) { 226 Log.w(TAG, "Cannot unregister the client. It has not been registered."); 227 return; 228 } 229 mRegisteredClient = null; 230 mCallbackExecutor = null; 231 } 232 try { 233 mService.unregisterClient(mClientImpl); 234 if (DEBUG) { 235 Log.d(TAG, "Car watchdog client is successfully unregistered"); 236 } 237 } catch (RemoteException e) { 238 handleRemoteExceptionFromCarService(e); 239 } 240 } 241 242 /** 243 * Tells {@link CarWatchdogManager} that the client is alive. 244 * 245 * @param client Watchdog client implementing {@link CarWatchdogClientCallback} interface. 246 * @param sessionId Session id given by {@link CarWatchdogManager}. 247 * @throws IllegalStateException if {@code client} is not registered. 248 * @throws IllegalArgumentException if {@code sessionId} is not correct. 249 * 250 * @hide 251 */ 252 @SystemApi 253 @RequiresPermission(Car.PERMISSION_USE_CAR_WATCHDOG) tellClientAlive(@onNull CarWatchdogClientCallback client, int sessionId)254 public void tellClientAlive(@NonNull CarWatchdogClientCallback client, int sessionId) { 255 Objects.requireNonNull(client, "Client must be non-null"); 256 boolean shouldReport; 257 synchronized (mLock) { 258 if (mRegisteredClient != client) { 259 throw new IllegalStateException( 260 "Cannot report client status. The client has not been registered."); 261 } 262 Preconditions.checkArgument(sessionId != -1 && mSession.currentId == sessionId, 263 "Cannot report client status. " 264 + "The given session id doesn't match the current one."); 265 if (mSession.lastReportedId == sessionId) { 266 Log.w(TAG, "The given session id is already reported."); 267 return; 268 } 269 mSession.lastReportedId = sessionId; 270 mRemainingConditions--; 271 shouldReport = checkConditionLocked(); 272 } 273 if (shouldReport) { 274 reportToService(sessionId); 275 } 276 } 277 278 /** @hide */ 279 @IntDef(flag = false, prefix = { "STATS_PERIOD_" }, value = { 280 STATS_PERIOD_CURRENT_DAY, 281 STATS_PERIOD_PAST_3_DAYS, 282 STATS_PERIOD_PAST_7_DAYS, 283 STATS_PERIOD_PAST_15_DAYS, 284 STATS_PERIOD_PAST_30_DAYS, 285 }) 286 @Retention(RetentionPolicy.SOURCE) 287 public @interface StatsPeriod {} 288 289 /** @hide */ 290 @IntDef(flag = true, prefix = { "FLAG_RESOURCE_OVERUSE_" }, value = { 291 FLAG_RESOURCE_OVERUSE_IO, 292 }) 293 @Retention(RetentionPolicy.SOURCE) 294 public @interface ResourceOveruseFlag {} 295 296 /** @hide */ 297 @IntDef(flag = true, prefix = { "FLAG_MINIMUM_STATS_" }, value = { 298 FLAG_MINIMUM_STATS_IO_1_MB, 299 FLAG_MINIMUM_STATS_IO_100_MB, 300 FLAG_MINIMUM_STATS_IO_1_GB, 301 }) 302 @Retention(RetentionPolicy.SOURCE) 303 public @interface MinimumStatsFlag {} 304 305 /** @hide */ 306 @IntDef(flag = true, prefix = { "RETURN_CODE_" }, value = { 307 RETURN_CODE_SUCCESS, 308 RETURN_CODE_ERROR, 309 }) 310 @Retention(RetentionPolicy.SOURCE) 311 public @interface ReturnCode {} 312 313 /** 314 * Constants that define the stats period in days. 315 */ 316 public static final int STATS_PERIOD_CURRENT_DAY = 1; 317 public static final int STATS_PERIOD_PAST_3_DAYS = 2; 318 public static final int STATS_PERIOD_PAST_7_DAYS = 3; 319 public static final int STATS_PERIOD_PAST_15_DAYS = 4; 320 public static final int STATS_PERIOD_PAST_30_DAYS = 5; 321 322 /** 323 * Constants that define the type of resource overuse. 324 */ 325 public static final int FLAG_RESOURCE_OVERUSE_IO = 1 << 0; 326 327 /** 328 * Constants that define the minimum stats for each resource type. 329 * 330 * Below constants specify the minimum amount of data written to disk. 331 * 332 * @hide 333 */ 334 @SystemApi 335 public static final int FLAG_MINIMUM_STATS_IO_1_MB = 1 << 0; 336 /** @hide */ 337 @SystemApi 338 public static final int FLAG_MINIMUM_STATS_IO_100_MB = 1 << 1; 339 /** @hide */ 340 @SystemApi 341 public static final int FLAG_MINIMUM_STATS_IO_1_GB = 1 << 2; 342 343 // Return codes used to indicate the result of a request. 344 /** @hide */ 345 @SystemApi 346 public static final int RETURN_CODE_SUCCESS = 0; 347 /** @hide */ 348 @SystemApi 349 public static final int RETURN_CODE_ERROR = -1; 350 351 /** 352 * Returns resource overuse stats for the calling package. Returns {@code null}, if no stats. 353 * 354 * @param resourceOveruseFlag Flag to indicate the types of resource overuse stats to return. 355 * @param maxStatsPeriod Maximum period to aggregate the resource overuse stats. 356 * 357 * @return Resource overuse stats for the calling package. If the calling package has no stats 358 * for a specified resource overuse type, null value is returned for the corresponding 359 * resource overuse stats. If the calling package doesn't have sufficient stats for 360 * {@code maxStatsPeriod} for a specified resource overuse type, the stats are returned 361 * only for the period returned in the individual resource overuse stats. 362 */ 363 @NonNull getResourceOveruseStats( @esourceOveruseFlag int resourceOveruseFlag, @StatsPeriod int maxStatsPeriod)364 public ResourceOveruseStats getResourceOveruseStats( 365 @ResourceOveruseFlag int resourceOveruseFlag, 366 @StatsPeriod int maxStatsPeriod) { 367 try { 368 return mService.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod); 369 } catch (RemoteException e) { 370 ResourceOveruseStats.Builder builder = 371 new ResourceOveruseStats.Builder("", UserHandle.CURRENT); 372 return handleRemoteExceptionFromCarService(e, builder.build()); 373 } 374 } 375 376 /** 377 * Returns resource overuse stats for all monitored packages. 378 * 379 * @param resourceOveruseFlag Flag to indicate the types of resource overuse stats to return. 380 * @param minimumStatsFlag Flag to specify the minimum stats for each resource overuse type. 381 * Only stats above the specified minimum stats for a resource overuse 382 * type will be returned. May provide only one minimum stats flag for 383 * each resource overuse type. When no minimum stats flag is specified, 384 * all stats are returned. 385 * @param maxStatsPeriod Maximum period to aggregate the resource overuse stats. 386 * 387 * @return Resource overuse stats for all monitored packages. If any package doesn't have stats 388 * for a specified resource type, null value is returned for the corresponding resource 389 * overuse stats. If any package doesn't have sufficient stats for 390 * {@code maxStatsPeriod} for a specified resource overuse type, the stats are returned 391 * only for the period returned in the individual resource stats. 392 * 393 * @hide 394 */ 395 @SystemApi 396 @RequiresPermission(Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS) 397 @NonNull getAllResourceOveruseStats( @esourceOveruseFlag int resourceOveruseFlag, @MinimumStatsFlag int minimumStatsFlag, @StatsPeriod int maxStatsPeriod)398 public List<ResourceOveruseStats> getAllResourceOveruseStats( 399 @ResourceOveruseFlag int resourceOveruseFlag, 400 @MinimumStatsFlag int minimumStatsFlag, 401 @StatsPeriod int maxStatsPeriod) { 402 try { 403 return mService.getAllResourceOveruseStats(resourceOveruseFlag, minimumStatsFlag, 404 maxStatsPeriod); 405 } catch (RemoteException e) { 406 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 407 } 408 } 409 410 /** 411 * Returns resource overuse stats for a specific user package. 412 * 413 * @param packageName Name of the package whose stats should be returned. 414 * @param userHandle Handle of the user whose stats should be returned. 415 * @param resourceOveruseFlag Flag to indicate the types of resource overuse stats to return. 416 * @param maxStatsPeriod Maximum period to aggregate the resource overuse stats. 417 * 418 * @return Resource overuse stats for the specified user package. If the user package has no 419 * stats for a specified resource overuse type, null value is returned for the 420 * corresponding resource overuse stats. If the user package doesn't have sufficient 421 * stats for {@code maxStatsPeriod} for a specified resource overuse type, the stats are 422 * returned only for the period returned in the individual resource overuse stats. 423 * 424 * @hide 425 */ 426 @SystemApi 427 @RequiresPermission(Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS) 428 @NonNull getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @ResourceOveruseFlag int resourceOveruseFlag, @StatsPeriod int maxStatsPeriod)429 public ResourceOveruseStats getResourceOveruseStatsForUserPackage( 430 @NonNull String packageName, @NonNull UserHandle userHandle, 431 @ResourceOveruseFlag int resourceOveruseFlag, 432 @StatsPeriod int maxStatsPeriod) { 433 try { 434 return mService.getResourceOveruseStatsForUserPackage(packageName, userHandle, 435 resourceOveruseFlag, maxStatsPeriod); 436 } catch (RemoteException e) { 437 ResourceOveruseStats.Builder builder = 438 new ResourceOveruseStats.Builder("", userHandle); 439 return handleRemoteExceptionFromCarService(e, builder.build()); 440 } 441 } 442 443 /** 444 * Listener to get resource overuse notifications. 445 * 446 * <p>Applications implement the listener method to take action and/or log on resource overuse. 447 */ 448 public interface ResourceOveruseListener { 449 /** 450 * Called when a package either overuses a resource or about to overuse a resource. 451 * 452 * <p>The listener is called at the executor which is specified in {@link 453 * CarWatchdogManager#addResourceOveruseListener} or 454 * {@link CarWatchdogManager#addResourceOveruseListenerForSystem}. 455 * 456 * <p>The listener is called only on overusing one of the resources specified at the 457 * {@code resourceOveruseFlag} in {@link CarWatchdogManager#addResourceOveruseListener} or 458 * {@link CarWatchdogManager#addResourceOveruseListenerForSystem}. 459 * 460 * @param resourceOveruseStats Resource overuse stats containing stats only for resources 461 * overuse types that are either overused or about to be 462 * overused by the package. Implementations must check for null 463 * value in each resource overuse stats before reading the 464 * stats. 465 */ onOveruse(@onNull ResourceOveruseStats resourceOveruseStats)466 void onOveruse(@NonNull ResourceOveruseStats resourceOveruseStats); 467 } 468 469 /** 470 * Adds the {@link ResourceOveruseListener} for the calling package. 471 * 472 * <p>Resource overuse notifications are sent only for the calling package's resource overuse. 473 * 474 * @param listener Listener implementing {@link ResourceOveruseListener} interface. 475 * @param resourceOveruseFlag Flag to indicate the types of resource overuses to listen. 476 * 477 * @throws IllegalStateException if (@code listener} is already added. 478 */ addResourceOveruseListener( @onNull @allbackExecutor Executor executor, @ResourceOveruseFlag int resourceOveruseFlag, @NonNull ResourceOveruseListener listener)479 public void addResourceOveruseListener( 480 @NonNull @CallbackExecutor Executor executor, 481 @ResourceOveruseFlag int resourceOveruseFlag, 482 @NonNull ResourceOveruseListener listener) { 483 Objects.requireNonNull(listener, "Listener must be non-null"); 484 Objects.requireNonNull(executor, "Executor must be non-null"); 485 Preconditions.checkArgument((resourceOveruseFlag > 0), 486 "Must provide valid resource overuse flag"); 487 boolean shouldRemoveFromService; 488 boolean shouldAddToService; 489 synchronized (mLock) { 490 ResourceOveruseListenerInfo listenerInfo = 491 new ResourceOveruseListenerInfo(listener, executor, resourceOveruseFlag); 492 if (mResourceOveruseListenerInfos.contains(listenerInfo)) { 493 throw new IllegalStateException( 494 "Cannot add the listener as it is already added"); 495 } 496 shouldRemoveFromService = mResourceOveruseListenerImpl.hasListeners(); 497 shouldAddToService = mResourceOveruseListenerImpl.maybeAppendFlag(resourceOveruseFlag); 498 mResourceOveruseListenerInfos.add(listenerInfo); 499 } 500 if (shouldAddToService) { 501 if (shouldRemoveFromService) { 502 removeResourceOveruseListenerImpl(); 503 } 504 addResourceOveruseListenerImpl(); 505 } 506 } 507 508 /** 509 * Removes the {@link ResourceOveruseListener} for the calling package. 510 * 511 * @param listener Listener implementing {@link ResourceOveruseListener} interface. 512 */ removeResourceOveruseListener(@onNull ResourceOveruseListener listener)513 public void removeResourceOveruseListener(@NonNull ResourceOveruseListener listener) { 514 Objects.requireNonNull(listener, "Listener must be non-null"); 515 boolean shouldRemoveFromService; 516 boolean shouldReAddToService; 517 synchronized (mLock) { 518 int index = 0; 519 int resourceOveruseFlag = 0; 520 for (; index != mResourceOveruseListenerInfos.size(); ++index) { 521 ResourceOveruseListenerInfo listenerInfo = mResourceOveruseListenerInfos.get(index); 522 if (listenerInfo.listener == listener) { 523 resourceOveruseFlag = listenerInfo.resourceOveruseFlag; 524 break; 525 } 526 } 527 if (index == mResourceOveruseListenerInfos.size()) { 528 Log.w(TAG, "Cannot remove the listener. It has not been added."); 529 return; 530 } 531 mResourceOveruseListenerInfos.remove(index); 532 shouldRemoveFromService = 533 mResourceOveruseListenerImpl.maybeRemoveFlag(resourceOveruseFlag); 534 shouldReAddToService = mResourceOveruseListenerImpl.hasListeners(); 535 } 536 if (shouldRemoveFromService) { 537 removeResourceOveruseListenerImpl(); 538 if (shouldReAddToService) { 539 addResourceOveruseListenerImpl(); 540 } 541 } 542 } 543 544 /** 545 * Adds {@link ResourceOveruseListener} to get resource overuse notifications for all packages. 546 * 547 * <p>Listening system services will get notified on any package overusing one of the resources 548 * specified at {@code resourceOveruseFlag}. 549 * 550 * @param listener Listener implementing {@link ResourceOveruseListener} interface. 551 * @param resourceOveruseFlag Flag to indicate the types of resource overuses to listen. 552 * 553 * @throws IllegalStateException if (@code listener} is already added. 554 * 555 * @hide 556 */ 557 @SystemApi 558 @RequiresPermission(Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS) addResourceOveruseListenerForSystem( @onNull @allbackExecutor Executor executor, @ResourceOveruseFlag int resourceOveruseFlag, @NonNull ResourceOveruseListener listener)559 public void addResourceOveruseListenerForSystem( 560 @NonNull @CallbackExecutor Executor executor, 561 @ResourceOveruseFlag int resourceOveruseFlag, 562 @NonNull ResourceOveruseListener listener) { 563 Objects.requireNonNull(listener, "Listener must be non-null"); 564 Objects.requireNonNull(executor, "Executor must be non-null"); 565 Preconditions.checkArgument((resourceOveruseFlag > 0), 566 "Must provide valid resource overuse flag"); 567 boolean shouldRemoveFromService; 568 boolean shouldAddToService; 569 synchronized (mLock) { 570 ResourceOveruseListenerInfo listenerInfo = 571 new ResourceOveruseListenerInfo(listener, executor, resourceOveruseFlag); 572 if (mResourceOveruseListenerForSystemInfos.contains(listenerInfo)) { 573 throw new IllegalStateException( 574 "Cannot add the listener as it is already added"); 575 } 576 shouldRemoveFromService = mResourceOveruseListenerForSystemImpl.hasListeners(); 577 shouldAddToService = 578 mResourceOveruseListenerForSystemImpl.maybeAppendFlag(resourceOveruseFlag); 579 mResourceOveruseListenerForSystemInfos.add(listenerInfo); 580 } 581 if (shouldAddToService) { 582 if (shouldRemoveFromService) { 583 removeResourceOveruseListenerForSystemImpl(); 584 } 585 addResourceOveruseListenerForSystemImpl(); 586 } 587 } 588 589 /** 590 * Removes {@link ResourceOveruseListener} from receiving system resource overuse notifications. 591 * 592 * @param listener Listener implementing {@link ResourceOveruseListener} interface. 593 * 594 * @hide 595 */ 596 @SystemApi 597 @RequiresPermission(Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS) removeResourceOveruseListenerForSystem( @onNull ResourceOveruseListener listener)598 public void removeResourceOveruseListenerForSystem( 599 @NonNull ResourceOveruseListener listener) { 600 Objects.requireNonNull(listener, "Listener must be non-null"); 601 boolean shouldRemoveFromService; 602 boolean shouldReAddToService; 603 synchronized (mLock) { 604 int index = 0; 605 int resourceOveruseFlag = 0; 606 for (; index != mResourceOveruseListenerForSystemInfos.size(); ++index) { 607 ResourceOveruseListenerInfo listenerInfo = 608 mResourceOveruseListenerForSystemInfos.get(index); 609 if (listenerInfo.listener == listener) { 610 resourceOveruseFlag = listenerInfo.resourceOveruseFlag; 611 break; 612 } 613 } 614 if (index == mResourceOveruseListenerForSystemInfos.size()) { 615 Log.w(TAG, "Cannot remove the listener. It has not been added."); 616 return; 617 } 618 mResourceOveruseListenerForSystemInfos.remove(index); 619 shouldRemoveFromService = 620 mResourceOveruseListenerForSystemImpl.maybeRemoveFlag(resourceOveruseFlag); 621 shouldReAddToService = mResourceOveruseListenerForSystemImpl.hasListeners(); 622 } 623 if (shouldRemoveFromService) { 624 removeResourceOveruseListenerForSystemImpl(); 625 if (shouldReAddToService) { 626 addResourceOveruseListenerForSystemImpl(); 627 } 628 } 629 } 630 631 /** 632 * Sets whether or not a package is killable on resource overuse. 633 * 634 * <p>Updating killable setting for package, whose state cannot be changed, will result in 635 * exception. This API may be used by CarSettings application or UI notification. 636 * 637 * @param packageName Name of the package whose setting should to be updated. 638 * Note: All packages under shared UID share the killable state as well. Thus 639 * setting the killable state for one package will set the killable state for 640 * all other packages that share a UID. 641 * @param userHandle User whose setting should be updated. 642 * @param isKillable Whether or not the package for the specified user is killable on resource 643 * overuse. 644 * 645 * @hide 646 */ 647 @SystemApi 648 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) setKillablePackageAsUser(@onNull String packageName, @NonNull UserHandle userHandle, boolean isKillable)649 public void setKillablePackageAsUser(@NonNull String packageName, 650 @NonNull UserHandle userHandle, boolean isKillable) { 651 try { 652 mService.setKillablePackageAsUser(packageName, userHandle, isKillable); 653 } catch (RemoteException e) { 654 handleRemoteExceptionFromCarService(e); 655 } 656 } 657 658 /** 659 * Returns the list of package killable states on resource overuse for the user. 660 * 661 * <p>This API may be used by CarSettings application or UI notification. 662 * 663 * @param userHandle User whose killable states for all packages should be returned. 664 * 665 * @hide 666 */ 667 @SystemApi 668 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) 669 @NonNull getPackageKillableStatesAsUser( @onNull UserHandle userHandle)670 public List<PackageKillableState> getPackageKillableStatesAsUser( 671 @NonNull UserHandle userHandle) { 672 try { 673 return mService.getPackageKillableStatesAsUser(userHandle); 674 } catch (RemoteException e) { 675 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 676 } 677 } 678 679 /** 680 * Sets the resource overuse configurations for the components provided in the configurations. 681 * 682 * <p>Must provide only one configuration per component. System services should set the 683 * configurations only for system and third-party components. Vendor services should set the 684 * configuration only for the vendor component. 685 * 686 * @param configurations List of resource overuse configurations. One configuration per 687 * component. 688 * @param resourceOveruseFlag Flag to indicate the types of resource overuse configurations to 689 * set. 690 * 691 * @return - {@link #RETURN_CODE_SUCCESS} if the set request is successful. 692 * - {@link #RETURN_CODE_ERROR} if the set request cannot be completed and the client 693 * should retry later. 694 * 695 * @throws IllegalArgumentException if {@code configurations} are invalid. 696 * 697 * @hide 698 */ 699 @SystemApi 700 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) 701 @ReturnCode setResourceOveruseConfigurations( @onNull List<ResourceOveruseConfiguration> configurations, @ResourceOveruseFlag int resourceOveruseFlag)702 public int setResourceOveruseConfigurations( 703 @NonNull List<ResourceOveruseConfiguration> configurations, 704 @ResourceOveruseFlag int resourceOveruseFlag) { 705 try { 706 return mService.setResourceOveruseConfigurations(configurations, resourceOveruseFlag); 707 } catch (RemoteException e) { 708 handleRemoteExceptionFromCarService(e); 709 return RETURN_CODE_ERROR; 710 } 711 } 712 713 /** 714 * Returns the current resource overuse configurations for all components. 715 * 716 * <p>This call is blocking and may take few seconds to return if the service is temporarily 717 * unavailable. 718 * 719 * @param resourceOveruseFlag Flag to indicate the types of resource overuse configurations to 720 * return. 721 * 722 * @return If the server process is alive and connected, returns list of available resource 723 * overuse configurations for all components. If the server process is dead, 724 * returns {@code null} value. 725 * 726 * @throws IllegalStateException if the system is in an invalid state. 727 * @hide 728 */ 729 @SystemApi 730 @RequiresPermission(anyOf = {Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, 731 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS}) 732 @Nullable getResourceOveruseConfigurations( @esourceOveruseFlag int resourceOveruseFlag)733 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( 734 @ResourceOveruseFlag int resourceOveruseFlag) { 735 try { 736 return mService.getResourceOveruseConfigurations(resourceOveruseFlag); 737 } catch (RemoteException e) { 738 return handleRemoteExceptionFromCarService(e, null); 739 } 740 } 741 742 /** @hide */ 743 @Override onCarDisconnected()744 public void onCarDisconnected() { 745 // nothing to do 746 } 747 checkClientStatus(int sessionId, int timeout)748 private void checkClientStatus(int sessionId, int timeout) { 749 CarWatchdogClientCallback client; 750 Executor executor; 751 mMainHandler.removeMessages(WHAT_CHECK_MAIN_THREAD); 752 synchronized (mLock) { 753 if (mRegisteredClient == null) { 754 Log.w(TAG, "Cannot check client status. The client has not been registered."); 755 return; 756 } 757 mSession.currentId = sessionId; 758 client = mRegisteredClient; 759 executor = mCallbackExecutor; 760 mRemainingConditions = NUMBER_OF_CONDITIONS_TO_BE_MET; 761 } 762 // For a car watchdog client to be active, 1) its main thread is active and 2) the client 763 // responds within timeout. When each condition is met, the remaining task counter is 764 // decreased. If the remaining task counter is zero, the client is considered active. 765 mMainHandler.sendMessage(obtainMessage(CarWatchdogManager::checkMainThread, this) 766 .setWhat(WHAT_CHECK_MAIN_THREAD)); 767 // Call the client callback to check if the client is active. 768 executor.execute(() -> { 769 boolean checkDone = client.onCheckHealthStatus(sessionId, timeout); 770 if (checkDone) { 771 boolean shouldReport; 772 synchronized (mLock) { 773 if (mSession.lastReportedId == sessionId) { 774 return; 775 } 776 mSession.lastReportedId = sessionId; 777 mRemainingConditions--; 778 shouldReport = checkConditionLocked(); 779 } 780 if (shouldReport) { 781 reportToService(sessionId); 782 } 783 } 784 }); 785 } 786 checkMainThread()787 private void checkMainThread() { 788 int sessionId; 789 boolean shouldReport; 790 synchronized (mLock) { 791 mRemainingConditions--; 792 sessionId = mSession.currentId; 793 shouldReport = checkConditionLocked(); 794 } 795 if (shouldReport) { 796 reportToService(sessionId); 797 } 798 } 799 checkConditionLocked()800 private boolean checkConditionLocked() { 801 if (mRemainingConditions < 0) { 802 Log.wtf(TAG, "Remaining condition is less than zero: should not happen"); 803 } 804 return mRemainingConditions == 0; 805 } 806 reportToService(int sessionId)807 private void reportToService(int sessionId) { 808 try { 809 mService.tellClientAlive(mClientImpl, sessionId); 810 } catch (RemoteException e) { 811 handleRemoteExceptionFromCarService(e); 812 } 813 } 814 notifyProcessTermination()815 private void notifyProcessTermination() { 816 CarWatchdogClientCallback client; 817 Executor executor; 818 synchronized (mLock) { 819 if (mRegisteredClient == null) { 820 Log.w(TAG, "Cannot notify the client. The client has not been registered."); 821 return; 822 } 823 client = mRegisteredClient; 824 executor = mCallbackExecutor; 825 } 826 executor.execute(() -> client.onPrepareProcessTermination()); 827 } 828 addResourceOveruseListenerImpl()829 private void addResourceOveruseListenerImpl() { 830 try { 831 mService.addResourceOveruseListener( 832 mResourceOveruseListenerImpl.resourceOveruseFlag(), 833 mResourceOveruseListenerImpl); 834 if (DEBUG) { 835 Log.d(TAG, "Resource overuse listener implementation is successfully added to " 836 + "service"); 837 } 838 } catch (RemoteException e) { 839 synchronized (mLock) { 840 mResourceOveruseListenerInfos.clear(); 841 } 842 handleRemoteExceptionFromCarService(e); 843 } 844 } 845 removeResourceOveruseListenerImpl()846 private void removeResourceOveruseListenerImpl() { 847 try { 848 mService.removeResourceOveruseListener(mResourceOveruseListenerImpl); 849 if (DEBUG) { 850 Log.d(TAG, "Resource overuse listener implementation is successfully removed " 851 + "from service"); 852 } 853 } catch (RemoteException e) { 854 handleRemoteExceptionFromCarService(e); 855 } 856 } 857 addResourceOveruseListenerForSystemImpl()858 private void addResourceOveruseListenerForSystemImpl() { 859 try { 860 mService.addResourceOveruseListenerForSystem( 861 mResourceOveruseListenerForSystemImpl.resourceOveruseFlag(), 862 mResourceOveruseListenerForSystemImpl); 863 if (DEBUG) { 864 Log.d(TAG, "Resource overuse listener for system implementation is successfully " 865 + "added to service"); 866 } 867 } catch (RemoteException e) { 868 synchronized (mLock) { 869 mResourceOveruseListenerForSystemInfos.clear(); 870 } 871 handleRemoteExceptionFromCarService(e); 872 } 873 } 874 removeResourceOveruseListenerForSystemImpl()875 private void removeResourceOveruseListenerForSystemImpl() { 876 try { 877 mService.removeResourceOveruseListenerForSystem(mResourceOveruseListenerForSystemImpl); 878 if (DEBUG) { 879 Log.d(TAG, "Resource overuse listener for system implementation is successfully " 880 + "removed from service"); 881 } 882 } catch (RemoteException e) { 883 handleRemoteExceptionFromCarService(e); 884 } 885 } 886 onResourceOveruse(ResourceOveruseStats resourceOveruseStats, boolean isSystem)887 private void onResourceOveruse(ResourceOveruseStats resourceOveruseStats, boolean isSystem) { 888 if (resourceOveruseStats.getIoOveruseStats() == null) { 889 Log.w(TAG, "Skipping resource overuse notification as the stats are missing"); 890 return; 891 } 892 List<ResourceOveruseListenerInfo> listenerInfos; 893 synchronized (mLock) { 894 if (isSystem) { 895 listenerInfos = mResourceOveruseListenerForSystemInfos; 896 } else { 897 listenerInfos = mResourceOveruseListenerInfos; 898 } 899 } 900 if (listenerInfos.isEmpty()) { 901 Log.w(TAG, "Cannot notify resource overuse listener " + (isSystem ? "for system " : "") 902 + "as it is not registered."); 903 return; 904 } 905 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) { 906 if ((listenerInfo.resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) == 1) { 907 listenerInfo.executor.execute(() -> { 908 listenerInfo.listener.onOveruse(resourceOveruseStats); 909 }); 910 } 911 } 912 } 913 914 /** @hide */ 915 private static final class ICarWatchdogClientImpl extends ICarWatchdogServiceCallback.Stub { 916 private final WeakReference<CarWatchdogManager> mManager; 917 ICarWatchdogClientImpl(CarWatchdogManager manager)918 ICarWatchdogClientImpl(CarWatchdogManager manager) { 919 mManager = new WeakReference<>(manager); 920 } 921 922 @Override onCheckHealthStatus(int sessionId, int timeout)923 public void onCheckHealthStatus(int sessionId, int timeout) { 924 CarWatchdogManager manager = mManager.get(); 925 if (manager != null) { 926 manager.checkClientStatus(sessionId, timeout); 927 } 928 } 929 930 @Override onPrepareProcessTermination()931 public void onPrepareProcessTermination() { 932 CarWatchdogManager manager = mManager.get(); 933 if (manager != null) { 934 manager.notifyProcessTermination(); 935 } 936 } 937 } 938 939 private static final class SessionInfo { 940 public int currentId; 941 public int lastReportedId; 942 SessionInfo(int currentId, int lastReportedId)943 SessionInfo(int currentId, int lastReportedId) { 944 this.currentId = currentId; 945 this.lastReportedId = lastReportedId; 946 } 947 } 948 949 /** @hide */ 950 private static final class IResourceOveruseListenerImpl extends IResourceOveruseListener.Stub { 951 private static final int[] RESOURCE_OVERUSE_FLAGS = new int[]{ FLAG_RESOURCE_OVERUSE_IO }; 952 953 private final WeakReference<CarWatchdogManager> mManager; 954 private final boolean mIsSystem; 955 956 private final Object mLock = new Object(); 957 @GuardedBy("mLock") 958 private final SparseIntArray mNumListenersByResource; 959 960 IResourceOveruseListenerImpl(CarWatchdogManager manager, boolean isSystem)961 IResourceOveruseListenerImpl(CarWatchdogManager manager, boolean isSystem) { 962 mManager = new WeakReference<>(manager); 963 mIsSystem = isSystem; 964 mNumListenersByResource = new SparseIntArray(); 965 } 966 967 @Override onOveruse(ResourceOveruseStats resourceOveruserStats)968 public void onOveruse(ResourceOveruseStats resourceOveruserStats) { 969 CarWatchdogManager manager = mManager.get(); 970 if (manager != null) { 971 manager.onResourceOveruse(resourceOveruserStats, mIsSystem); 972 } 973 } 974 hasListeners()975 public boolean hasListeners() { 976 synchronized (mLock) { 977 return mNumListenersByResource.size() != 0; 978 } 979 } 980 maybeAppendFlag(int appendFlag)981 public boolean maybeAppendFlag(int appendFlag) { 982 boolean isChanged = false; 983 synchronized (mLock) { 984 for (int flag : RESOURCE_OVERUSE_FLAGS) { 985 if ((appendFlag & flag) != 1) { 986 continue; 987 } 988 int value = mNumListenersByResource.get(flag, 0); 989 isChanged = ++value == 1; 990 mNumListenersByResource.put(flag, value); 991 } 992 } 993 return isChanged; 994 } 995 maybeRemoveFlag(int removeFlag)996 public boolean maybeRemoveFlag(int removeFlag) { 997 boolean isChanged = false; 998 synchronized (mLock) { 999 for (int flag : RESOURCE_OVERUSE_FLAGS) { 1000 if ((removeFlag & flag) != 1) { 1001 continue; 1002 } 1003 int value = mNumListenersByResource.get(flag, 0); 1004 if (value == 0) { 1005 continue; 1006 } 1007 if (--value == 0) { 1008 isChanged = true; 1009 mNumListenersByResource.delete(flag); 1010 } else { 1011 mNumListenersByResource.put(flag, value); 1012 } 1013 } 1014 } 1015 return isChanged; 1016 } 1017 resourceOveruseFlag()1018 public int resourceOveruseFlag() { 1019 int flag = 0; 1020 for (int i = 0; i < mNumListenersByResource.size(); ++i) { 1021 flag |= mNumListenersByResource.valueAt(i) > 0 ? mNumListenersByResource.keyAt(i) 1022 : 0; 1023 } 1024 return flag; 1025 } 1026 } 1027 1028 /** @hide */ 1029 private static final class ResourceOveruseListenerInfo { 1030 public final ResourceOveruseListener listener; 1031 public final Executor executor; 1032 public final int resourceOveruseFlag; 1033 ResourceOveruseListenerInfo(ResourceOveruseListener listener, Executor executor, int resourceOveruseFlag)1034 ResourceOveruseListenerInfo(ResourceOveruseListener listener, 1035 Executor executor, int resourceOveruseFlag) { 1036 this.listener = listener; 1037 this.executor = executor; 1038 this.resourceOveruseFlag = resourceOveruseFlag; 1039 } 1040 1041 @Override equals(Object obj)1042 public boolean equals(Object obj) { 1043 if (obj == this) { 1044 return true; 1045 } 1046 if (!(obj instanceof ResourceOveruseListenerInfo)) { 1047 return false; 1048 } 1049 ResourceOveruseListenerInfo listenerInfo = (ResourceOveruseListenerInfo) obj; 1050 return listenerInfo.listener == listener; 1051 } 1052 } 1053 } 1054