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