1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.watchdog;
18 
19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
21 import static android.content.Intent.ACTION_USER_REMOVED;
22 
23 import static com.android.car.CarLog.TAG_WATCHDOG;
24 
25 import android.annotation.NonNull;
26 import android.automotive.watchdog.internal.GarageMode;
27 import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem;
28 import android.automotive.watchdog.internal.PackageInfo;
29 import android.automotive.watchdog.internal.PackageIoOveruseStats;
30 import android.automotive.watchdog.internal.PowerCycle;
31 import android.automotive.watchdog.internal.StateType;
32 import android.automotive.watchdog.internal.UserPackageIoUsageStats;
33 import android.automotive.watchdog.internal.UserState;
34 import android.car.Car;
35 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
36 import android.car.hardware.power.CarPowerPolicy;
37 import android.car.hardware.power.CarPowerPolicyFilter;
38 import android.car.hardware.power.ICarPowerPolicyListener;
39 import android.car.hardware.power.ICarPowerStateListener;
40 import android.car.hardware.power.PowerComponent;
41 import android.car.watchdog.CarWatchdogManager;
42 import android.car.watchdog.ICarWatchdogService;
43 import android.car.watchdog.ICarWatchdogServiceCallback;
44 import android.car.watchdog.IResourceOveruseListener;
45 import android.car.watchdog.PackageKillableState;
46 import android.car.watchdog.ResourceOveruseConfiguration;
47 import android.car.watchdog.ResourceOveruseStats;
48 import android.car.watchdoglib.CarWatchdogDaemonHelper;
49 import android.content.BroadcastReceiver;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.pm.UserInfo;
54 import android.os.RemoteException;
55 import android.os.UserHandle;
56 import android.os.UserManager;
57 import android.util.ArraySet;
58 import android.util.IndentingPrintWriter;
59 
60 import com.android.car.CarLocalServices;
61 import com.android.car.CarLog;
62 import com.android.car.CarServiceBase;
63 import com.android.car.CarServiceUtils;
64 import com.android.car.ICarImpl;
65 import com.android.car.power.CarPowerManagementService;
66 import com.android.car.systeminterface.SystemInterface;
67 import com.android.car.user.CarUserService;
68 import com.android.internal.annotations.GuardedBy;
69 import com.android.internal.annotations.VisibleForTesting;
70 import com.android.internal.util.ArrayUtils;
71 import com.android.server.utils.Slogf;
72 
73 import java.io.File;
74 import java.lang.ref.WeakReference;
75 import java.time.Instant;
76 import java.util.List;
77 
78 /**
79  * Service to implement CarWatchdogManager API.
80  */
81 public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase {
82     static final boolean DEBUG = false; // STOPSHIP if true
83     static final String TAG = CarLog.tagFor(CarWatchdogService.class);
84     static final String ACTION_GARAGE_MODE_ON =
85             "com.android.server.jobscheduler.GARAGE_MODE_ON";
86     static final String ACTION_GARAGE_MODE_OFF =
87             "com.android.server.jobscheduler.GARAGE_MODE_OFF";
88     static final String ACTION_LAUNCH_APP_SETTINGS =
89             "com.android.car.watchdog.ACTION_LAUNCH_APP_SETTINGS";
90     static final String ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION =
91             "com.android.car.watchdog.ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION";
92     static final String ACTION_RESOURCE_OVERUSE_DISABLE_APP =
93             "com.android.car.watchdog.ACTION_RESOURCE_OVERUSE_DISABLE_APP";
94 
95     @VisibleForTesting
96     static final int MISSING_ARG_VALUE = -1;
97 
98     private static final String FALLBACK_DATA_SYSTEM_CAR_DIR_PATH = "/data/system/car";
99     private static final String WATCHDOG_DIR_NAME = "watchdog";
100 
101     private static final TimeSource SYSTEM_INSTANCE = new TimeSource() {
102         @Override
103         public Instant now() {
104             return Instant.now();
105         }
106 
107         @Override
108         public String toString() {
109             return "System time instance";
110         }
111     };
112 
113     private final Context mContext;
114     private final ICarWatchdogServiceForSystemImpl mWatchdogServiceForSystem;
115     private final PackageInfoHandler mPackageInfoHandler;
116     private final WatchdogStorage mWatchdogStorage;
117     private final WatchdogProcessHandler mWatchdogProcessHandler;
118     private final WatchdogPerfHandler mWatchdogPerfHandler;
119     private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
120     private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener;
121     /*
122      * TODO(b/192481350): Listen for GarageMode change notification rather than depending on the
123      *  system_server broadcast when the CarService internal API for listening GarageMode change is
124      *  implemented.
125      */
126     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
127         @Override
128         public void onReceive(Context context, Intent intent) {
129             String action = intent.getAction();
130             switch (action) {
131                 case ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION:
132                 case ACTION_LAUNCH_APP_SETTINGS:
133                 case ACTION_RESOURCE_OVERUSE_DISABLE_APP:
134                     mWatchdogPerfHandler.handleIntent(context, intent);
135                     break;
136                 case ACTION_GARAGE_MODE_ON:
137                 case ACTION_GARAGE_MODE_OFF:
138                     int garageMode;
139                     synchronized (mLock) {
140                         garageMode = mCurrentGarageMode = action.equals(ACTION_GARAGE_MODE_ON)
141                                 ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF;
142                     }
143                     mWatchdogPerfHandler.onGarageModeChange(garageMode);
144                     if (garageMode == GarageMode.GARAGE_MODE_ON) {
145                         mWatchdogStorage.shrinkDatabase();
146                     }
147                     notifyGarageModeChange(garageMode);
148                     return;
149                 case ACTION_USER_REMOVED:
150                     UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
151                     int userId = userHandle.getIdentifier();
152                     try {
153                         mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE,
154                                 userId, UserState.USER_STATE_REMOVED);
155                         if (DEBUG) {
156                             Slogf.d(TAG, "Notified car watchdog daemon of removed user %d",
157                                     userId);
158                         }
159                     } catch (RemoteException e) {
160                         Slogf.w(TAG, e, "Failed to notify car watchdog daemon of removed user %d",
161                                 userId);
162                     }
163                     mWatchdogPerfHandler.deleteUser(userId);
164                     return;
165             }
166         }
167     };
168 
169     private final ICarPowerStateListener mCarPowerStateListener =
170             new ICarPowerStateListener.Stub() {
171         @Override
172         public void onStateChanged(int state) {
173             CarPowerManagementService powerService =
174                     CarLocalServices.getService(CarPowerManagementService.class);
175             if (powerService == null) {
176                 return;
177             }
178             int powerCycle = carPowerStateToPowerCycle(powerService.getPowerState());
179             switch (powerCycle) {
180                 case PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE:
181                     // Perform time consuming disk I/O operation during shutdown prepare to avoid
182                     // incomplete I/O.
183                     mWatchdogPerfHandler.writeMetadataFile();
184                     break;
185                 case PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER:
186                     // Watchdog service and daemon performs garage mode monitoring so delay writing
187                     // to database until after shutdown enter.
188                     mWatchdogPerfHandler.writeToDatabase();
189                     break;
190                 // ON covers resume.
191                 case PowerCycle.POWER_CYCLE_RESUME:
192                     // There might be outdated & incorrect info. We should reset them before
193                     // starting to do health check.
194                     mWatchdogProcessHandler.prepareHealthCheck();
195                     break;
196                 default:
197                     return;
198             }
199             notifyPowerCycleChange(powerCycle);
200         }
201     };
202 
203     private final ICarPowerPolicyListener mCarDisplayPowerPolicyListener =
204             new ICarPowerPolicyListener.Stub() {
205                 @Override
206                 public void onPolicyChanged(CarPowerPolicy appliedPolicy,
207                         CarPowerPolicy accumulatedPolicy) {
208                     boolean isDisplayEnabled =
209                             appliedPolicy.isComponentEnabled(PowerComponent.DISPLAY);
210                     boolean didStateChange = false;
211                     synchronized (mLock) {
212                         didStateChange = mIsDisplayEnabled != isDisplayEnabled;
213                         mIsDisplayEnabled = isDisplayEnabled;
214                     }
215                     if (didStateChange) {
216                         mWatchdogPerfHandler.onDisplayStateChanged(isDisplayEnabled);
217                     }
218                 }
219             };
220 
221     private final Object mLock = new Object();
222     @GuardedBy("mLock")
223     private boolean mReadyToRespond;
224     @GuardedBy("mLock")
225     private boolean mIsConnected;
226     @GuardedBy("mLock")
227     private @GarageMode int mCurrentGarageMode;
228     @GuardedBy("mLock")
229     private boolean mIsDisplayEnabled;
230 
CarWatchdogService(Context context)231     public CarWatchdogService(Context context) {
232         this(context, new WatchdogStorage(context, SYSTEM_INSTANCE),
233                 new UserNotificationHelper(context), SYSTEM_INSTANCE);
234     }
235 
236     @VisibleForTesting
CarWatchdogService(Context context, WatchdogStorage watchdogStorage, UserNotificationHelper userNotificationHelper, TimeSource timeSource)237     CarWatchdogService(Context context, WatchdogStorage watchdogStorage,
238             UserNotificationHelper userNotificationHelper, TimeSource timeSource) {
239         mContext = context;
240         mWatchdogStorage = watchdogStorage;
241         mPackageInfoHandler = new PackageInfoHandler(mContext.getPackageManager());
242         mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG);
243         mWatchdogServiceForSystem = new ICarWatchdogServiceForSystemImpl(this);
244         mWatchdogProcessHandler = new WatchdogProcessHandler(mWatchdogServiceForSystem,
245                 mCarWatchdogDaemonHelper);
246         mWatchdogPerfHandler = new WatchdogPerfHandler(mContext, mCarWatchdogDaemonHelper,
247                 mPackageInfoHandler, mWatchdogStorage, userNotificationHelper, timeSource);
248         mConnectionListener = (isConnected) -> {
249             mWatchdogPerfHandler.onDaemonConnectionChange(isConnected);
250             synchronized (mLock) {
251                 mIsConnected = isConnected;
252             }
253             registerToDaemon();
254         };
255         mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF;
256         mIsDisplayEnabled = true;
257     }
258 
259     @Override
init()260     public void init() {
261         mWatchdogProcessHandler.init();
262         mWatchdogPerfHandler.init();
263         subscribePowerManagementService();
264         subscribeUserStateChange();
265         subscribeBroadcastReceiver();
266         mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);
267         mCarWatchdogDaemonHelper.connect();
268         // To make sure the main handler is ready for responding to car watchdog daemon, registering
269         // to the daemon is done through the main handler. Once the registration is completed, we
270         // can assume that the main handler is not too busy handling other stuffs.
271         postRegisterToDaemonMessage();
272         if (DEBUG) {
273             Slogf.d(TAG, "CarWatchdogService is initialized");
274         }
275     }
276 
277     @Override
release()278     public void release() {
279         mContext.unregisterReceiver(mBroadcastReceiver);
280         unsubscribePowerManagementService();
281         mWatchdogPerfHandler.release();
282         mWatchdogStorage.release();
283         unregisterFromDaemon();
284         mCarWatchdogDaemonHelper.disconnect();
285     }
286 
287     @Override
dump(IndentingPrintWriter writer)288     public void dump(IndentingPrintWriter writer) {
289         writer.println("*" + getClass().getSimpleName() + "*");
290         writer.increaseIndent();
291         synchronized (mLock) {
292             writer.println("Current garage mode: " + toGarageModeString(mCurrentGarageMode));
293         }
294         mWatchdogProcessHandler.dump(writer);
295         mWatchdogPerfHandler.dump(writer);
296         writer.decreaseIndent();
297     }
298 
299     /**
300      * Registers {@link android.car.watchdog.ICarWatchdogServiceCallback} to
301      * {@link CarWatchdogService}.
302      */
303     @Override
registerClient(ICarWatchdogServiceCallback client, int timeout)304     public void registerClient(ICarWatchdogServiceCallback client, int timeout) {
305         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
306         mWatchdogProcessHandler.registerClient(client, timeout);
307     }
308 
309     /**
310      * Unregisters {@link android.car.watchdog.ICarWatchdogServiceCallback} from
311      * {@link CarWatchdogService}.
312      */
313     @Override
unregisterClient(ICarWatchdogServiceCallback client)314     public void unregisterClient(ICarWatchdogServiceCallback client) {
315         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
316         mWatchdogProcessHandler.unregisterClient(client);
317     }
318 
319     /**
320      * Tells {@link CarWatchdogService} that the client is alive.
321      */
322     @Override
tellClientAlive(ICarWatchdogServiceCallback client, int sessionId)323     public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) {
324         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
325         mWatchdogProcessHandler.tellClientAlive(client, sessionId);
326     }
327 
328     /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the calling package. */
329     @Override
330     @NonNull
getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)331     public ResourceOveruseStats getResourceOveruseStats(
332             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
333             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
334         return mWatchdogPerfHandler.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod);
335     }
336 
337     /**
338       *  Returns {@link android.car.watchdog.ResourceOveruseStats} for all packages for the maximum
339       *  specified period, and the specified resource types with stats greater than or equal to the
340       *  minimum specified stats.
341       */
342     @Override
343     @NonNull
getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)344     public List<ResourceOveruseStats> getAllResourceOveruseStats(
345             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
346             @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
347             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
348         ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
349         return mWatchdogPerfHandler.getAllResourceOveruseStats(resourceOveruseFlag,
350                 minimumStatsFlag, maxStatsPeriod);
351     }
352 
353     /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the specified user package. */
354     @Override
355     @NonNull
getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)356     public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
357             @NonNull String packageName, @NonNull UserHandle userHandle,
358             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
359             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
360         ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
361         return mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(packageName, userHandle,
362                 resourceOveruseFlag, maxStatsPeriod);
363     }
364 
365     /**
366      * Adds {@link android.car.watchdog.IResourceOveruseListener} for the calling package's resource
367      * overuse notifications.
368      */
369     @Override
addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)370     public void addResourceOveruseListener(
371             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
372             @NonNull IResourceOveruseListener listener) {
373         mWatchdogPerfHandler.addResourceOveruseListener(resourceOveruseFlag, listener);
374     }
375 
376     /**
377      * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for the
378      * calling package's resource overuse notifications.
379      */
380     @Override
removeResourceOveruseListener(@onNull IResourceOveruseListener listener)381     public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
382         mWatchdogPerfHandler.removeResourceOveruseListener(listener);
383     }
384 
385     /**
386      * Adds {@link android.car.watchdog.IResourceOveruseListener} for all packages' resource overuse
387      * notifications.
388      */
389     @Override
addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)390     public void addResourceOveruseListenerForSystem(
391             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
392             @NonNull IResourceOveruseListener listener) {
393         ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
394         mWatchdogPerfHandler.addResourceOveruseListenerForSystem(resourceOveruseFlag, listener);
395     }
396 
397     /**
398      * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for all
399      * packages' resource overuse notifications.
400      */
401     @Override
removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)402     public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
403         ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
404         mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(listener);
405     }
406 
407     /** Sets whether or not a user package is killable on resource overuse. */
408     @Override
setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)409     public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
410             boolean isKillable) {
411         ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
412         mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle, isKillable);
413     }
414 
415     /**
416      * Returns all {@link android.car.watchdog.PackageKillableState} on resource overuse for
417      * the specified user.
418      */
419     @Override
420     @NonNull
getPackageKillableStatesAsUser(UserHandle userHandle)421     public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
422         ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
423         return mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle);
424     }
425 
426     /**
427      * Sets {@link android.car.watchdog.ResourceOveruseConfiguration} for the specified resources.
428      */
429     @Override
430     @CarWatchdogManager.ReturnCode
setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)431     public int setResourceOveruseConfigurations(
432             List<ResourceOveruseConfiguration> configurations,
433             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
434             throws RemoteException {
435         ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG);
436         return mWatchdogPerfHandler.setResourceOveruseConfigurations(configurations,
437                 resourceOveruseFlag);
438     }
439 
440     /** Returns the available {@link android.car.watchdog.ResourceOveruseConfiguration}. */
441     @Override
442     @NonNull
getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)443     public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
444             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
445         ICarImpl.assertAnyPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG,
446                 Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS);
447         return mWatchdogPerfHandler.getResourceOveruseConfigurations(resourceOveruseFlag);
448     }
449 
450     /**
451      * Enables/disables the watchdog daemon client health check process.
452      */
controlProcessHealthCheck(boolean disable)453     public void controlProcessHealthCheck(boolean disable) {
454         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG);
455         mWatchdogProcessHandler.controlProcessHealthCheck(disable);
456     }
457 
458     @VisibleForTesting
getClientCount(int timeout)459     int getClientCount(int timeout) {
460         return mWatchdogProcessHandler.getClientCount(timeout);
461     }
462 
463     @VisibleForTesting
setOveruseHandlingDelay(long millis)464     void setOveruseHandlingDelay(long millis) {
465         mWatchdogPerfHandler.setOveruseHandlingDelay(millis);
466     }
467 
468     @VisibleForTesting
setUidIoUsageSummaryTopCount(int uidIoUsageSummaryTopCount)469     void setUidIoUsageSummaryTopCount(int uidIoUsageSummaryTopCount) {
470         mWatchdogPerfHandler.setUidIoUsageSummaryTopCount(uidIoUsageSummaryTopCount);
471     }
472 
getWatchdogDirFile()473     static File getWatchdogDirFile() {
474         SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
475         String systemCarDirPath = systemInterface == null ? FALLBACK_DATA_SYSTEM_CAR_DIR_PATH
476                 : systemInterface.getSystemCarDir().getAbsolutePath();
477         return new File(systemCarDirPath, WATCHDOG_DIR_NAME);
478     }
479 
notifyAllUserStates()480     private void notifyAllUserStates() {
481         UserManager userManager = UserManager.get(mContext);
482         List<UserInfo> users = userManager.getUsers();
483         try {
484             // TODO(b/152780162): reduce the number of RPC calls(isUserRunning).
485             for (int i = 0; i < users.size(); ++i) {
486                 UserInfo info = users.get(i);
487                 int userState = userManager.isUserRunning(info.id)
488                         ? UserState.USER_STATE_STARTED
489                         : UserState.USER_STATE_STOPPED;
490                 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, info.id,
491                         userState);
492                 mWatchdogProcessHandler.updateUserState(info.id,
493                         userState == UserState.USER_STATE_STOPPED);
494             }
495             if (DEBUG) {
496                 Slogf.d(TAG, "Notified car watchdog daemon of user states");
497             }
498         } catch (RemoteException | RuntimeException e) {
499             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
500             // throws IllegalStateException. Catch the exception to avoid crashing the process.
501             Slogf.w(TAG, e, "Notifying latest user states failed");
502         }
503     }
504 
notifyPowerCycleChange(@owerCycle int powerCycle)505     private void notifyPowerCycleChange(@PowerCycle int powerCycle) {
506         if (powerCycle == PowerCycle.NUM_POWER_CYLES) {
507             Slogf.e(TAG, "Skipping notifying invalid power cycle (%d)", powerCycle);
508             return;
509         }
510         try {
511             mCarWatchdogDaemonHelper.notifySystemStateChange(
512                     StateType.POWER_CYCLE, powerCycle, MISSING_ARG_VALUE);
513             if (DEBUG) {
514                 Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
515             }
516         } catch (RemoteException | RuntimeException e) {
517             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
518             // throws IllegalStateException. Catch the exception to avoid crashing the process.
519             Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle);
520         }
521     }
522 
notifyGarageModeChange(@arageMode int garageMode)523     private void notifyGarageModeChange(@GarageMode int garageMode) {
524         try {
525             mCarWatchdogDaemonHelper.notifySystemStateChange(
526                     StateType.GARAGE_MODE, garageMode, MISSING_ARG_VALUE);
527             if (DEBUG) {
528                 Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode);
529             }
530         } catch (RemoteException | RuntimeException e) {
531             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
532             // throws IllegalStateException. Catch the exception to avoid crashing the process.
533             Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode);
534         }
535     }
536 
postRegisterToDaemonMessage()537     private void postRegisterToDaemonMessage() {
538         CarServiceUtils.runOnMain(() -> {
539             synchronized (mLock) {
540                 mReadyToRespond = true;
541             }
542             registerToDaemon();
543         });
544     }
545 
registerToDaemon()546     private void registerToDaemon() {
547         synchronized (mLock) {
548             if (!mIsConnected || !mReadyToRespond) {
549                 return;
550             }
551         }
552         try {
553             mCarWatchdogDaemonHelper.registerCarWatchdogService(mWatchdogServiceForSystem);
554             if (DEBUG) {
555                 Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon");
556             }
557         } catch (RemoteException | RuntimeException e) {
558             // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
559             // throws IllegalStateException. Catch the exception to avoid crashing the process.
560             Slogf.w(TAG, e, "Cannot register to car watchdog daemon");
561         }
562         notifyAllUserStates();
563         CarPowerManagementService powerService =
564                 CarLocalServices.getService(CarPowerManagementService.class);
565         if (powerService != null) {
566             int powerState = powerService.getPowerState();
567             int powerCycle = carPowerStateToPowerCycle(powerState);
568             if (powerCycle != PowerCycle.NUM_POWER_CYLES) {
569                 notifyPowerCycleChange(powerCycle);
570             } else {
571                 Slogf.i(TAG, "Skipping notifying %d power state", powerState);
572             }
573         }
574         int garageMode;
575         synchronized (mLock) {
576             // To avoid race condition, fetch {@link mCurrentGarageMode} just before
577             // the {@link notifyGarageModeChange} call. For instance, if {@code mCurrentGarageMode}
578             // changes before the above {@link notifyPowerCycleChange} call returns,
579             // the {@link garageMode}'s value will be out of date.
580             garageMode = mCurrentGarageMode;
581         }
582         notifyGarageModeChange(garageMode);
583     }
584 
unregisterFromDaemon()585     private void unregisterFromDaemon() {
586         try {
587             mCarWatchdogDaemonHelper.unregisterCarWatchdogService(mWatchdogServiceForSystem);
588             if (DEBUG) {
589                 Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon");
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, "Cannot unregister from car watchdog daemon");
595         }
596     }
597 
subscribePowerManagementService()598     private void subscribePowerManagementService() {
599         CarPowerManagementService powerService =
600                 CarLocalServices.getService(CarPowerManagementService.class);
601         if (powerService == null) {
602             Slogf.w(TAG, "Cannot get CarPowerManagementService");
603             return;
604         }
605         powerService.registerListener(mCarPowerStateListener);
606         powerService.addPowerPolicyListener(
607                 new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(),
608                 mCarDisplayPowerPolicyListener);
609     }
610 
unsubscribePowerManagementService()611     private void unsubscribePowerManagementService() {
612         CarPowerManagementService powerService =
613                 CarLocalServices.getService(CarPowerManagementService.class);
614         if (powerService == null) {
615             Slogf.w(TAG, "Cannot get CarPowerManagementService");
616             return;
617         }
618         powerService.unregisterListener(mCarPowerStateListener);
619         powerService.removePowerPolicyListener(mCarDisplayPowerPolicyListener);
620     }
621 
subscribeUserStateChange()622     private void subscribeUserStateChange() {
623         CarUserService userService = CarLocalServices.getService(CarUserService.class);
624         if (userService == null) {
625             Slogf.w(TAG, "Cannot get CarUserService");
626             return;
627         }
628         userService.addUserLifecycleListener((event) -> {
629             int userId = event.getUserHandle().getIdentifier();
630             int userState;
631             String userStateDesc;
632             switch (event.getEventType()) {
633                 case USER_LIFECYCLE_EVENT_TYPE_STARTING:
634                     mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ false);
635                     userState = UserState.USER_STATE_STARTED;
636                     userStateDesc = "STARTING";
637                     break;
638                 case USER_LIFECYCLE_EVENT_TYPE_STOPPED:
639                     mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ true);
640                     userState = UserState.USER_STATE_STOPPED;
641                     userStateDesc = "STOPPING";
642                     break;
643                 default:
644                     return;
645             }
646             try {
647                 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId,
648                         userState);
649                 if (DEBUG) {
650                     Slogf.d(TAG, "Notified car watchdog daemon user %d's user state, %s",
651                             userId, userStateDesc);
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, "Notifying user state change failed");
657             }
658         });
659     }
660 
subscribeBroadcastReceiver()661     private void subscribeBroadcastReceiver() {
662         IntentFilter filter = new IntentFilter();
663         filter.addAction(ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION);
664         filter.addAction(ACTION_GARAGE_MODE_ON);
665         filter.addAction(ACTION_GARAGE_MODE_OFF);
666         filter.addAction(ACTION_LAUNCH_APP_SETTINGS);
667         filter.addAction(ACTION_RESOURCE_OVERUSE_DISABLE_APP);
668         filter.addAction(ACTION_USER_REMOVED);
669 
670         mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter,
671                 Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, /* scheduler= */ null);
672     }
673 
carPowerStateToPowerCycle(int powerState)674     private static @PowerCycle int carPowerStateToPowerCycle(int powerState) {
675         switch (powerState) {
676             // SHUTDOWN_PREPARE covers suspend and shutdown.
677             case CarPowerStateListener.SHUTDOWN_PREPARE:
678                 return PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE;
679             case CarPowerStateListener.SHUTDOWN_ENTER:
680             case CarPowerStateListener.SUSPEND_ENTER:
681                 return PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER;
682             // ON covers resume.
683             case CarPowerStateListener.ON:
684                 return PowerCycle.POWER_CYCLE_RESUME;
685         }
686         return PowerCycle.NUM_POWER_CYLES;
687     }
688 
toGarageModeString(@arageMode int garageMode)689     private static String toGarageModeString(@GarageMode int garageMode) {
690         switch (garageMode) {
691             case GarageMode.GARAGE_MODE_OFF:
692                 return "GARAGE_MODE_OFF";
693             case GarageMode.GARAGE_MODE_ON:
694                 return "GARAGE_MODE_ON";
695         }
696         return "INVALID";
697     }
698 
699     private static final class ICarWatchdogServiceForSystemImpl
700             extends ICarWatchdogServiceForSystem.Stub {
701         private final WeakReference<CarWatchdogService> mService;
702 
ICarWatchdogServiceForSystemImpl(CarWatchdogService service)703         ICarWatchdogServiceForSystemImpl(CarWatchdogService service) {
704             mService = new WeakReference<>(service);
705         }
706 
707         @Override
checkIfAlive(int sessionId, int timeout)708         public void checkIfAlive(int sessionId, int timeout) {
709             CarWatchdogService service = mService.get();
710             if (service == null) {
711                 Slogf.w(TAG, "CarWatchdogService is not available");
712                 return;
713             }
714             service.mWatchdogProcessHandler.postHealthCheckMessage(sessionId);
715         }
716 
717         @Override
prepareProcessTermination()718         public void prepareProcessTermination() {
719             Slogf.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon");
720         }
721 
722         @Override
getPackageInfosForUids( int[] uids, List<String> vendorPackagePrefixes)723         public List<PackageInfo> getPackageInfosForUids(
724                 int[] uids, List<String> vendorPackagePrefixes) {
725             if (ArrayUtils.isEmpty(uids)) {
726                 Slogf.w(TAG, "UID list is empty");
727                 return null;
728             }
729             CarWatchdogService service = mService.get();
730             if (service == null) {
731                 Slogf.w(TAG, "CarWatchdogService is not available");
732                 return null;
733             }
734             return service.mPackageInfoHandler.getPackageInfosForUids(uids, vendorPackagePrefixes);
735         }
736 
737         @Override
latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)738         public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
739             if (packageIoOveruseStats.isEmpty()) {
740                 Slogf.w(TAG, "Latest I/O overuse stats is empty");
741                 return;
742             }
743             CarWatchdogService service = mService.get();
744             if (service == null) {
745                 Slogf.w(TAG, "CarWatchdogService is not available");
746                 return;
747             }
748             service.mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats);
749         }
750 
751         @Override
resetResourceOveruseStats(List<String> packageNames)752         public void resetResourceOveruseStats(List<String> packageNames) {
753             if (packageNames.isEmpty()) {
754                 Slogf.w(TAG, "Provided an empty package name to reset resource overuse stats");
755                 return;
756             }
757             CarWatchdogService service = mService.get();
758             if (service == null) {
759                 Slogf.w(TAG, "CarWatchdogService is not available");
760                 return;
761             }
762             service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames));
763         }
764 
765         @Override
getTodayIoUsageStats()766         public List<UserPackageIoUsageStats> getTodayIoUsageStats() {
767             CarWatchdogService service = mService.get();
768             if (service == null) {
769                 Slogf.w(TAG, "CarWatchdogService is not available");
770                 return null;
771             }
772             return service.mWatchdogPerfHandler.getTodayIoUsageStats();
773         }
774     }
775 }
776