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