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