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 to be returned. 414 * @param userId ID 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 * @param userHandle User whose setting should to be updated. 639 * @param isKillable Whether or not the package for the specified user is killable on resource 640 * overuse. 641 * 642 * @hide 643 */ 644 @SystemApi 645 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) setKillablePackageAsUser(@onNull String packageName, @NonNull UserHandle userHandle, boolean isKillable)646 public void setKillablePackageAsUser(@NonNull String packageName, 647 @NonNull UserHandle userHandle, boolean isKillable) { 648 try { 649 mService.setKillablePackageAsUser(packageName, userHandle, isKillable); 650 } catch (RemoteException e) { 651 handleRemoteExceptionFromCarService(e); 652 } 653 } 654 655 /** 656 * Returns the list of package killable states on resource overuse for the user. 657 * 658 * <p>This API may be used by CarSettings application or UI notification. 659 * 660 * @param userHandle User whose killable states for all packages should to be returned. 661 * 662 * @hide 663 */ 664 @SystemApi 665 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) 666 @NonNull getPackageKillableStatesAsUser( @onNull UserHandle userHandle)667 public List<PackageKillableState> getPackageKillableStatesAsUser( 668 @NonNull UserHandle userHandle) { 669 try { 670 return mService.getPackageKillableStatesAsUser(userHandle); 671 } catch (RemoteException e) { 672 return handleRemoteExceptionFromCarService(e, new ArrayList<>()); 673 } 674 } 675 676 /** 677 * Sets the resource overuse configurations for the components provided in the configurations. 678 * 679 * <p>Must provide only one configuration per component. System services should set the 680 * configurations only for system and third-party components. Vendor services should set the 681 * configuration only for the vendor component. 682 * 683 * @param configurations List of resource overuse configurations. One configuration per 684 * component. 685 * @param resourceOveruseFlag Flag to indicate the types of resource overuse configurations to 686 * set. 687 * 688 * @return - {@link #RETURN_CODE_SUCCESS} if the set request is successful. 689 * - {@link #RETURN_CODE_ERROR} if the set request cannot be completed and the client 690 * should retry later. 691 * 692 * @throws IllegalArgumentException if {@code configurations} are invalid. 693 * 694 * @hide 695 */ 696 @SystemApi 697 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG) 698 @ReturnCode setResourceOveruseConfigurations( @onNull List<ResourceOveruseConfiguration> configurations, @ResourceOveruseFlag int resourceOveruseFlag)699 public int setResourceOveruseConfigurations( 700 @NonNull List<ResourceOveruseConfiguration> configurations, 701 @ResourceOveruseFlag int resourceOveruseFlag) { 702 try { 703 return mService.setResourceOveruseConfigurations(configurations, resourceOveruseFlag); 704 } catch (RemoteException e) { 705 handleRemoteExceptionFromCarService(e); 706 return RETURN_CODE_ERROR; 707 } 708 } 709 710 /** 711 * Returns the current resource overuse configurations for all components. 712 * 713 * <p>This call is blocking and may take few seconds to return if the service is temporarily 714 * unavailable. 715 * 716 * @param resourceOveruseFlag Flag to indicate the types of resource overuse configurations to 717 * return. 718 * 719 * @return If the server process is alive and connected, returns list of available resource 720 * overuse configurations for all components. If the server process is dead, 721 * returns {@code null} value. 722 * 723 * @throws IllegalStateException if the system is in an invalid state. 724 * @hide 725 */ 726 @SystemApi 727 @RequiresPermission(anyOf = {Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, 728 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS}) 729 @Nullable getResourceOveruseConfigurations( @esourceOveruseFlag int resourceOveruseFlag)730 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( 731 @ResourceOveruseFlag int resourceOveruseFlag) { 732 try { 733 return mService.getResourceOveruseConfigurations(resourceOveruseFlag); 734 } catch (RemoteException e) { 735 return handleRemoteExceptionFromCarService(e, null); 736 } 737 } 738 739 /** @hide */ 740 @Override onCarDisconnected()741 public void onCarDisconnected() { 742 // nothing to do 743 } 744 checkClientStatus(int sessionId, int timeout)745 private void checkClientStatus(int sessionId, int timeout) { 746 CarWatchdogClientCallback client; 747 Executor executor; 748 mMainHandler.removeMessages(WHAT_CHECK_MAIN_THREAD); 749 synchronized (mLock) { 750 if (mRegisteredClient == null) { 751 Log.w(TAG, "Cannot check client status. The client has not been registered."); 752 return; 753 } 754 mSession.currentId = sessionId; 755 client = mRegisteredClient; 756 executor = mCallbackExecutor; 757 mRemainingConditions = NUMBER_OF_CONDITIONS_TO_BE_MET; 758 } 759 // For a car watchdog client to be active, 1) its main thread is active and 2) the client 760 // responds within timeout. When each condition is met, the remaining task counter is 761 // decreased. If the remaining task counter is zero, the client is considered active. 762 mMainHandler.sendMessage(obtainMessage(CarWatchdogManager::checkMainThread, this) 763 .setWhat(WHAT_CHECK_MAIN_THREAD)); 764 // Call the client callback to check if the client is active. 765 executor.execute(() -> { 766 boolean checkDone = client.onCheckHealthStatus(sessionId, timeout); 767 if (checkDone) { 768 boolean shouldReport; 769 synchronized (mLock) { 770 if (mSession.lastReportedId == sessionId) { 771 return; 772 } 773 mSession.lastReportedId = sessionId; 774 mRemainingConditions--; 775 shouldReport = checkConditionLocked(); 776 } 777 if (shouldReport) { 778 reportToService(sessionId); 779 } 780 } 781 }); 782 } 783 checkMainThread()784 private void checkMainThread() { 785 int sessionId; 786 boolean shouldReport; 787 synchronized (mLock) { 788 mRemainingConditions--; 789 sessionId = mSession.currentId; 790 shouldReport = checkConditionLocked(); 791 } 792 if (shouldReport) { 793 reportToService(sessionId); 794 } 795 } 796 checkConditionLocked()797 private boolean checkConditionLocked() { 798 if (mRemainingConditions < 0) { 799 Log.wtf(TAG, "Remaining condition is less than zero: should not happen"); 800 } 801 return mRemainingConditions == 0; 802 } 803 reportToService(int sessionId)804 private void reportToService(int sessionId) { 805 try { 806 mService.tellClientAlive(mClientImpl, sessionId); 807 } catch (RemoteException e) { 808 handleRemoteExceptionFromCarService(e); 809 } 810 } 811 notifyProcessTermination()812 private void notifyProcessTermination() { 813 CarWatchdogClientCallback client; 814 Executor executor; 815 synchronized (mLock) { 816 if (mRegisteredClient == null) { 817 Log.w(TAG, "Cannot notify the client. The client has not been registered."); 818 return; 819 } 820 client = mRegisteredClient; 821 executor = mCallbackExecutor; 822 } 823 executor.execute(() -> client.onPrepareProcessTermination()); 824 } 825 addResourceOveruseListenerImpl()826 private void addResourceOveruseListenerImpl() { 827 try { 828 mService.addResourceOveruseListener( 829 mResourceOveruseListenerImpl.resourceOveruseFlag(), 830 mResourceOveruseListenerImpl); 831 if (DEBUG) { 832 Log.d(TAG, "Resource overuse listener implementation is successfully added to " 833 + "service"); 834 } 835 } catch (RemoteException e) { 836 synchronized (mLock) { 837 mResourceOveruseListenerInfos.clear(); 838 } 839 handleRemoteExceptionFromCarService(e); 840 } 841 } 842 removeResourceOveruseListenerImpl()843 private void removeResourceOveruseListenerImpl() { 844 try { 845 mService.removeResourceOveruseListener(mResourceOveruseListenerImpl); 846 if (DEBUG) { 847 Log.d(TAG, "Resource overuse listener implementation is successfully removed " 848 + "from service"); 849 } 850 } catch (RemoteException e) { 851 handleRemoteExceptionFromCarService(e); 852 } 853 } 854 addResourceOveruseListenerForSystemImpl()855 private void addResourceOveruseListenerForSystemImpl() { 856 try { 857 mService.addResourceOveruseListenerForSystem( 858 mResourceOveruseListenerForSystemImpl.resourceOveruseFlag(), 859 mResourceOveruseListenerForSystemImpl); 860 if (DEBUG) { 861 Log.d(TAG, "Resource overuse listener for system implementation is successfully " 862 + "added to service"); 863 } 864 } catch (RemoteException e) { 865 synchronized (mLock) { 866 mResourceOveruseListenerForSystemInfos.clear(); 867 } 868 handleRemoteExceptionFromCarService(e); 869 } 870 } 871 removeResourceOveruseListenerForSystemImpl()872 private void removeResourceOveruseListenerForSystemImpl() { 873 try { 874 mService.removeResourceOveruseListenerForSystem(mResourceOveruseListenerForSystemImpl); 875 if (DEBUG) { 876 Log.d(TAG, "Resource overuse listener for system implementation is successfully " 877 + "removed from service"); 878 } 879 } catch (RemoteException e) { 880 handleRemoteExceptionFromCarService(e); 881 } 882 } 883 onResourceOveruse(ResourceOveruseStats resourceOveruseStats, boolean isSystem)884 private void onResourceOveruse(ResourceOveruseStats resourceOveruseStats, boolean isSystem) { 885 if (resourceOveruseStats.getIoOveruseStats() == null) { 886 Log.w(TAG, "Skipping resource overuse notification as the stats are missing"); 887 return; 888 } 889 List<ResourceOveruseListenerInfo> listenerInfos; 890 synchronized (mLock) { 891 if (isSystem) { 892 listenerInfos = mResourceOveruseListenerForSystemInfos; 893 } else { 894 listenerInfos = mResourceOveruseListenerInfos; 895 } 896 } 897 if (listenerInfos.isEmpty()) { 898 Log.w(TAG, "Cannot notify resource overuse listener " + (isSystem ? "for system " : "") 899 + "as it is not registered."); 900 return; 901 } 902 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) { 903 if ((listenerInfo.resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) == 1) { 904 listenerInfo.executor.execute(() -> { 905 listenerInfo.listener.onOveruse(resourceOveruseStats); 906 }); 907 } 908 } 909 } 910 911 /** @hide */ 912 private static final class ICarWatchdogClientImpl extends ICarWatchdogServiceCallback.Stub { 913 private final WeakReference<CarWatchdogManager> mManager; 914 ICarWatchdogClientImpl(CarWatchdogManager manager)915 ICarWatchdogClientImpl(CarWatchdogManager manager) { 916 mManager = new WeakReference<>(manager); 917 } 918 919 @Override onCheckHealthStatus(int sessionId, int timeout)920 public void onCheckHealthStatus(int sessionId, int timeout) { 921 CarWatchdogManager manager = mManager.get(); 922 if (manager != null) { 923 manager.checkClientStatus(sessionId, timeout); 924 } 925 } 926 927 @Override onPrepareProcessTermination()928 public void onPrepareProcessTermination() { 929 CarWatchdogManager manager = mManager.get(); 930 if (manager != null) { 931 manager.notifyProcessTermination(); 932 } 933 } 934 } 935 936 private static final class SessionInfo { 937 public int currentId; 938 public int lastReportedId; 939 SessionInfo(int currentId, int lastReportedId)940 SessionInfo(int currentId, int lastReportedId) { 941 this.currentId = currentId; 942 this.lastReportedId = lastReportedId; 943 } 944 } 945 946 /** @hide */ 947 private static final class IResourceOveruseListenerImpl extends IResourceOveruseListener.Stub { 948 private static final int[] RESOURCE_OVERUSE_FLAGS = new int[]{ FLAG_RESOURCE_OVERUSE_IO }; 949 950 private final WeakReference<CarWatchdogManager> mManager; 951 private final boolean mIsSystem; 952 953 private final Object mLock = new Object(); 954 @GuardedBy("mLock") 955 private final SparseIntArray mNumListenersByResource; 956 957 IResourceOveruseListenerImpl(CarWatchdogManager manager, boolean isSystem)958 IResourceOveruseListenerImpl(CarWatchdogManager manager, boolean isSystem) { 959 mManager = new WeakReference<>(manager); 960 mIsSystem = isSystem; 961 mNumListenersByResource = new SparseIntArray(); 962 } 963 964 @Override onOveruse(ResourceOveruseStats resourceOveruserStats)965 public void onOveruse(ResourceOveruseStats resourceOveruserStats) { 966 CarWatchdogManager manager = mManager.get(); 967 if (manager != null) { 968 manager.onResourceOveruse(resourceOveruserStats, mIsSystem); 969 } 970 } 971 hasListeners()972 public boolean hasListeners() { 973 synchronized (mLock) { 974 return mNumListenersByResource.size() != 0; 975 } 976 } 977 maybeAppendFlag(int appendFlag)978 public boolean maybeAppendFlag(int appendFlag) { 979 boolean isChanged = false; 980 synchronized (mLock) { 981 for (int flag : RESOURCE_OVERUSE_FLAGS) { 982 if ((appendFlag & flag) != 1) { 983 continue; 984 } 985 int value = mNumListenersByResource.get(flag, 0); 986 isChanged = ++value == 1; 987 mNumListenersByResource.put(flag, value); 988 } 989 } 990 return isChanged; 991 } 992 maybeRemoveFlag(int removeFlag)993 public boolean maybeRemoveFlag(int removeFlag) { 994 boolean isChanged = false; 995 synchronized (mLock) { 996 for (int flag : RESOURCE_OVERUSE_FLAGS) { 997 if ((removeFlag & flag) != 1) { 998 continue; 999 } 1000 int value = mNumListenersByResource.get(flag, 0); 1001 if (value == 0) { 1002 continue; 1003 } 1004 if (--value == 0) { 1005 isChanged = true; 1006 mNumListenersByResource.delete(flag); 1007 } else { 1008 mNumListenersByResource.put(flag, value); 1009 } 1010 } 1011 } 1012 return isChanged; 1013 } 1014 resourceOveruseFlag()1015 public int resourceOveruseFlag() { 1016 int flag = 0; 1017 for (int i = 0; i < mNumListenersByResource.size(); ++i) { 1018 flag |= mNumListenersByResource.valueAt(i) > 0 ? mNumListenersByResource.keyAt(i) 1019 : 0; 1020 } 1021 return flag; 1022 } 1023 } 1024 1025 /** @hide */ 1026 private static final class ResourceOveruseListenerInfo { 1027 public final ResourceOveruseListener listener; 1028 public final Executor executor; 1029 public final int resourceOveruseFlag; 1030 ResourceOveruseListenerInfo(ResourceOveruseListener listener, Executor executor, int resourceOveruseFlag)1031 ResourceOveruseListenerInfo(ResourceOveruseListener listener, 1032 Executor executor, int resourceOveruseFlag) { 1033 this.listener = listener; 1034 this.executor = executor; 1035 this.resourceOveruseFlag = resourceOveruseFlag; 1036 } 1037 1038 @Override equals(Object obj)1039 public boolean equals(Object obj) { 1040 if (obj == this) { 1041 return true; 1042 } 1043 if (!(obj instanceof ResourceOveruseListenerInfo)) { 1044 return false; 1045 } 1046 ResourceOveruseListenerInfo listenerInfo = (ResourceOveruseListenerInfo) obj; 1047 return listenerInfo.listener == listener; 1048 } 1049 } 1050 } 1051