1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.watchdog;
18 
19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED;
20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING;
24 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
25 import static android.content.Intent.ACTION_REBOOT;
26 import static android.content.Intent.ACTION_SHUTDOWN;
27 import static android.content.Intent.ACTION_USER_REMOVED;
28 
29 import static com.android.car.CarLog.TAG_WATCHDOG;
30 import static com.android.car.CarServiceUtils.assertAnyPermission;
31 import static com.android.car.CarServiceUtils.assertPermission;
32 import static com.android.car.CarServiceUtils.isEventAnyOfTypes;
33 import static com.android.car.CarServiceUtils.runOnMain;
34 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
35 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION;
36 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS;
37 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP;
38 
39 import android.annotation.NonNull;
40 import android.annotation.UserIdInt;
41 import android.automotive.watchdog.internal.GarageMode;
42 import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem;
43 import android.automotive.watchdog.internal.PackageInfo;
44 import android.automotive.watchdog.internal.PackageIoOveruseStats;
45 import android.automotive.watchdog.internal.PowerCycle;
46 import android.automotive.watchdog.internal.ResourceStats;
47 import android.automotive.watchdog.internal.StateType;
48 import android.automotive.watchdog.internal.UserPackageIoUsageStats;
49 import android.automotive.watchdog.internal.UserState;
50 import android.car.Car;
51 import android.car.builtin.util.Slogf;
52 import android.car.hardware.power.CarPowerManager;
53 import android.car.hardware.power.CarPowerPolicy;
54 import android.car.hardware.power.CarPowerPolicyFilter;
55 import android.car.hardware.power.ICarPowerPolicyListener;
56 import android.car.hardware.power.ICarPowerStateListener;
57 import android.car.hardware.power.PowerComponent;
58 import android.car.user.UserLifecycleEventFilter;
59 import android.car.watchdog.CarWatchdogManager;
60 import android.car.watchdog.ICarWatchdogService;
61 import android.car.watchdog.ICarWatchdogServiceCallback;
62 import android.car.watchdog.IResourceOveruseListener;
63 import android.car.watchdog.PackageKillableState;
64 import android.car.watchdog.ResourceOveruseConfiguration;
65 import android.car.watchdog.ResourceOveruseStats;
66 import android.car.watchdoglib.CarWatchdogDaemonHelper;
67 import android.content.BroadcastReceiver;
68 import android.content.Context;
69 import android.content.Intent;
70 import android.content.IntentFilter;
71 import android.os.RemoteException;
72 import android.os.ServiceSpecificException;
73 import android.os.UserHandle;
74 import android.os.UserManager;
75 import android.util.ArraySet;
76 import android.util.Log;
77 import android.util.proto.ProtoOutputStream;
78 
79 import com.android.car.CarLocalServices;
80 import com.android.car.CarLog;
81 import com.android.car.CarServiceBase;
82 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
83 import com.android.car.internal.dep.Trace;
84 import com.android.car.internal.util.ArrayUtils;
85 import com.android.car.internal.util.IndentingPrintWriter;
86 import com.android.car.power.CarPowerManagementService;
87 import com.android.car.systeminterface.SystemInterface;
88 import com.android.car.user.CarUserService;
89 import com.android.internal.annotations.GuardedBy;
90 import com.android.internal.annotations.VisibleForTesting;
91 
92 import java.io.File;
93 import java.lang.ref.WeakReference;
94 import java.time.Instant;
95 import java.util.Collections;
96 import java.util.List;
97 import java.util.Objects;
98 
99 /**
100  * Service to implement CarWatchdogManager API.
101  */
102 public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase {
103     static final String TAG = CarLog.tagFor(CarWatchdogService.class);
104     static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
105     static final String ACTION_GARAGE_MODE_ON =
106             "com.android.server.jobscheduler.GARAGE_MODE_ON";
107     static final String ACTION_GARAGE_MODE_OFF =
108             "com.android.server.jobscheduler.GARAGE_MODE_OFF";
109 
110     @VisibleForTesting
111     static final int MISSING_ARG_VALUE = -1;
112 
113     private static final String FALLBACK_DATA_SYSTEM_CAR_DIR_PATH = "/data/system/car";
114     private static final String WATCHDOG_DIR_NAME = "watchdog";
115 
116     private static final TimeSource SYSTEM_INSTANCE = new TimeSource() {
117         @Override
118         public Instant now() {
119             return Instant.now();
120         }
121 
122         @Override
123         public String toString() {
124             return "System time instance";
125         }
126     };
127 
128     private final Context mContext;
129     private final ICarWatchdogServiceForSystemImpl mWatchdogServiceForSystem;
130     private final PackageInfoHandler mPackageInfoHandler;
131     private final WatchdogStorage mWatchdogStorage;
132     private final WatchdogProcessHandler mWatchdogProcessHandler;
133     private final WatchdogPerfHandler mWatchdogPerfHandler;
134     private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener;
135 
136     private CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
137 
138     /*
139      * TODO(b/192481350): Listen for GarageMode change notification rather than depending on the
140      *  system_server broadcast when the CarService internal API for listening GarageMode change is
141      *  implemented.
142      */
143     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
144         @Override
145         public void onReceive(Context context, Intent intent) {
146             String action = intent.getAction();
147             Trace.beginSection("CarWatchdogService-broadcast-" + action);
148             switch (action) {
149                 case CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION:
150                 case CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS:
151                 case CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP:
152                     mWatchdogPerfHandler.processUserNotificationIntent(intent);
153                     break;
154                 case ACTION_GARAGE_MODE_ON:
155                 case ACTION_GARAGE_MODE_OFF:
156                     int garageMode;
157                     synchronized (mLock) {
158                         garageMode = mCurrentGarageMode = Objects.equals(action,
159                                 ACTION_GARAGE_MODE_ON)
160                                 ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF;
161                     }
162                     mWatchdogPerfHandler.onGarageModeChange(garageMode);
163                     if (garageMode == GarageMode.GARAGE_MODE_ON) {
164                         mWatchdogStorage.shrinkDatabase();
165                     }
166                     notifyGarageModeChange(garageMode);
167                     break;
168                 case ACTION_REBOOT:
169                 case ACTION_SHUTDOWN:
170                     // FLAG_RECEIVER_FOREGROUND is checked to ignore the intent from UserController
171                     // when a user is stopped.
172                     if ((intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) == 0) {
173                         break;
174                     }
175                     int powerCycle = PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER;
176                     try {
177                         mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE,
178                                 powerCycle, /* arg2= */ 0);
179                         if (DEBUG) {
180                             Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)",
181                                     powerCycle);
182                         }
183                     } catch (Exception e) {
184                         Slogf.w(TAG, e, "Notifying power cycle state change failed");
185                     }
186                     break;
187                 case ACTION_USER_REMOVED: {
188                     UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
189                     int userId = userHandle.getIdentifier();
190                     try {
191                         mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE,
192                                 userId, UserState.USER_STATE_REMOVED);
193                         if (DEBUG) {
194                             Slogf.d(TAG, "Notified car watchdog daemon of removed user %d",
195                                     userId);
196                         }
197                     } catch (RemoteException e) {
198                         Slogf.w(TAG, e, "Failed to notify car watchdog daemon of removed user %d",
199                                 userId);
200                     }
201                     mWatchdogPerfHandler.deleteUser(userId);
202                     break;
203                 }
204                 case ACTION_PACKAGE_CHANGED: {
205                     mWatchdogPerfHandler.processPackageChangedIntent(intent);
206                     break;
207                 }
208                 default:
209                     Slogf.i(TAG, "Ignoring unknown intent %s", intent);
210             }
211             Trace.endSection();
212         }
213     };
214 
215     private final ICarPowerStateListener mCarPowerStateListener =
216             new ICarPowerStateListener.Stub() {
217         @Override
218         public void onStateChanged(int state, long expirationTimeMs) {
219             CarPowerManagementService powerService =
220                     CarLocalServices.getService(CarPowerManagementService.class);
221             if (powerService == null) {
222                 return;
223             }
224             int powerState = powerService.getPowerState();
225             int powerCycle = carPowerStateToPowerCycle(powerState);
226             if (powerCycle < 0) {
227                 return;
228             }
229             Trace.beginSection("CarWatchdogService-powerStateChanged-"
230                     + CarPowerManagementService.powerStateToString(powerState));
231             switch (powerCycle) {
232                 case PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE:
233                     // Perform time consuming disk I/O operation during shutdown prepare to avoid
234                     // incomplete I/O.
235                     mWatchdogPerfHandler.writeMetadataFile();
236                     break;
237                 case PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER:
238                     // Watchdog service and daemon performs garage mode monitoring so delay writing
239                     // to database until after shutdown enter.
240                     mWatchdogPerfHandler.writeToDatabase();
241                     break;
242                 case PowerCycle.POWER_CYCLE_SUSPEND_EXIT:
243                     break;
244                 // ON covers resume.
245                 case PowerCycle.POWER_CYCLE_RESUME:
246                     // There might be outdated & incorrect info. We should reset them before
247                     // starting to do health check.
248                     mWatchdogProcessHandler.prepareHealthCheck();
249                     break;
250                 default:
251                     return;
252             }
253             notifyPowerCycleChange(powerCycle);
254             Trace.endSection();
255         }
256     };
257 
258     private final ICarPowerPolicyListener mCarDisplayPowerPolicyListener =
259             new ICarPowerPolicyListener.Stub() {
260                 @Override
261                 public void onPolicyChanged(CarPowerPolicy appliedPolicy,
262                         CarPowerPolicy accumulatedPolicy) {
263                     Trace.beginSection("CarWatchdogService-carPowerPolicyChanged-"
264                             + appliedPolicy.getPolicyId());
265                     boolean isDisplayEnabled =
266                             appliedPolicy.isComponentEnabled(PowerComponent.DISPLAY);
267                     boolean didStateChange = false;
268                     synchronized (mLock) {
269                         didStateChange = mIsDisplayEnabled != isDisplayEnabled;
270                         mIsDisplayEnabled = isDisplayEnabled;
271                     }
272                     if (didStateChange) {
273                         mWatchdogPerfHandler.onDisplayStateChanged(isDisplayEnabled);
274                     }
275                     Trace.endSection();;
276                 }
277             };
278 
279     private final Object mLock = new Object();
280     @GuardedBy("mLock")
281     private boolean mReadyToRespond;
282     @GuardedBy("mLock")
283     private boolean mIsConnected;
284     @GuardedBy("mLock")
285     private @GarageMode int mCurrentGarageMode;
286     @GuardedBy("mLock")
287     private boolean mIsDisplayEnabled;
288 
CarWatchdogService(Context context, Context carServiceBuiltinPackageContext)289     public CarWatchdogService(Context context, Context carServiceBuiltinPackageContext) {
290         this(context, carServiceBuiltinPackageContext,
291                 new WatchdogStorage(context, SYSTEM_INSTANCE), SYSTEM_INSTANCE);
292     }
293 
294     @VisibleForTesting
CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, WatchdogStorage watchdogStorage, TimeSource timeSource)295     public CarWatchdogService(Context context, Context carServiceBuiltinPackageContext,
296             WatchdogStorage watchdogStorage, TimeSource timeSource) {
297         this(context, carServiceBuiltinPackageContext, watchdogStorage,
298                 timeSource, /*watchdogProcessHandler=*/ null, /*watchdogPerfHandler=*/ null);
299     }
300 
301     @VisibleForTesting
CarWatchdogService(Context context, Context carServiceBuiltinPackageContext, WatchdogStorage watchdogStorage, TimeSource timeSource, WatchdogProcessHandler watchdogProcessHandler, WatchdogPerfHandler watchdogPerfHandler)302     CarWatchdogService(Context context, Context carServiceBuiltinPackageContext,
303             WatchdogStorage watchdogStorage, TimeSource timeSource,
304             WatchdogProcessHandler watchdogProcessHandler,
305             WatchdogPerfHandler watchdogPerfHandler) {
306         mContext = context;
307         mWatchdogStorage = watchdogStorage;
308         mPackageInfoHandler = new PackageInfoHandler(mContext.getPackageManager());
309         mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG);
310         mWatchdogServiceForSystem = new ICarWatchdogServiceForSystemImpl(this);
311         mWatchdogProcessHandler = watchdogProcessHandler != null ? watchdogProcessHandler
312                 : new WatchdogProcessHandler(mWatchdogServiceForSystem, mCarWatchdogDaemonHelper,
313                         mPackageInfoHandler);
314         mWatchdogPerfHandler =
315                 watchdogPerfHandler != null ? watchdogPerfHandler : new WatchdogPerfHandler(
316                         mContext, carServiceBuiltinPackageContext,
317                         mCarWatchdogDaemonHelper, mPackageInfoHandler, mWatchdogStorage,
318                         timeSource);
319         mConnectionListener = (isConnected) -> {
320             mWatchdogPerfHandler.onDaemonConnectionChange(isConnected);
321             synchronized (mLock) {
322                 mIsConnected = isConnected;
323             }
324             registerToDaemon();
325         };
326         mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF;
327         mIsDisplayEnabled = true;
328     }
329 
330     @VisibleForTesting
setCarWatchdogDaemonHelper(CarWatchdogDaemonHelper helper)331     public void setCarWatchdogDaemonHelper(CarWatchdogDaemonHelper helper) {
332         mCarWatchdogDaemonHelper = helper;
333     }
334 
335     @Override
init()336     public void init() {
337         Trace.beginSection("CarWatchdogService.init");
338         // TODO(b/266008677): The daemon reads the sendResourceUsageStatsEnabled sysprop at the
339         // moment the CarWatchdogService connects to it. Therefore, the property must be set by
340         // CarWatchdogService before connecting with the CarWatchdog daemon. Set the property to
341         // true to enable the sending of resource usage stats from the daemon.
342         mWatchdogProcessHandler.init();
343         mWatchdogPerfHandler.init();
344         subscribePowerManagementService();
345         subscribeUserStateChange();
346         subscribeBroadcastReceiver();
347         mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);
348         mCarWatchdogDaemonHelper.connect();
349         // To make sure the main handler is ready for responding to car watchdog daemon, registering
350         // to the daemon is done through the main handler. Once the registration is completed, we
351         // can assume that the main handler is not too busy handling other stuffs.
352         postRegisterToDaemonMessage();
353         if (DEBUG) {
354             Slogf.d(TAG, "CarWatchdogService is initialized");
355         }
356         Trace.endSection();
357     }
358 
359     @Override
release()360     public void release() {
361         Trace.beginSection("CarWatchdogService.release");
362         mContext.unregisterReceiver(mBroadcastReceiver);
363         unsubscribePowerManagementService();
364         mWatchdogPerfHandler.release();
365         mWatchdogStorage.release();
366         unregisterFromDaemon();
367         mCarWatchdogDaemonHelper.disconnect();
368         Trace.endSection();
369     }
370 
371     @Override
372     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)373     public void dump(IndentingPrintWriter writer) {
374         writer.println("*" + getClass().getSimpleName() + "*");
375         writer.increaseIndent();
376         synchronized (mLock) {
377             writer.println("Current garage mode: " + toGarageModeString(mCurrentGarageMode));
378         }
379         mWatchdogProcessHandler.dump(writer);
380         mWatchdogPerfHandler.dump(writer);
381         writer.decreaseIndent();
382     }
383 
384     @Override
385     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)386     public void dumpProto(ProtoOutputStream proto) {
387         synchronized (mLock) {
388             proto.write(CarWatchdogDumpProto.CURRENT_GARAGE_MODE, mCurrentGarageMode);
389         }
390         mWatchdogProcessHandler.dumpProto(proto);
391         mWatchdogPerfHandler.dumpProto(proto);
392     }
393 
394     /**
395      * Registers {@link android.car.watchdog.ICarWatchdogServiceCallback} to
396      * {@link CarWatchdogService}.
397      */
398     @Override
registerClient(ICarWatchdogServiceCallback client, int timeout)399     public void registerClient(ICarWatchdogServiceCallback client, int timeout) {
400         assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
401         mWatchdogProcessHandler.registerClient(client, timeout);
402     }
403 
404     /**
405      * Unregisters {@link android.car.watchdog.ICarWatchdogServiceCallback} from
406      * {@link CarWatchdogService}.
407      */
408     @Override
unregisterClient(ICarWatchdogServiceCallback client)409     public void unregisterClient(ICarWatchdogServiceCallback client) {
410         assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
411         mWatchdogProcessHandler.unregisterClient(client);
412     }
413 
414     /**
415      * Tells {@link CarWatchdogService} that the client is alive.
416      */
417     @Override
tellClientAlive(ICarWatchdogServiceCallback client, int sessionId)418     public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) {
419         assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
420         mWatchdogProcessHandler.tellClientAlive(client, sessionId);
421     }
422 
423     /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the calling package. */
424     @Override
425     @NonNull
getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)426     public ResourceOveruseStats getResourceOveruseStats(
427             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
428             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
429         return mWatchdogPerfHandler.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod);
430     }
431 
432     /**
433       *  Returns {@link android.car.watchdog.ResourceOveruseStats} for all packages for the maximum
434       *  specified period, and the specified resource types with stats greater than or equal to the
435       *  minimum specified stats.
436       */
437     @Override
438     @NonNull
getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)439     public List<ResourceOveruseStats> getAllResourceOveruseStats(
440             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
441             @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
442             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
443         assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
444         return mWatchdogPerfHandler.getAllResourceOveruseStats(resourceOveruseFlag,
445                 minimumStatsFlag, maxStatsPeriod);
446     }
447 
448     /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the specified user package. */
449     @Override
450     @NonNull
getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)451     public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
452             @NonNull String packageName, @NonNull UserHandle userHandle,
453             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
454             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
455         assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
456         return mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(packageName, userHandle,
457                 resourceOveruseFlag, maxStatsPeriod);
458     }
459 
460     /**
461      * Adds {@link android.car.watchdog.IResourceOveruseListener} for the calling package's resource
462      * overuse notifications.
463      */
464     @Override
addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)465     public void addResourceOveruseListener(
466             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
467             @NonNull IResourceOveruseListener listener) {
468         mWatchdogPerfHandler.addResourceOveruseListener(resourceOveruseFlag, listener);
469     }
470 
471     /**
472      * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for the
473      * calling package's resource overuse notifications.
474      */
475     @Override
removeResourceOveruseListener(@onNull IResourceOveruseListener listener)476     public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
477         mWatchdogPerfHandler.removeResourceOveruseListener(listener);
478     }
479 
480     /**
481      * Adds {@link android.car.watchdog.IResourceOveruseListener} for all packages' resource overuse
482      * notifications.
483      */
484     @Override
addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)485     public void addResourceOveruseListenerForSystem(
486             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
487             @NonNull IResourceOveruseListener listener) {
488         assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
489         mWatchdogPerfHandler.addResourceOveruseListenerForSystem(resourceOveruseFlag, listener);
490     }
491 
492     /**
493      * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for all
494      * packages' resource overuse notifications.
495      */
496     @Override
removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)497     public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
498         assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
499         mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(listener);
500     }
501 
502     /** Sets whether or not a user package is killable on resource overuse. */
503     @Override
setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)504     public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
505             boolean isKillable) {
506         assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
507         mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle, isKillable);
508     }
509 
510     /**
511      * Returns all {@link android.car.watchdog.PackageKillableState} on resource overuse for
512      * the specified user.
513      */
514     @Override
515     @NonNull
getPackageKillableStatesAsUser(UserHandle userHandle)516     public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
517         assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
518         return mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle);
519     }
520 
521     /**
522      * Sets {@link android.car.watchdog.ResourceOveruseConfiguration} for the specified resources.
523      */
524     @Override
525     @CarWatchdogManager.ReturnCode
setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)526     public int setResourceOveruseConfigurations(
527             List<ResourceOveruseConfiguration> configurations,
528             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
529             throws RemoteException {
530         assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
531         return mWatchdogPerfHandler.setResourceOveruseConfigurations(configurations,
532                 resourceOveruseFlag);
533     }
534 
535     /** Returns the available {@link android.car.watchdog.ResourceOveruseConfiguration}. */
536     @Override
537     @NonNull
getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)538     public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
539             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
540         assertAnyPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG,
541                 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
542         return mWatchdogPerfHandler.getResourceOveruseConfigurations(resourceOveruseFlag);
543     }
544 
545     /**
546      * Enables/disables the watchdog daemon client health check process.
547      */
controlProcessHealthCheck(boolean enable)548     public void controlProcessHealthCheck(boolean enable) {
549         assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
550         mWatchdogProcessHandler.controlProcessHealthCheck(enable);
551     }
552 
553     /**
554      * Kills a specific package for a user due to resource overuse.
555      *
556      * @return whether package was killed
557      */
performResourceOveruseKill(String packageName, @UserIdInt int userId)558     public boolean performResourceOveruseKill(String packageName, @UserIdInt int userId) {
559         assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
560         return mWatchdogPerfHandler.disablePackageForUser(packageName, userId);
561     }
562 
563     /**
564      * Sets the thread priority for a specific thread.
565      *
566      * The thread must belong to the calling process.
567      *
568      * @throws IllegalArgumentException If the policy/priority is not valid.
569      * @throws IllegalStateException If the provided tid does not belong to the calling process.
570      * @throws RemoteException If binder error happens.
571      * @throws ServiceSpecificException If car watchdog daemon failed to set the thread priority.
572      * @throws UnsupportedOperationException If the current android release doesn't support the API.
573      */
setThreadPriority(int pid, int tid, int uid, int policy, int priority)574     public void setThreadPriority(int pid, int tid, int uid, int policy, int priority)
575             throws RemoteException {
576         mCarWatchdogDaemonHelper.setThreadPriority(pid, tid, uid, policy, priority);
577     }
578 
579     /**
580      * Gets the thread scheduling policy and priority for the specified thread.
581      *
582      * The thread must belong to the calling process.
583      *
584      * @throws IllegalStateException If the provided tid does not belong to the calling process or
585      *         car watchdog daemon failed to get the priority.
586      * @throws RemoteException If binder error happens.
587      * @throws UnsupportedOperationException If the current android release doesn't support the API.
588      */
getThreadPriority(int pid, int tid, int uid)589     public int[] getThreadPriority(int pid, int tid, int uid) throws RemoteException {
590         try {
591             return mCarWatchdogDaemonHelper.getThreadPriority(pid, tid, uid);
592         } catch (ServiceSpecificException e) {
593             // Car watchdog daemon failed to get the priority.
594             throw new IllegalStateException(e);
595         }
596     }
597 
598     @VisibleForTesting
getClientCount(int timeout)599     int getClientCount(int timeout) {
600         return mWatchdogProcessHandler.getClientCount(timeout);
601     }
602 
603     @VisibleForTesting
setOveruseHandlingDelay(long millis)604     void setOveruseHandlingDelay(long millis) {
605         mWatchdogPerfHandler.setOveruseHandlingDelay(millis);
606     }
607 
getWatchdogDirFile()608     static File getWatchdogDirFile() {
609         SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
610         String systemCarDirPath = systemInterface == null ? FALLBACK_DATA_SYSTEM_CAR_DIR_PATH
611                 : systemInterface.getSystemCarDir().getAbsolutePath();
612         return new File(systemCarDirPath, WATCHDOG_DIR_NAME);
613     }
614 
notifyAllUserStates()615     private void notifyAllUserStates() {
616         Trace.beginSection("CarWatchdogService.notifyAllUserStates");
617         UserManager userManager = mContext.getSystemService(UserManager.class);
618         List<UserHandle> users = userManager.getUserHandles(/* excludeDying= */ false);
619         try {
620             // TODO(b/152780162): reduce the number of RPC calls(isUserRunning).
621             for (int i = 0; i < users.size(); ++i) {
622                 UserHandle user = users.get(i);
623                 int userState = userManager.isUserRunning(user)
624                         ? UserState.USER_STATE_STARTED
625                         : UserState.USER_STATE_STOPPED;
626                 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE,
627                         user.getIdentifier(), userState);
628                 mWatchdogProcessHandler.updateUserState(user.getIdentifier(),
629                         userState == UserState.USER_STATE_STOPPED);
630             }
631             if (DEBUG) {
632                 Slogf.d(TAG, "Notified car watchdog daemon of user states");
633             }
634         } catch (RemoteException | RuntimeException e) {
635             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
636             // throws IllegalStateException. Catch the exception to avoid crashing the process.
637             Slogf.w(TAG, e, "Notifying latest user states failed");
638         }
639         Trace.endSection();
640     }
641 
notifyPowerCycleChange(@owerCycle int powerCycle)642     private void notifyPowerCycleChange(@PowerCycle int powerCycle) {
643         try {
644             mCarWatchdogDaemonHelper.notifySystemStateChange(
645                     StateType.POWER_CYCLE, powerCycle, MISSING_ARG_VALUE);
646             if (DEBUG) {
647                 Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
648             }
649         } catch (RemoteException | RuntimeException e) {
650             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
651             // throws IllegalStateException. Catch the exception to avoid crashing the process.
652             Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle);
653         }
654     }
655 
notifyGarageModeChange(@arageMode int garageMode)656     private void notifyGarageModeChange(@GarageMode int garageMode) {
657         try {
658             mCarWatchdogDaemonHelper.notifySystemStateChange(
659                     StateType.GARAGE_MODE, garageMode, MISSING_ARG_VALUE);
660             if (DEBUG) {
661                 Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode);
662             }
663         } catch (RemoteException | RuntimeException e) {
664             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
665             // throws IllegalStateException. Catch the exception to avoid crashing the process.
666             Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode);
667         }
668     }
669 
postRegisterToDaemonMessage()670     private void postRegisterToDaemonMessage() {
671         runOnMain(() -> {
672             synchronized (mLock) {
673                 mReadyToRespond = true;
674             }
675             registerToDaemon();
676         });
677     }
678 
registerToDaemon()679     private void registerToDaemon() {
680         synchronized (mLock) {
681             if (!mIsConnected || !mReadyToRespond) {
682                 return;
683             }
684         }
685         Trace.beginSection("CarWatchdogService.registerToDaemon");
686         try {
687             mCarWatchdogDaemonHelper.registerCarWatchdogService(mWatchdogServiceForSystem);
688             if (DEBUG) {
689                 Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon");
690             }
691         } catch (RemoteException | RuntimeException e) {
692             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
693             // throws IllegalStateException. Catch the exception to avoid crashing the process.
694             Slogf.w(TAG, e, "Cannot register to car watchdog daemon");
695         }
696         notifyAllUserStates();
697         CarPowerManagementService powerService =
698                 CarLocalServices.getService(CarPowerManagementService.class);
699         if (powerService != null) {
700             int powerState = powerService.getPowerState();
701             int powerCycle = carPowerStateToPowerCycle(powerState);
702             if (powerCycle >= 0) {
703                 notifyPowerCycleChange(powerCycle);
704             } else {
705                 Slogf.i(TAG, "Skipping notifying %d power state", powerState);
706             }
707         }
708         int garageMode;
709         synchronized (mLock) {
710             // To avoid race condition, fetch {@link mCurrentGarageMode} just before
711             // the {@link notifyGarageModeChange} call. For instance, if {@code mCurrentGarageMode}
712             // changes before the above {@link notifyPowerCycleChange} call returns,
713             // the {@link garageMode}'s value will be out of date.
714             garageMode = mCurrentGarageMode;
715         }
716         notifyGarageModeChange(garageMode);
717         Trace.endSection();
718     }
719 
unregisterFromDaemon()720     private void unregisterFromDaemon() {
721         Trace.beginSection("CarWatchdogService.unregisterFromDaemon");
722         try {
723             mCarWatchdogDaemonHelper.unregisterCarWatchdogService(mWatchdogServiceForSystem);
724             if (DEBUG) {
725                 Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon");
726             }
727         } catch (RemoteException | RuntimeException e) {
728             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
729             // throws IllegalStateException. Catch the exception to avoid crashing the process.
730             Slogf.w(TAG, e, "Cannot unregister from car watchdog daemon");
731         }
732         Trace.endSection();
733     }
734 
subscribePowerManagementService()735     private void subscribePowerManagementService() {
736         CarPowerManagementService powerService =
737                 CarLocalServices.getService(CarPowerManagementService.class);
738         if (powerService == null) {
739             Slogf.w(TAG, "Cannot get CarPowerManagementService");
740             return;
741         }
742         powerService.registerListener(mCarPowerStateListener);
743         powerService.addPowerPolicyListener(
744                 new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(),
745                 mCarDisplayPowerPolicyListener);
746     }
747 
unsubscribePowerManagementService()748     private void unsubscribePowerManagementService() {
749         CarPowerManagementService powerService =
750                 CarLocalServices.getService(CarPowerManagementService.class);
751         if (powerService == null) {
752             Slogf.w(TAG, "Cannot get CarPowerManagementService");
753             return;
754         }
755         powerService.unregisterListener(mCarPowerStateListener);
756         powerService.removePowerPolicyListener(mCarDisplayPowerPolicyListener);
757     }
758 
subscribeUserStateChange()759     private void subscribeUserStateChange() {
760         CarUserService userService = CarLocalServices.getService(CarUserService.class);
761         if (userService == null) {
762             Slogf.w(TAG, "Cannot get CarUserService");
763             return;
764         }
765         UserLifecycleEventFilter userEventFilter =
766                 new UserLifecycleEventFilter.Builder()
767                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_STARTING)
768                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
769                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING)
770                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED)
771                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPED).build();
772         userService.addUserLifecycleListener(userEventFilter, (event) -> {
773             if (!isEventAnyOfTypes(TAG, event, USER_LIFECYCLE_EVENT_TYPE_STARTING,
774                     USER_LIFECYCLE_EVENT_TYPE_SWITCHING, USER_LIFECYCLE_EVENT_TYPE_UNLOCKING,
775                     USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, USER_LIFECYCLE_EVENT_TYPE_STOPPED)) {
776                 return;
777             }
778 
779             int userId = event.getUserHandle().getIdentifier();
780             int userState;
781             String userStateDesc;
782             switch (event.getEventType()) {
783                 case USER_LIFECYCLE_EVENT_TYPE_STARTING:
784                     mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ false);
785                     userState = UserState.USER_STATE_STARTED;
786                     userStateDesc = "STARTING";
787                     break;
788                 case USER_LIFECYCLE_EVENT_TYPE_SWITCHING:
789                     userState = UserState.USER_STATE_SWITCHING;
790                     userStateDesc = "SWITCHING";
791                     break;
792                 case USER_LIFECYCLE_EVENT_TYPE_UNLOCKING:
793                     userState = UserState.USER_STATE_UNLOCKING;
794                     userStateDesc = "UNLOCKING";
795                     break;
796                 case USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED:
797                     userState = UserState.USER_STATE_POST_UNLOCKED;
798                     userStateDesc = "POST_UNLOCKED";
799                     break;
800                 case USER_LIFECYCLE_EVENT_TYPE_STOPPED:
801                     mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ true);
802                     userState = UserState.USER_STATE_STOPPED;
803                     userStateDesc = "STOPPING";
804                     break;
805                 default:
806                     return;
807             }
808             try {
809                 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId,
810                         userState);
811                 if (DEBUG) {
812                     Slogf.d(TAG, "Notified car watchdog daemon user %d's user state, %s",
813                             userId, userStateDesc);
814                 }
815             } catch (RemoteException | RuntimeException e) {
816                 // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
817                 // throws IllegalStateException. Catch the exception to avoid crashing the process.
818                 Slogf.w(TAG, e, "Notifying user state change failed");
819             }
820         });
821     }
822 
subscribeBroadcastReceiver()823     private void subscribeBroadcastReceiver() {
824         IntentFilter filter = new IntentFilter();
825         filter.addAction(CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION);
826         filter.addAction(ACTION_GARAGE_MODE_ON);
827         filter.addAction(ACTION_GARAGE_MODE_OFF);
828         filter.addAction(CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS);
829         filter.addAction(CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP);
830         filter.addAction(ACTION_USER_REMOVED);
831         filter.addAction(ACTION_REBOOT);
832         filter.addAction(ACTION_SHUTDOWN);
833 
834         mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter,
835                 Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, /* scheduler= */ null,
836                 Context.RECEIVER_NOT_EXPORTED);
837 
838         // The package data scheme applies only for the ACTION_PACKAGE_CHANGED action. So, add a
839         // filter for this action separately. Otherwise, the broadcast receiver won't receive
840         // notifications for other actions.
841         IntentFilter packageChangedFilter = new IntentFilter();
842         packageChangedFilter.addAction(ACTION_PACKAGE_CHANGED);
843         packageChangedFilter.addDataScheme("package");
844 
845         mContext.registerReceiverForAllUsers(mBroadcastReceiver, packageChangedFilter,
846                 /* broadcastPermission= */ null, /* scheduler= */ null,
847                 Context.RECEIVER_NOT_EXPORTED);
848     }
849 
carPowerStateToPowerCycle(int powerState)850     private static int carPowerStateToPowerCycle(int powerState) {
851         switch (powerState) {
852             // SHUTDOWN_PREPARE covers suspend and shutdown.
853             case CarPowerManager.STATE_SHUTDOWN_PREPARE:
854                 return PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE;
855             case CarPowerManager.STATE_SHUTDOWN_ENTER:
856             case CarPowerManager.STATE_SUSPEND_ENTER:
857             case CarPowerManager.STATE_HIBERNATION_ENTER:
858                 return PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER;
859             case CarPowerManager.STATE_SUSPEND_EXIT:
860             case CarPowerManager.STATE_HIBERNATION_EXIT:
861                 return PowerCycle.POWER_CYCLE_SUSPEND_EXIT;
862             // ON covers resume.
863             case CarPowerManager.STATE_ON:
864                 return PowerCycle.POWER_CYCLE_RESUME;
865             default:
866                 Slogf.e(TAG, "Invalid power state: %d", powerState);
867         }
868         return -1;
869     }
870 
toGarageModeString(@arageMode int garageMode)871     private static String toGarageModeString(@GarageMode int garageMode) {
872         switch (garageMode) {
873             case GarageMode.GARAGE_MODE_OFF:
874                 return "GARAGE_MODE_OFF";
875             case GarageMode.GARAGE_MODE_ON:
876                 return "GARAGE_MODE_ON";
877             default:
878                 Slogf.e(TAG, "Invalid garage mode: %d", garageMode);
879         }
880         return "INVALID";
881     }
882 
883     private static final class ICarWatchdogServiceForSystemImpl
884             extends ICarWatchdogServiceForSystem.Stub {
885         private final WeakReference<CarWatchdogService> mService;
886 
ICarWatchdogServiceForSystemImpl(CarWatchdogService service)887         ICarWatchdogServiceForSystemImpl(CarWatchdogService service) {
888             mService = new WeakReference<>(service);
889         }
890 
891         @Override
checkIfAlive(int sessionId, int timeout)892         public void checkIfAlive(int sessionId, int timeout) {
893             CarWatchdogService service = mService.get();
894             if (service == null) {
895                 Slogf.w(TAG, "CarWatchdogService is not available");
896                 return;
897             }
898             service.mWatchdogProcessHandler.postHealthCheckMessage(sessionId);
899         }
900 
901         @Override
prepareProcessTermination()902         public void prepareProcessTermination() {
903             Slogf.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon");
904         }
905 
906         @Override
getPackageInfosForUids( int[] uids, List<String> vendorPackagePrefixes)907         public List<PackageInfo> getPackageInfosForUids(
908                 int[] uids, List<String> vendorPackagePrefixes) {
909             if (ArrayUtils.isEmpty(uids)) {
910                 Slogf.w(TAG, "UID list is empty");
911                 return Collections.emptyList();
912             }
913             CarWatchdogService service = mService.get();
914             if (service == null) {
915                 Slogf.w(TAG, "CarWatchdogService is not available");
916                 return Collections.emptyList();
917             }
918             return service.mPackageInfoHandler.getPackageInfosForUids(uids, vendorPackagePrefixes);
919         }
920 
921         // TODO(b/269191275): This method was replaced by onLatestResourceStats in Android U.
922         //  Make method no-op in Android W (N+2 releases).
923         @Override
latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)924         public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
925             if (packageIoOveruseStats.isEmpty()) {
926                 Slogf.w(TAG, "Latest I/O overuse stats is empty");
927                 return;
928             }
929             CarWatchdogService service = mService.get();
930             if (service == null) {
931                 Slogf.w(TAG, "CarWatchdogService is not available");
932                 return;
933             }
934             service.mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats);
935         }
936 
937         @Override
onLatestResourceStats(List<ResourceStats> resourceStats)938         public void onLatestResourceStats(List<ResourceStats> resourceStats) {
939             // TODO(b/266008146): Handle the resourceUsageStats.
940             if (resourceStats.isEmpty()) {
941                 Slogf.w(TAG, "Latest resource stats is empty");
942                 return;
943             }
944             CarWatchdogService service = mService.get();
945             if (service == null) {
946                 Slogf.w(TAG, "CarWatchdogService is not available");
947                 return;
948             }
949             for (int i = 0; i < resourceStats.size(); i++) {
950                 ResourceStats stats = resourceStats.get(i);
951                 if (stats.resourceOveruseStats == null
952                         || stats.resourceOveruseStats.packageIoOveruseStats.isEmpty()) {
953                     Slogf.w(TAG, "Received latest I/O overuse stats is empty");
954                     continue;
955                 }
956                 service.mWatchdogPerfHandler.latestIoOveruseStats(
957                         stats.resourceOveruseStats.packageIoOveruseStats);
958             }
959         }
960 
961         @Override
resetResourceOveruseStats(List<String> packageNames)962         public void resetResourceOveruseStats(List<String> packageNames) {
963             if (packageNames.isEmpty()) {
964                 Slogf.w(TAG, "Provided an empty package name to reset resource overuse stats");
965                 return;
966             }
967             CarWatchdogService service = mService.get();
968             if (service == null) {
969                 Slogf.w(TAG, "CarWatchdogService is not available");
970                 return;
971             }
972             service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames));
973         }
974 
975         // TODO(b/273354756): This method was replaced by an async request/response pattern
976         // Android U. Requests for the I/O stats are received through the requestTodayIoUsageStats
977         // method. And responses are sent through the carwatchdog daemon via
978         // ICarWatchdog#onTodayIoUsageStats. Make method no-op in Android W (N+2 releases).
979         @Override
getTodayIoUsageStats()980         public List<UserPackageIoUsageStats> getTodayIoUsageStats() {
981             CarWatchdogService service = mService.get();
982             if (service == null) {
983                 Slogf.w(TAG, "CarWatchdogService is not available");
984                 return Collections.emptyList();
985             }
986             return service.mWatchdogPerfHandler.getTodayIoUsageStats();
987         }
988 
989         @Override
requestAidlVhalPid()990         public void requestAidlVhalPid() {
991             CarWatchdogService service = mService.get();
992             if (service == null) {
993                 Slogf.w(TAG, "CarWatchdogService is not available");
994                 return;
995             }
996             service.mWatchdogProcessHandler.asyncFetchAidlVhalPid();
997         }
998 
999         @Override
requestTodayIoUsageStats()1000         public void requestTodayIoUsageStats() {
1001             CarWatchdogService service = mService.get();
1002             if (service == null) {
1003                 Slogf.w(TAG, "CarWatchdogService is not available");
1004                 return;
1005             }
1006             service.mWatchdogPerfHandler.asyncFetchTodayIoUsageStats();
1007         }
1008 
1009         @Override
getInterfaceHash()1010         public String getInterfaceHash() {
1011             return ICarWatchdogServiceForSystemImpl.HASH;
1012         }
1013 
1014         @Override
getInterfaceVersion()1015         public int getInterfaceVersion() {
1016             return ICarWatchdogServiceForSystemImpl.VERSION;
1017         }
1018     }
1019 }
1020