• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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