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