| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.watchdog; |
| |
| import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING; |
| import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED; |
| |
| import static com.android.car.CarLog.TAG_WATCHDOG; |
| |
| import android.annotation.NonNull; |
| import android.automotive.watchdog.internal.GarageMode; |
| import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem; |
| import android.automotive.watchdog.internal.PackageInfo; |
| import android.automotive.watchdog.internal.PackageIoOveruseStats; |
| import android.automotive.watchdog.internal.PowerCycle; |
| import android.automotive.watchdog.internal.StateType; |
| import android.automotive.watchdog.internal.UserPackageIoUsageStats; |
| import android.automotive.watchdog.internal.UserState; |
| import android.car.Car; |
| import android.car.hardware.power.CarPowerManager.CarPowerStateListener; |
| import android.car.hardware.power.CarPowerPolicy; |
| import android.car.hardware.power.CarPowerPolicyFilter; |
| import android.car.hardware.power.ICarPowerPolicyListener; |
| import android.car.hardware.power.ICarPowerStateListener; |
| import android.car.hardware.power.PowerComponent; |
| import android.car.watchdog.CarWatchdogManager; |
| import android.car.watchdog.ICarWatchdogService; |
| import android.car.watchdog.ICarWatchdogServiceCallback; |
| import android.car.watchdog.IResourceOveruseListener; |
| import android.car.watchdog.PackageKillableState; |
| import android.car.watchdog.ResourceOveruseConfiguration; |
| import android.car.watchdog.ResourceOveruseStats; |
| import android.car.watchdoglib.CarWatchdogDaemonHelper; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.UserInfo; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.util.ArraySet; |
| import android.util.IndentingPrintWriter; |
| |
| import com.android.car.CarLocalServices; |
| import com.android.car.CarLog; |
| import com.android.car.CarServiceBase; |
| import com.android.car.CarServiceUtils; |
| import com.android.car.ICarImpl; |
| import com.android.car.power.CarPowerManagementService; |
| import com.android.car.user.CarUserService; |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.server.utils.Slogf; |
| |
| import java.lang.ref.WeakReference; |
| import java.time.Instant; |
| import java.util.List; |
| |
| /** |
| * Service to implement CarWatchdogManager API. |
| */ |
| public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase { |
| static final boolean DEBUG = false; // STOPSHIP if true |
| static final String TAG = CarLog.tagFor(CarWatchdogService.class); |
| static final String ACTION_GARAGE_MODE_ON = |
| "com.android.server.jobscheduler.GARAGE_MODE_ON"; |
| static final String ACTION_GARAGE_MODE_OFF = |
| "com.android.server.jobscheduler.GARAGE_MODE_OFF"; |
| static final int MISSING_ARG_VALUE = -1; |
| static final TimeSourceInterface SYSTEM_INSTANCE = new TimeSourceInterface() { |
| @Override |
| public Instant now() { |
| return Instant.now(); |
| } |
| |
| @Override |
| public String toString() { |
| return "System time instance"; |
| } |
| }; |
| |
| private final Context mContext; |
| private final ICarWatchdogServiceForSystemImpl mWatchdogServiceForSystem; |
| private final PackageInfoHandler mPackageInfoHandler; |
| private final WatchdogStorage mWatchdogStorage; |
| private final WatchdogProcessHandler mWatchdogProcessHandler; |
| private final WatchdogPerfHandler mWatchdogPerfHandler; |
| private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper; |
| private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener; |
| /* |
| * TODO(b/192481350): Listen for GarageMode change notification rather than depending on the |
| * system_server broadcast when the CarService internal API for listening GarageMode change is |
| * implemented. |
| */ |
| private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final String action = intent.getAction(); |
| switch (action) { |
| case ACTION_GARAGE_MODE_ON: |
| case ACTION_GARAGE_MODE_OFF: |
| int garageMode; |
| synchronized (mLock) { |
| garageMode = mCurrentGarageMode = action.equals(ACTION_GARAGE_MODE_ON) |
| ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF; |
| } |
| mWatchdogPerfHandler.onGarageModeChange(garageMode); |
| if (garageMode == GarageMode.GARAGE_MODE_ON) { |
| mWatchdogStorage.shrinkDatabase(); |
| } |
| notifyGarageModeChange(garageMode); |
| return; |
| case Intent.ACTION_USER_REMOVED: |
| UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); |
| mWatchdogPerfHandler.deleteUser(user.getIdentifier()); |
| return; |
| } |
| } |
| }; |
| |
| private final ICarPowerStateListener mCarPowerStateListener = |
| new ICarPowerStateListener.Stub() { |
| @Override |
| public void onStateChanged(int state) { |
| CarPowerManagementService powerService = |
| CarLocalServices.getService(CarPowerManagementService.class); |
| if (powerService == null) { |
| return; |
| } |
| int powerCycle = carPowerStateToPowerCycle(powerService.getPowerState()); |
| switch (powerCycle) { |
| case PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER: |
| mWatchdogPerfHandler.writeToDatabase(); |
| break; |
| // ON covers resume. |
| case PowerCycle.POWER_CYCLE_RESUME: |
| // There might be outdated & incorrect info. We should reset them before |
| // starting to do health check. |
| mWatchdogProcessHandler.prepareHealthCheck(); |
| break; |
| default: |
| return; |
| } |
| notifyPowerCycleChange(powerCycle); |
| } |
| }; |
| |
| private final ICarPowerPolicyListener mCarDisplayPowerPolicyListener = |
| new ICarPowerPolicyListener.Stub() { |
| @Override |
| public void onPolicyChanged(CarPowerPolicy appliedPolicy, |
| CarPowerPolicy accumulatedPolicy) { |
| boolean isDisplayEnabled = |
| appliedPolicy.isComponentEnabled(PowerComponent.DISPLAY); |
| boolean didStateChange = false; |
| synchronized (mLock) { |
| didStateChange = mIsDisplayEnabled != isDisplayEnabled; |
| mIsDisplayEnabled = isDisplayEnabled; |
| } |
| if (didStateChange) { |
| mWatchdogPerfHandler.onDisplayStateChanged(isDisplayEnabled); |
| } |
| } |
| }; |
| |
| private final Object mLock = new Object(); |
| @GuardedBy("mLock") |
| private boolean mReadyToRespond; |
| @GuardedBy("mLock") |
| private boolean mIsConnected; |
| @GuardedBy("mLock") |
| private @GarageMode int mCurrentGarageMode; |
| @GuardedBy("mLock") |
| private boolean mIsDisplayEnabled; |
| |
| public CarWatchdogService(Context context) { |
| this(context, new WatchdogStorage(context)); |
| } |
| |
| @VisibleForTesting |
| CarWatchdogService(Context context, WatchdogStorage watchdogStorage) { |
| mContext = context; |
| mWatchdogStorage = watchdogStorage; |
| mPackageInfoHandler = new PackageInfoHandler(mContext.getPackageManager()); |
| mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG); |
| mWatchdogServiceForSystem = new ICarWatchdogServiceForSystemImpl(this); |
| mWatchdogProcessHandler = new WatchdogProcessHandler(mWatchdogServiceForSystem, |
| mCarWatchdogDaemonHelper); |
| mWatchdogPerfHandler = new WatchdogPerfHandler(mContext, mCarWatchdogDaemonHelper, |
| mPackageInfoHandler, mWatchdogStorage); |
| mConnectionListener = (isConnected) -> { |
| mWatchdogPerfHandler.onDaemonConnectionChange(isConnected); |
| synchronized (mLock) { |
| mIsConnected = isConnected; |
| } |
| registerToDaemon(); |
| }; |
| mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF; |
| mIsDisplayEnabled = true; |
| } |
| |
| @Override |
| public void init() { |
| mWatchdogProcessHandler.init(); |
| mWatchdogPerfHandler.init(); |
| subscribePowerManagementService(); |
| subscribeUserStateChange(); |
| subscribeBroadcastReceiver(); |
| mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener); |
| mCarWatchdogDaemonHelper.connect(); |
| // To make sure the main handler is ready for responding to car watchdog daemon, registering |
| // to the daemon is done through the main handler. Once the registration is completed, we |
| // can assume that the main handler is not too busy handling other stuffs. |
| postRegisterToDaemonMessage(); |
| if (DEBUG) { |
| Slogf.d(TAG, "CarWatchdogService is initialized"); |
| } |
| } |
| |
| @Override |
| public void release() { |
| mContext.unregisterReceiver(mBroadcastReceiver); |
| unsubscribePowerManagementService(); |
| mWatchdogPerfHandler.release(); |
| mWatchdogStorage.release(); |
| unregisterFromDaemon(); |
| mCarWatchdogDaemonHelper.disconnect(); |
| } |
| |
| @Override |
| public void dump(IndentingPrintWriter writer) { |
| writer.println("*" + getClass().getSimpleName() + "*"); |
| writer.increaseIndent(); |
| synchronized (mLock) { |
| writer.println("Current garage mode: " + toGarageModeString(mCurrentGarageMode)); |
| } |
| mWatchdogProcessHandler.dump(writer); |
| mWatchdogPerfHandler.dump(writer); |
| writer.decreaseIndent(); |
| } |
| |
| /** |
| * Registers {@link android.car.watchdog.ICarWatchdogServiceCallback} to |
| * {@link CarWatchdogService}. |
| */ |
| @Override |
| public void registerClient(ICarWatchdogServiceCallback client, int timeout) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); |
| mWatchdogProcessHandler.registerClient(client, timeout); |
| } |
| |
| /** |
| * Unregisters {@link android.car.watchdog.ICarWatchdogServiceCallback} from |
| * {@link CarWatchdogService}. |
| */ |
| @Override |
| public void unregisterClient(ICarWatchdogServiceCallback client) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); |
| mWatchdogProcessHandler.unregisterClient(client); |
| } |
| |
| /** |
| * Tells {@link CarWatchdogService} that the client is alive. |
| */ |
| @Override |
| public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); |
| mWatchdogProcessHandler.tellClientAlive(client, sessionId); |
| } |
| |
| /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the calling package. */ |
| @Override |
| @NonNull |
| public ResourceOveruseStats getResourceOveruseStats( |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, |
| @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { |
| return mWatchdogPerfHandler.getResourceOveruseStats(resourceOveruseFlag, maxStatsPeriod); |
| } |
| |
| /** |
| * Returns {@link android.car.watchdog.ResourceOveruseStats} for all packages for the maximum |
| * specified period, and the specified resource types with stats greater than or equal to the |
| * minimum specified stats. |
| */ |
| @Override |
| @NonNull |
| public List<ResourceOveruseStats> getAllResourceOveruseStats( |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, |
| @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, |
| @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); |
| return mWatchdogPerfHandler.getAllResourceOveruseStats(resourceOveruseFlag, |
| minimumStatsFlag, maxStatsPeriod); |
| } |
| |
| /** Returns {@link android.car.watchdog.ResourceOveruseStats} for the specified user package. */ |
| @Override |
| @NonNull |
| public ResourceOveruseStats getResourceOveruseStatsForUserPackage( |
| @NonNull String packageName, @NonNull UserHandle userHandle, |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, |
| @CarWatchdogManager.StatsPeriod int maxStatsPeriod) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); |
| return mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(packageName, userHandle, |
| resourceOveruseFlag, maxStatsPeriod); |
| } |
| |
| /** |
| * Adds {@link android.car.watchdog.IResourceOveruseListener} for the calling package's resource |
| * overuse notifications. |
| */ |
| @Override |
| public void addResourceOveruseListener( |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, |
| @NonNull IResourceOveruseListener listener) { |
| mWatchdogPerfHandler.addResourceOveruseListener(resourceOveruseFlag, listener); |
| } |
| |
| /** |
| * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for the |
| * calling package's resource overuse notifications. |
| */ |
| @Override |
| public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) { |
| mWatchdogPerfHandler.removeResourceOveruseListener(listener); |
| } |
| |
| /** |
| * Adds {@link android.car.watchdog.IResourceOveruseListener} for all packages' resource overuse |
| * notifications. |
| */ |
| @Override |
| public void addResourceOveruseListenerForSystem( |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, |
| @NonNull IResourceOveruseListener listener) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); |
| mWatchdogPerfHandler.addResourceOveruseListenerForSystem(resourceOveruseFlag, listener); |
| } |
| |
| /** |
| * Removes the previously added {@link android.car.watchdog.IResourceOveruseListener} for all |
| * packages' resource overuse notifications. |
| */ |
| @Override |
| public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); |
| mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(listener); |
| } |
| |
| /** Sets whether or not a user package is killable on resource overuse. */ |
| @Override |
| public void setKillablePackageAsUser(String packageName, UserHandle userHandle, |
| boolean isKillable) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); |
| mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle, isKillable); |
| } |
| |
| /** |
| * Returns all {@link android.car.watchdog.PackageKillableState} on resource overuse for |
| * the specified user. |
| */ |
| @Override |
| @NonNull |
| public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); |
| return mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle); |
| } |
| |
| /** |
| * Sets {@link android.car.watchdog.ResourceOveruseConfiguration} for the specified resources. |
| */ |
| @Override |
| @CarWatchdogManager.ReturnCode |
| public int setResourceOveruseConfigurations( |
| List<ResourceOveruseConfiguration> configurations, |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) |
| throws RemoteException { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG); |
| return mWatchdogPerfHandler.setResourceOveruseConfigurations(configurations, |
| resourceOveruseFlag); |
| } |
| |
| /** Returns the available {@link android.car.watchdog.ResourceOveruseConfiguration}. */ |
| @Override |
| @NonNull |
| public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations( |
| @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) { |
| ICarImpl.assertAnyPermission(mContext, Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG, |
| Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS); |
| return mWatchdogPerfHandler.getResourceOveruseConfigurations(resourceOveruseFlag); |
| } |
| |
| /** |
| * Enables/disables the watchdog daemon client health check process. |
| */ |
| public void controlProcessHealthCheck(boolean disable) { |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_WATCHDOG); |
| mWatchdogProcessHandler.controlProcessHealthCheck(disable); |
| } |
| |
| @VisibleForTesting |
| int getClientCount(int timeout) { |
| return mWatchdogProcessHandler.getClientCount(timeout); |
| } |
| |
| @VisibleForTesting |
| void setTimeSource(TimeSourceInterface timeSource) { |
| mWatchdogPerfHandler.setTimeSource(timeSource); |
| } |
| |
| @VisibleForTesting |
| void setOveruseHandlingDelay(long millis) { |
| mWatchdogPerfHandler.setOveruseHandlingDelay(millis); |
| } |
| |
| @VisibleForTesting |
| void setRecurringOveruseThreshold(int threshold) { |
| mWatchdogPerfHandler.setRecurringOveruseThreshold(threshold); |
| } |
| |
| private void notifyAllUserStates() { |
| UserManager userManager = UserManager.get(mContext); |
| List<UserInfo> users = userManager.getUsers(); |
| try { |
| // TODO(b/152780162): reduce the number of RPC calls(isUserRunning). |
| for (int i = 0; i < users.size(); ++i) { |
| UserInfo info = users.get(i); |
| int userState = userManager.isUserRunning(info.id) |
| ? UserState.USER_STATE_STARTED |
| : UserState.USER_STATE_STOPPED; |
| mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, info.id, |
| userState); |
| mWatchdogProcessHandler.updateUserState(info.id, |
| userState == UserState.USER_STATE_STOPPED); |
| } |
| if (DEBUG) { |
| Slogf.d(TAG, "Notified car watchdog daemon of user states"); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, "Notifying latest user states failed: %s", e); |
| } |
| } |
| |
| private void notifyPowerCycleChange(@PowerCycle int powerCycle) { |
| if (powerCycle == PowerCycle.NUM_POWER_CYLES) { |
| Slogf.e(TAG, "Skipping notifying invalid power cycle (%d)", powerCycle); |
| return; |
| } |
| try { |
| mCarWatchdogDaemonHelper.notifySystemStateChange( |
| StateType.POWER_CYCLE, powerCycle, MISSING_ARG_VALUE); |
| if (DEBUG) { |
| Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle); |
| } |
| } |
| |
| private void notifyGarageModeChange(@GarageMode int garageMode) { |
| try { |
| mCarWatchdogDaemonHelper.notifySystemStateChange( |
| StateType.GARAGE_MODE, garageMode, MISSING_ARG_VALUE); |
| if (DEBUG) { |
| Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode); |
| } |
| } |
| |
| private void postRegisterToDaemonMessage() { |
| CarServiceUtils.runOnMain(() -> { |
| synchronized (mLock) { |
| mReadyToRespond = true; |
| } |
| registerToDaemon(); |
| }); |
| } |
| |
| private void registerToDaemon() { |
| synchronized (mLock) { |
| if (!mIsConnected || !mReadyToRespond) { |
| return; |
| } |
| } |
| try { |
| mCarWatchdogDaemonHelper.registerCarWatchdogService(mWatchdogServiceForSystem); |
| if (DEBUG) { |
| Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon"); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, "Cannot register to car watchdog daemon: %s", e); |
| } |
| notifyAllUserStates(); |
| CarPowerManagementService powerService = |
| CarLocalServices.getService(CarPowerManagementService.class); |
| if (powerService != null) { |
| int powerState = powerService.getPowerState(); |
| int powerCycle = carPowerStateToPowerCycle(powerState); |
| if (powerCycle != PowerCycle.NUM_POWER_CYLES) { |
| notifyPowerCycleChange(powerCycle); |
| } else { |
| Slogf.i(TAG, "Skipping notifying %d power state", powerState); |
| } |
| } |
| int garageMode; |
| synchronized (mLock) { |
| // To avoid race condition, fetch {@link mCurrentGarageMode} just before |
| // the {@link notifyGarageModeChange} call. For instance, if {@code mCurrentGarageMode} |
| // changes before the above {@link notifyPowerCycleChange} call returns, |
| // the {@link garageMode}'s value will be out of date. |
| garageMode = mCurrentGarageMode; |
| } |
| notifyGarageModeChange(garageMode); |
| } |
| |
| private void unregisterFromDaemon() { |
| try { |
| mCarWatchdogDaemonHelper.unregisterCarWatchdogService(mWatchdogServiceForSystem); |
| if (DEBUG) { |
| Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon"); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, "Cannot unregister from car watchdog daemon: %s", e); |
| } |
| } |
| |
| private void subscribePowerManagementService() { |
| CarPowerManagementService powerService = |
| CarLocalServices.getService(CarPowerManagementService.class); |
| if (powerService == null) { |
| Slogf.w(TAG, "Cannot get CarPowerManagementService"); |
| return; |
| } |
| powerService.registerListener(mCarPowerStateListener); |
| powerService.addPowerPolicyListener( |
| new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(), |
| mCarDisplayPowerPolicyListener); |
| } |
| |
| private void unsubscribePowerManagementService() { |
| CarPowerManagementService powerService = |
| CarLocalServices.getService(CarPowerManagementService.class); |
| if (powerService == null) { |
| Slogf.w(TAG, "Cannot get CarPowerManagementService"); |
| return; |
| } |
| powerService.unregisterListener(mCarPowerStateListener); |
| powerService.removePowerPolicyListener(mCarDisplayPowerPolicyListener); |
| } |
| |
| private void subscribeUserStateChange() { |
| CarUserService userService = CarLocalServices.getService(CarUserService.class); |
| if (userService == null) { |
| Slogf.w(TAG, "Cannot get CarUserService"); |
| return; |
| } |
| userService.addUserLifecycleListener((event) -> { |
| int userId = event.getUserHandle().getIdentifier(); |
| int userState; |
| String userStateDesc; |
| switch (event.getEventType()) { |
| case USER_LIFECYCLE_EVENT_TYPE_STARTING: |
| mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ false); |
| userState = UserState.USER_STATE_STARTED; |
| userStateDesc = "STARTING"; |
| break; |
| case USER_LIFECYCLE_EVENT_TYPE_STOPPED: |
| mWatchdogProcessHandler.updateUserState(userId, /*isStopped=*/ true); |
| userState = UserState.USER_STATE_STOPPED; |
| userStateDesc = "STOPPING"; |
| break; |
| default: |
| return; |
| } |
| try { |
| mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId, |
| userState); |
| if (DEBUG) { |
| Slogf.d(TAG, "Notified car watchdog daemon user %d's user state, %s", |
| userId, userStateDesc); |
| } |
| } catch (RemoteException | RuntimeException e) { |
| Slogf.w(TAG, "Notifying user state change failed: %s", e); |
| } |
| }); |
| } |
| |
| private void subscribeBroadcastReceiver() { |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(ACTION_GARAGE_MODE_ON); |
| filter.addAction(ACTION_GARAGE_MODE_OFF); |
| filter.addAction(Intent.ACTION_USER_REMOVED); |
| |
| mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter, null, null); |
| } |
| |
| private static @PowerCycle int carPowerStateToPowerCycle(int powerState) { |
| switch (powerState) { |
| // SHUTDOWN_PREPARE covers suspend and shutdown. |
| case CarPowerStateListener.SHUTDOWN_PREPARE: |
| return PowerCycle.POWER_CYCLE_SHUTDOWN_PREPARE; |
| case CarPowerStateListener.SHUTDOWN_ENTER: |
| case CarPowerStateListener.SUSPEND_ENTER: |
| return PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER; |
| // ON covers resume. |
| case CarPowerStateListener.ON: |
| return PowerCycle.POWER_CYCLE_RESUME; |
| } |
| return PowerCycle.NUM_POWER_CYLES; |
| } |
| |
| private static String toGarageModeString(@GarageMode int garageMode) { |
| switch (garageMode) { |
| case GarageMode.GARAGE_MODE_OFF: |
| return "GARAGE_MODE_OFF"; |
| case GarageMode.GARAGE_MODE_ON: |
| return "GARAGE_MODE_ON"; |
| } |
| return "INVALID"; |
| } |
| |
| private static final class ICarWatchdogServiceForSystemImpl |
| extends ICarWatchdogServiceForSystem.Stub { |
| private final WeakReference<CarWatchdogService> mService; |
| |
| ICarWatchdogServiceForSystemImpl(CarWatchdogService service) { |
| mService = new WeakReference<>(service); |
| } |
| |
| @Override |
| public void checkIfAlive(int sessionId, int timeout) { |
| CarWatchdogService service = mService.get(); |
| if (service == null) { |
| Slogf.w(TAG, "CarWatchdogService is not available"); |
| return; |
| } |
| service.mWatchdogProcessHandler.postHealthCheckMessage(sessionId); |
| } |
| |
| @Override |
| public void prepareProcessTermination() { |
| Slogf.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon"); |
| } |
| |
| @Override |
| public List<PackageInfo> getPackageInfosForUids( |
| int[] uids, List<String> vendorPackagePrefixes) { |
| if (ArrayUtils.isEmpty(uids)) { |
| Slogf.w(TAG, "UID list is empty"); |
| return null; |
| } |
| CarWatchdogService service = mService.get(); |
| if (service == null) { |
| Slogf.w(TAG, "CarWatchdogService is not available"); |
| return null; |
| } |
| return service.mPackageInfoHandler.getPackageInfosForUids(uids, vendorPackagePrefixes); |
| } |
| |
| @Override |
| public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) { |
| if (packageIoOveruseStats.isEmpty()) { |
| Slogf.w(TAG, "Latest I/O overuse stats is empty"); |
| return; |
| } |
| CarWatchdogService service = mService.get(); |
| if (service == null) { |
| Slogf.w(TAG, "CarWatchdogService is not available"); |
| return; |
| } |
| service.mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats); |
| } |
| |
| @Override |
| public void resetResourceOveruseStats(List<String> packageNames) { |
| if (packageNames.isEmpty()) { |
| Slogf.w(TAG, "Provided an empty package name to reset resource overuse stats"); |
| return; |
| } |
| CarWatchdogService service = mService.get(); |
| if (service == null) { |
| Slogf.w(TAG, "CarWatchdogService is not available"); |
| return; |
| } |
| service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames)); |
| } |
| |
| @Override |
| public List<UserPackageIoUsageStats> getTodayIoUsageStats() { |
| CarWatchdogService service = mService.get(); |
| if (service == null) { |
| Slogf.w(TAG, "CarWatchdogService is not available"); |
| return null; |
| } |
| return service.mWatchdogPerfHandler.getTodayIoUsageStats(); |
| } |
| } |
| } |