/* * Copyright (C) 2015 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.power; import static android.car.hardware.power.CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE; import static android.car.hardware.power.CarPowerManager.STATE_SHUTDOWN_PREPARE; import static android.car.hardware.power.PowerComponentUtil.FIRST_POWER_COMPONENT; import static android.car.hardware.power.PowerComponentUtil.LAST_POWER_COMPONENT; import static android.net.ConnectivityManager.TETHERING_WIFI; import static com.android.car.hal.PowerHalService.BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE; import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.automotive.powerpolicy.internal.ICarPowerPolicyDelegate; import android.automotive.powerpolicy.internal.ICarPowerPolicyDelegateCallback; import android.automotive.powerpolicy.internal.PowerPolicyFailureReason; import android.automotive.powerpolicy.internal.PowerPolicyInitData; import android.car.Car; import android.car.CarOccupantZoneManager; import android.car.ICarResultReceiver; import android.car.builtin.app.ActivityManagerHelper; import android.car.builtin.content.pm.PackageManagerHelper; import android.car.builtin.os.BuildHelper; import android.car.builtin.os.HandlerHelper; import android.car.builtin.os.ServiceManagerHelper; import android.car.builtin.os.UserManagerHelper; import android.car.builtin.util.EventLogHelper; import android.car.builtin.util.Slogf; import android.car.feature.FeatureFlags; import android.car.feature.FeatureFlagsImpl; import android.car.hardware.power.CarPowerManager; import android.car.hardware.power.CarPowerPolicy; import android.car.hardware.power.CarPowerPolicyFilter; import android.car.hardware.power.ICarPower; import android.car.hardware.power.ICarPowerPolicyListener; import android.car.hardware.power.ICarPowerStateListener; import android.car.hardware.power.PowerComponentUtil; import android.car.remoteaccess.CarRemoteAccessManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.frameworks.automotive.powerpolicy.internal.ICarPowerPolicySystemNotification; import android.frameworks.automotive.powerpolicy.internal.PolicyState; import android.hardware.automotive.vehicle.VehicleApPowerStateReport; import android.hardware.automotive.vehicle.VehicleApPowerStateReq; import android.hardware.automotive.vehicle.VehicleApPowerStateShutdownParam; import android.hardware.display.DisplayManager; import android.net.TetheringManager; import android.net.TetheringManager.TetheringRequest; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IInterface; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; import android.util.AtomicFile; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.car.CarLocalServices; import com.android.car.CarLog; import com.android.car.CarOccupantZoneService; import com.android.car.CarServiceBase; import com.android.car.CarServiceUtils; import com.android.car.CarStatsLogHelper; import com.android.car.ICarImpl; import com.android.car.R; import com.android.car.hal.PowerHalService; import com.android.car.hal.PowerHalService.BootupReason; import com.android.car.hal.PowerHalService.PowerState; import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; import com.android.car.internal.util.DebugUtils; import com.android.car.internal.util.IndentingPrintWriter; import com.android.car.internal.util.Lists; import com.android.car.power.CarPowerDumpProto.CpmsStateProto; import com.android.car.systeminterface.SystemInterface; import com.android.car.user.CarUserNoticeService; import com.android.car.user.CarUserService; import com.android.car.user.UserHandleHelper; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Consumer; /** * Power Management service class for cars. Controls the power states and interacts with other * parts of the system to ensure its own state. */ public class CarPowerManagementService extends ICarPower.Stub implements CarServiceBase, PowerHalService.PowerEventListener { public static final String SILENT_MODE_FORCED_SILENT = SilentModeHandler.SILENT_MODE_FORCED_SILENT; public static final String SILENT_MODE_FORCED_NON_SILENT = SilentModeHandler.SILENT_MODE_FORCED_NON_SILENT; public static final String SILENT_MODE_NON_FORCED = SilentModeHandler.SILENT_MODE_NON_FORCED; public static final long INVALID_TIMEOUT = -1L; public static final int NO_WAKEUP_BY_TIMER = -1; static final String TAG = CarLog.tagFor(CarPowerManagementService.class); private static final String WIFI_STATE_FILENAME = "wifi_state"; private static final String TETHERING_STATE_FILENAME = "tethering_state"; private static final String COMPONENT_STATE_MODIFIED = "forcibly_disabled"; private static final String COMPONENT_STATE_ORIGINAL = "original"; // If Suspend to RAM fails, we retry with an exponential back-off: // The wait interval will be 10 msec, 20 msec, 40 msec, ... // Once the wait interval goes beyond 100 msec, it is fixed at 100 msec. private static final long INITIAL_SUSPEND_RETRY_INTERVAL_MS = 10; private static final long MAX_RETRY_INTERVAL_MS = 100; // Minimum and maximum wait duration before the system goes into Suspend to RAM. private static final long MIN_SUSPEND_WAIT_DURATION_MS = 0; private static final long MAX_SUSPEND_WAIT_DURATION_MS = 3 * 60 * 1000; private static final long CAR_POWER_POLICY_DAEMON_FIND_MARGINAL_TIME_MS = 300; private static final long CAR_POWER_POLICY_DAEMON_BIND_RETRY_INTERVAL_MS = 500; private static final int CAR_POWER_POLICY_DAEMON_BIND_MAX_RETRY = 3; private static final long CAR_POWER_POLICY_DEFINITION_TIMEOUT_MS = 500; // TODO(b/286303350): remove once power policy refactor complete, replace w/refactored version private static final String CAR_POWER_POLICY_DAEMON_INTERFACE = "android.frameworks.automotive.powerpolicy.internal.ICarPowerPolicySystemNotification/" + "default"; private static final String REFACTORED_CAR_POWER_POLICY_DAEMON_INTERFACE = "android.automotive.powerpolicy.internal.ICarPowerPolicyDelegate/default"; // TODO: Make this OEM configurable. private static final int SHUTDOWN_POLLING_INTERVAL_MS = 2000; private static final int SHUTDOWN_EXTEND_MAX_MS = 5000; // maxGarageModeRunningDurationInSecs should be equal or greater than this. 15 min for now. private static final int MIN_MAX_GARAGE_MODE_DURATION_MS = 15 * 60 * 1000; // in secs private static final String PROP_MAX_GARAGE_MODE_DURATION_OVERRIDE = "android.car.garagemodeduration"; // Constants for action on finish private static final int ACTION_ON_FINISH_SHUTDOWN = 0; private static final int ACTION_ON_FINISH_DEEP_SLEEP = 1; private static final int ACTION_ON_FINISH_HIBERNATION = 2; // Default timeout for listener completion during shutdown. private static final int DEFAULT_COMPLETION_WAIT_TIMEOUT = 5_000; // Default timeout for power policy change requests private static final int DEFAULT_POWER_POLICY_REQUEST_TIMEOUT_MS = 5_000; private static final int INDEX_WAIT_FOR_VHAL = 0; private static final int INDEX_ON = 1; private final Object mLock = new Object(); private final Object mSimulationWaitObject = new Object(); private final Context mContext; private final PowerHalService mHal; private final SystemInterface mSystemInterface; private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread( getClass().getSimpleName()); private final PowerHandler mHandler = new PowerHandler(mHandlerThread.getLooper(), this); private final HandlerThread mBroadcastHandlerThread = CarServiceUtils.getHandlerThread( getClass().getSimpleName() + " broadcasts"); private final Handler mBroadcastHandler = new Handler(mBroadcastHandlerThread.getLooper()); // The listeners that complete simply by returning from onStateChanged() private final PowerManagerCallbackList mPowerManagerListeners = new PowerManagerCallbackList<>( l -> CarPowerManagementService.this.doUnregisterListener(l)); // The listeners that must indicate asynchronous completion by calling finished(). private final PowerManagerCallbackList mPowerManagerListenersWithCompletion = new PowerManagerCallbackList<>( l -> CarPowerManagementService.this.doUnregisterListener(l)); private final AtomicInteger mPolicyRequestIdCounter = new AtomicInteger(0); // The internal listeners that must indicates asynchronous completion by calling // completeStateChangeHandling(). Note that they are not binder objects. @GuardedBy("mLock") private final ArrayList mInternalPowerListeners = new ArrayList<>(); @GuardedBy("mLock") private final ArraySet mListenersWeAreWaitingFor = new ArraySet<>(); @GuardedBy("mLock") private final LinkedList mPendingPowerStates = new LinkedList<>(); @GuardedBy("mLock") private final SparseArray mRequestIdToPolicyRequest = new SparseArray<>(); private final UserManager mUserManager; private final CarUserService mUserService; private final WifiManager mWifiManager; private final TetheringManager mTetheringManager; private final AtomicFile mWifiStateFile; private final AtomicFile mTetheringStateFile; private final boolean mWifiAdjustmentForSuspend; // This is a temp work-around to reduce user switching delay after wake-up. private final boolean mSwitchGuestUserBeforeSleep; // CPMS tries to enter Suspend to RAM within the duration specified at // mMaxSuspendWaitDurationMs. The default max duration is MAX_SUSPEND_WAIT_DRATION, and can be // overridden by setting config_maxSuspendWaitDuration in an overrlay resource. // The valid range is MIN_SUSPEND_WAIT_DRATION to MAX_SUSPEND_WAIT_DURATION. private final long mMaxSuspendWaitDurationMs; @GuardedBy("mSimulationWaitObject") private boolean mWakeFromSimulatedSleep; @GuardedBy("mSimulationWaitObject") private boolean mInSimulatedDeepSleepMode; @GuardedBy("mSimulationWaitObject") private int mResumeDelayFromSimulatedSuspendSec = NO_WAKEUP_BY_TIMER; @GuardedBy("mSimulationWaitObject") private int mCancelDelayFromSimulatedSuspendSec = NO_WAKEUP_BY_TIMER; @GuardedBy("mSimulationWaitObject") private boolean mFreeMemoryBeforeSuspend; @GuardedBy("mLock") private CpmsState mCurrentState; @GuardedBy("mLock") private long mShutdownStartTime; @GuardedBy("mLock") private long mLastSleepEntryTime; @GuardedBy("mLock") private int mNextWakeupSec; @GuardedBy("mLock") private int mActionOnFinish; @GuardedBy("mLock") private boolean mShutdownOnNextSuspend; @GuardedBy("mLock") private boolean mShouldResumeUserService; @GuardedBy("mLock") private int mShutdownPrepareTimeMs = MIN_MAX_GARAGE_MODE_DURATION_MS; @GuardedBy("mLock") private int mShutdownPollingIntervalMs = SHUTDOWN_POLLING_INTERVAL_MS; @GuardedBy("mLock") private boolean mRebootAfterGarageMode; @GuardedBy("mLock") private boolean mGarageModeShouldExitImmediately; // TODO(b/286303350): remove once power policy refactor complete @GuardedBy("mLock") private ICarPowerPolicySystemNotification mCarPowerPolicyDaemon; // TODO(b/286303350): rename "mCarPowerPolicyDaemon" once power policy refactor complete @GuardedBy("mLock") private ICarPowerPolicyDelegate mRefactoredCarPowerPolicyDaemon; @GuardedBy("mLock") private boolean mConnectionInProgress; // After ICarPowerPolicyDelegateCallback is set, mReadyForCallback is set to true; private AtomicBoolean mReadyForCallback = new AtomicBoolean(false); private BinderHandler mBinderHandler; private boolean mPowerPoliciesInitialized; // TODO(b/286303350): remove after policy refactor, since daemon will be source of truth @GuardedBy("mLock") private String mCurrentPowerPolicyId; // TODO(b/286303350): remove after policy refactor, since daemon will control power policy @GuardedBy("mLock") private String mPendingPowerPolicyId; @GuardedBy("mLock") private String mCurrentPowerPolicyGroupId; @GuardedBy("mLock") private boolean mIsPowerPolicyLocked; // TODO(b/286303350): remove after policy refactor, since daemon will control power policy @GuardedBy("mLock") private boolean mHasControlOverDaemon; private final CountDownLatch mPowerPolicyInitializationLatch = new CountDownLatch(1); @GuardedBy("mLock") private CarPowerPolicy mCurrentAccumulatedPowerPolicy = getInitialAccumulatedPowerPolicy(); private AtomicBoolean mIsListenerWaitingCancelled = new AtomicBoolean(false); private final Semaphore mListenerCompletionSem = new Semaphore(/* permits= */ 0); @GuardedBy("mLock") @CarPowerManager.CarPowerState private int mStateForCompletion = CarPowerManager.STATE_INVALID; @GuardedBy("mLock") @CarRemoteAccessManager.NextPowerState private int mLastShutdownState = CarRemoteAccessManager.NEXT_POWER_STATE_OFF; @GuardedBy("mLock") @Nullable private ICarResultReceiver mFactoryResetCallback; @GuardedBy("mLock") private boolean mIsEmergencyShutdown; private final PowerManagerCallbackList mPowerPolicyListeners = new PowerManagerCallbackList<>( l -> CarPowerManagementService.this.mPowerPolicyListeners.unregister(l)); private final PowerComponentHandler mPowerComponentHandler; private final PolicyReader mPolicyReader = new PolicyReader(); private final SilentModeHandler mSilentModeHandler; private final ScreenOffHandler mScreenOffHandler; // Allows for injecting feature flag values during testing private FeatureFlags mFeatureFlags = new FeatureFlagsImpl(); @GuardedBy("mSimulationWaitObject") private boolean mBlockFromSimulatedCancelEvent; @VisibleForTesting void readPowerPolicyFromXml(InputStream inputStream) throws IOException, PolicyReader.PolicyXmlException, XmlPullParserException { mPolicyReader.readPowerPolicyFromXml(inputStream); Integer[] customComponents = new Integer[mPolicyReader.getCustomComponents().values().size()]; mPolicyReader.getCustomComponents().values().toArray(customComponents); mPowerComponentHandler.registerCustomComponents(customComponents); } interface ActionOnDeath { void take(T listener); } private static final class PowerManagerCallbackList extends RemoteCallbackList { private ActionOnDeath mActionOnDeath; PowerManagerCallbackList(ActionOnDeath action) { mActionOnDeath = action; } /** * Old version of {@link #onCallbackDied(E, Object)} that * does not provide a cookie. */ @Override public void onCallbackDied(T listener) { Slogf.i(TAG, "binderDied %s", listener.asBinder()); mActionOnDeath.take(listener); } } /** * Builder for {@link android.car.power.CarPowerManagementService}. */ public static final class Builder { private Context mContext; private PowerHalService mPowerHalService; private SystemInterface mSystemInterface; private UserManager mUserManager; private CarUserService mCarUserService; private PowerComponentHandler mPowerComponentHandler; private @Nullable IInterface mPowerPolicyDaemon; private @Nullable FeatureFlags mFeatureFlags; private @Nullable ScreenOffHandler mScreenOffHandler; private @Nullable String mSilentModeHwStatePath; private @Nullable String mSilentModeKernelStatePath; private @Nullable String mBootReason; private Resources mResources; private boolean mBuilt; /** * Sets the {@link Context}. */ public Builder setContext(Context context) { mContext = context; return this; } /** * Sets the {@link PowerHalService}. */ public Builder setPowerHalService(PowerHalService powerHalService) { mPowerHalService = powerHalService; return this; } /** * Sets the {@link SystemInterface}. */ public Builder setSystemInterface(SystemInterface systemInterface) { mSystemInterface = systemInterface; return this; } /** * Sets the {@link CarUserService}. */ public Builder setCarUserService(CarUserService carUserService) { mCarUserService = carUserService; return this; } /** * Sets the {@link IInterface} for power policy daemon. */ public Builder setPowerPolicyDaemon(@Nullable IInterface powerPolicyDaemon) { mPowerPolicyDaemon = powerPolicyDaemon; return this; } /** * Builds the object. */ public CarPowerManagementService build() { if (mBuilt) { throw new IllegalStateException("Only allowed to be built once"); } mBuilt = true; return new CarPowerManagementService(this); } /** * Sets the {@link PowerComponentHandler}. */ @VisibleForTesting public Builder setPowerComponentHandler(PowerComponentHandler powerComponentHandler) { mPowerComponentHandler = powerComponentHandler; return this; } /** * Sets the {@link UserManager}. */ @VisibleForTesting public Builder setUserManager(UserManager userManager) { mUserManager = userManager; return this; } /** * Sets the {@link FeatureFlags}. */ @VisibleForTesting public Builder setFeatureFlags(FeatureFlags featureFlags) { mFeatureFlags = featureFlags; return this; } /** * Sets the {@link ScreenOffHandler}. */ @VisibleForTesting public Builder setScreenOffHandler(ScreenOffHandler screenOffHandler) { mScreenOffHandler = screenOffHandler; return this; } /** * Sets the silent mode hardware state path. */ @VisibleForTesting public Builder setSilentModeHwStatePath(String silentModeHwStatePath) { mSilentModeHwStatePath = silentModeHwStatePath; return this; } /** * Sets the silent mode kernel state path. */ @VisibleForTesting public Builder setSilentModeKernelStatePath(String silentModeKernelStatePath) { mSilentModeKernelStatePath = silentModeKernelStatePath; return this; } /** * Sets the boot reason. */ @VisibleForTesting public Builder setBootReason(String bootReason) { mBootReason = bootReason; return this; } /** * Sets the {@link Resources}. */ @VisibleForTesting public Builder setResources(Resources resources) { mResources = resources; return this; } } public CarPowerManagementService(Context context, PowerHalService powerHalService, SystemInterface systemInterface, CarUserService carUserService, IInterface powerPolicyDaemon) { this(new Builder().setContext(context).setPowerHalService(powerHalService) .setSystemInterface(systemInterface).setCarUserService(carUserService) .setPowerPolicyDaemon(powerPolicyDaemon)); } private CarPowerManagementService(Builder builder) { mContext = Objects.requireNonNull(builder.mContext); mHal = Objects.requireNonNull(builder.mPowerHalService); mSystemInterface = Objects.requireNonNull(builder.mSystemInterface); mUserManager = Objects.requireNonNullElseGet(builder.mUserManager, () -> builder.mContext.getSystemService(UserManager.class)); Resources resources = Objects.requireNonNullElseGet(builder.mResources, () -> builder.mContext.getResources()); mShutdownPrepareTimeMs = resources.getInteger( R.integer.maxGarageModeRunningDurationInSecs) * 1000; mSwitchGuestUserBeforeSleep = resources.getBoolean( R.bool.config_switchGuestUserBeforeGoingSleep); if (mShutdownPrepareTimeMs < MIN_MAX_GARAGE_MODE_DURATION_MS) { Slogf.w(TAG, "maxGarageModeRunningDurationInSecs smaller than minimum required, " + "resource:%d(ms) while should exceed:%d(ms), Ignore resource.", mShutdownPrepareTimeMs, MIN_MAX_GARAGE_MODE_DURATION_MS); mShutdownPrepareTimeMs = MIN_MAX_GARAGE_MODE_DURATION_MS; } mUserService = Objects.requireNonNull(builder.mCarUserService); if (builder.mFeatureFlags != null) { mFeatureFlags = builder.mFeatureFlags; } // In a real situation, this should be null. IInterface powerPolicyDaemon = builder.mPowerPolicyDaemon; if (mFeatureFlags.carPowerPolicyRefactoring()) { mRefactoredCarPowerPolicyDaemon = (ICarPowerPolicyDelegate) powerPolicyDaemon; mPowerPoliciesInitialized = false; } else { mCarPowerPolicyDaemon = (ICarPowerPolicySystemNotification) powerPolicyDaemon; if (powerPolicyDaemon != null) { // For testing purpose mHasControlOverDaemon = true; } } mWifiManager = mContext.getSystemService(WifiManager.class); mTetheringManager = mContext.getSystemService(TetheringManager.class); mWifiStateFile = new AtomicFile( new File(mSystemInterface.getSystemCarDir(), WIFI_STATE_FILENAME)); mTetheringStateFile = new AtomicFile( new File(mSystemInterface.getSystemCarDir(), TETHERING_STATE_FILENAME)); mWifiAdjustmentForSuspend = isWifiAdjustmentForSuspendConfig(); mPowerComponentHandler = Objects.requireNonNullElseGet(builder.mPowerComponentHandler, () -> new PowerComponentHandler(mContext, mSystemInterface)); mSilentModeHandler = new SilentModeHandler(this, mFeatureFlags, builder.mSilentModeHwStatePath, builder.mSilentModeKernelStatePath, builder.mBootReason); mMaxSuspendWaitDurationMs = Math.max(MIN_SUSPEND_WAIT_DURATION_MS, Math.min(getMaxSuspendWaitDurationConfig(), MAX_SUSPEND_WAIT_DURATION_MS)); mScreenOffHandler = Objects.requireNonNullElseGet(builder.mScreenOffHandler, () -> new ScreenOffHandler(mContext, mSystemInterface, mHandler.getLooper())); } /** * Overrides timers to keep testing time short. * *

Passing in {@code 0} resets the value to the default. */ @VisibleForTesting public void setShutdownTimersForTest(int pollingIntervalMs, int shutdownTimeoutMs) { synchronized (mLock) { mShutdownPollingIntervalMs = (pollingIntervalMs == 0) ? SHUTDOWN_POLLING_INTERVAL_MS : pollingIntervalMs; mShutdownPrepareTimeMs = (shutdownTimeoutMs == 0) ? SHUTDOWN_EXTEND_MAX_MS : shutdownTimeoutMs; } } @VisibleForTesting protected HandlerThread getHandlerThread() { return mHandlerThread; } @Override public void init() { mPolicyReader.init(mFeatureFlags); mPowerComponentHandler.init(mPolicyReader.getCustomComponents()); mHal.setListener(this); mSystemInterface.init(this, mUserService); mScreenOffHandler.init(); if (mHal.isPowerStateSupported()) { // Initialize CPMS in WAIT_FOR_VHAL state onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, CarPowerManager.STATE_WAIT_FOR_VHAL); } else { Slogf.w(TAG, "Vehicle hal does not support power state yet."); onApPowerStateChange(CpmsState.ON, CarPowerManager.STATE_ON); } mSystemInterface.startDisplayStateMonitoring(); connectToPowerPolicyDaemon(); } @Override public void release() { if (mBinderHandler != null) { mBinderHandler.unlinkToDeath(); } mReadyForCallback.set(false); synchronized (mLock) { clearWaitingForCompletion(/*clearQueue=*/false); mCurrentState = null; if (mFeatureFlags.carPowerPolicyRefactoring()) { mRefactoredCarPowerPolicyDaemon = null; } else { mCarPowerPolicyDaemon = null; } mHandler.cancelAll(); mListenersWeAreWaitingFor.clear(); } mSystemInterface.stopDisplayStateMonitoring(); mPowerManagerListeners.kill(); mPowerPolicyListeners.kill(); forEachDisplay(mContext, mSystemInterface::releaseAllWakeLocks); } @Override @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) public void dump(IndentingPrintWriter writer) { synchronized (mLock) { writer.println("*CarPowerManagementService*"); writer.printf("mCurrentState: %s\n", mCurrentState); writer.printf("mShutdownStartTime: %d\n", mShutdownStartTime); writer.printf("mLastSleepEntryTime: %d\n", mLastSleepEntryTime); writer.printf("mNextWakeupSec: %d\n", mNextWakeupSec); writer.printf("mShutdownOnNextSuspend: %b\n", mShutdownOnNextSuspend); writer.printf("mActionOnFinish: %s\n", actionOnFinishToString(mActionOnFinish)); writer.printf("mShutdownPollingIntervalMs: %d\n", mShutdownPollingIntervalMs); writer.printf("mShutdownPrepareTimeMs: %d\n", mShutdownPrepareTimeMs); writer.printf("mRebootAfterGarageMode: %b\n", mRebootAfterGarageMode); writer.printf("mSwitchGuestUserBeforeSleep: %b\n", mSwitchGuestUserBeforeSleep); writer.printf("mCurrentPowerPolicyId: %s\n", mCurrentPowerPolicyId); writer.printf("mPendingPowerPolicyId: %s\n", mPendingPowerPolicyId); writer.printf("mCurrentPowerPolicyGroupId: %s\n", mCurrentPowerPolicyGroupId); writer.printf("mIsPowerPolicyLocked: %b\n", mIsPowerPolicyLocked); writer.printf("mMaxSuspendWaitDurationMs: %d\n", mMaxSuspendWaitDurationMs); writer.printf("config_maxSuspendWaitDuration: %d\n", getMaxSuspendWaitDurationConfig()); writer.printf("mWifiStateFile: %s\n", mWifiStateFile); writer.printf("mTetheringStateFile: %s\n", mTetheringStateFile); writer.printf("mWifiAdjustmentForSuspend: %b\n", mWifiAdjustmentForSuspend); writer.printf("# of power policy change listener: %d\n", mPowerPolicyListeners.getRegisteredCallbackCount()); writer.printf("mFactoryResetCallback: %s\n", mFactoryResetCallback); writer.printf("mIsListenerWaitingCancelled: %b\n", mIsListenerWaitingCancelled.get()); writer.printf("kernel support S2R: %b\n", mSystemInterface.isSystemSupportingDeepSleep()); writer.printf("kernel support S2D: %b\n", mSystemInterface.isSystemSupportingHibernation()); writer.printf("mLastShutdownState: %d\n", mLastShutdownState); writer.printf("mReadyForCallback: %b\n", mReadyForCallback.get()); } synchronized (mSimulationWaitObject) { writer.printf("mWakeFromSimulatedSleep: %b\n", mWakeFromSimulatedSleep); writer.printf("mInSimulatedDeepSleepMode: %b\n", mInSimulatedDeepSleepMode); writer.printf("mResumeDelayFromSimulatedSuspendSec: %d\n", mResumeDelayFromSimulatedSuspendSec); writer.printf("mFreeMemoryBeforeSuspend: %b\n", mFreeMemoryBeforeSuspend); } mPolicyReader.dump(writer); mPowerComponentHandler.dump(writer); mSilentModeHandler.dump(writer); mScreenOffHandler.dump(writer); } @Override @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) public void dumpProto(ProtoOutputStream proto) { synchronized (mLock) { long currentStateToken = proto.start(CarPowerDumpProto.CURRENT_STATE); proto.write(CpmsStateProto.CAN_POSTPONE, mCurrentState.mCanPostpone); proto.write(CpmsStateProto.CAR_POWER_MANAGER_STATE, mCurrentState.mCarPowerStateListenerState); proto.write(CpmsStateProto.SHUTDOWN_TYPE, mCurrentState.mShutdownType); proto.write(CpmsStateProto.STATE, mCurrentState.mState); proto.write(CpmsStateProto.STATE_NAME, mCurrentState.stateToString()); proto.end(currentStateToken); proto.write(CarPowerDumpProto.SHUTDOWN_START_TIME, mShutdownStartTime); proto.write(CarPowerDumpProto.LAST_SLEEP_ENTRY_TIME, mLastSleepEntryTime); proto.write(CarPowerDumpProto.NEXT_WAKEUP_SEC, mNextWakeupSec); proto.write(CarPowerDumpProto.SHUTDOWN_ON_NEXT_SUSPEND, mShutdownOnNextSuspend); proto.write( CarPowerDumpProto.ACTION_ON_FINISH, actionOnFinishToString(mActionOnFinish)); proto.write(CarPowerDumpProto.SHUTDOWN_POLLING_INTERVAL_MS, mShutdownPollingIntervalMs); proto.write(CarPowerDumpProto.SHUTDOWN_PREPARE_TIME_MS, mShutdownPrepareTimeMs); proto.write(CarPowerDumpProto.REBOOT_AFTER_GARAGE_MODE, mRebootAfterGarageMode); proto.write( CarPowerDumpProto.SWITCH_GUEST_USER_BEFORE_SLEEP, mSwitchGuestUserBeforeSleep); proto.write(CarPowerDumpProto.CURRENT_POWER_POLICY_ID, mCurrentPowerPolicyId); proto.write(CarPowerDumpProto.PENDING_POWER_POLICY_ID, mPendingPowerPolicyId); proto.write( CarPowerDumpProto.CURRENT_POWER_POLICY_GROUP_ID, mCurrentPowerPolicyGroupId); proto.write(CarPowerDumpProto.IS_POWER_POLICY_LOCKED, mIsPowerPolicyLocked); proto.write(CarPowerDumpProto.MAX_SUSPEND_WAIT_DURATION_MS, mMaxSuspendWaitDurationMs); proto.write(CarPowerDumpProto.MAX_SUSPEND_WAIT_DURATION_CONFIG, getMaxSuspendWaitDurationConfig()); proto.write(CarPowerDumpProto.WIFI_STATE_FILE, mWifiStateFile.toString()); proto.write(CarPowerDumpProto.TETHERING_STATE_FILE, mTetheringStateFile.toString()); proto.write(CarPowerDumpProto.WIFI_ADJUSTMENT_FOR_SUSPEND, mWifiAdjustmentForSuspend); proto.write(CarPowerDumpProto.POWER_POLICY_LISTENERS, mPowerPolicyListeners.getRegisteredCallbackCount()); proto.write(CarPowerDumpProto.FACTORY_RESET_CALLBACK, mFactoryResetCallback != null ? mFactoryResetCallback.toString() : ""); proto.write(CarPowerDumpProto.IS_LISTENER_WAITING_CANCELLED, mIsListenerWaitingCancelled.get()); proto.write(CarPowerDumpProto.KERNEL_SUPPORTS_DEEP_SLEEP, mSystemInterface.isSystemSupportingDeepSleep()); proto.write(CarPowerDumpProto.KERNEL_SUPPORTS_HIBERNATION, mSystemInterface.isSystemSupportingHibernation()); } synchronized (mSimulationWaitObject) { proto.write(CarPowerDumpProto.WAKE_FROM_SIMULATED_SLEEP, mWakeFromSimulatedSleep); proto.write(CarPowerDumpProto.IN_SIMULATED_DEEP_SLEEP_MODE, mInSimulatedDeepSleepMode); proto.write(CarPowerDumpProto.RESUME_DELAY_FROM_SIMULATED_SUSPEND_SEC, mResumeDelayFromSimulatedSuspendSec); proto.write(CarPowerDumpProto.FREE_MEMORY_BEFORE_SUSPEND, mFreeMemoryBeforeSuspend); } mPolicyReader.dumpProto(proto); mPowerComponentHandler.dumpProto(proto); mSilentModeHandler.dumpProto(proto); mScreenOffHandler.dumpProto(proto); } @Override public void onApPowerStateChange(PowerState state) { EventLogHelper.writeCarPowerManagerStateRequest(state.mState, state.mParam); synchronized (mLock) { mPendingPowerStates.addFirst(new CpmsState(state)); mLock.notifyAll(); } mHandler.handlePowerStateChange(); } @VisibleForTesting void setStateForWakeUp() { mSilentModeHandler.init(); synchronized (mLock) { mShouldResumeUserService = true; } handleWaitForVhal(new CpmsState(CpmsState.WAIT_FOR_VHAL, CarPowerManager.STATE_WAIT_FOR_VHAL, /* canPostpone= */ false)); Slogf.d(TAG, "setStateForTesting(): mShouldResumeUserService is set to false and power state " + "is switched " + "to Wait For Vhal"); } /** * Initiate state change from CPMS directly. */ private void onApPowerStateChange(int apState, @CarPowerManager.CarPowerState int carPowerStateListenerState) { CpmsState newState = new CpmsState(apState, carPowerStateListenerState, /* canPostpone= */ false); BiFunction eventFilter = null; // We are ready to shut down. Suppress this transition if // there is a request to cancel the shutdown (WAIT_FOR_VHAL). // Completely ignore this WAIT_FOR_FINISH if (newState.mState == CpmsState.WAIT_FOR_FINISH) { eventFilter = (stateToAdd, pendingSate) -> stateToAdd.mState == CpmsState.WAIT_FOR_FINISH && pendingSate.mState == CpmsState.WAIT_FOR_VHAL; } // Check if there is another pending SHUTDOWN_PREPARE. // This could happen, when another SHUTDOWN_PREPARE request is received from VHAL // while notifying PRE_SHUTDOWN_PREPARE. // If SHUTDOWN_PREPARE request already exist in the queue, and it skips Garage Mode, // then newState is ignored . if (newState.mState == CpmsState.SHUTDOWN_PREPARE) { eventFilter = (stateToAdd, pendingState) -> pendingState.mState == CpmsState.SHUTDOWN_PREPARE && !pendingState.mCanPostpone && pendingState.mCarPowerStateListenerState == STATE_PRE_SHUTDOWN_PREPARE; } synchronized (mLock) { // If eventFilter exists, lets check if event that satisfies filter is in queue. if (eventFilter != null) { for (int idx = 0; idx < mPendingPowerStates.size(); idx++) { CpmsState pendingState = mPendingPowerStates.get(idx); if (eventFilter.apply(newState, pendingState)) { return; } } } mPendingPowerStates.addFirst(newState); mLock.notifyAll(); } mHandler.handlePowerStateChange(); } private void doHandlePowerStateChange() { CpmsState newState; CpmsState prevState; synchronized (mLock) { prevState = mCurrentState; newState = mPendingPowerStates.pollFirst(); if (newState == null) { Slogf.w(TAG, "No more power state to process"); return; } Slogf.i(TAG, "doHandlePowerStateChange: newState=%s", newState.name()); if (!needPowerStateChangeLocked(newState)) { // We may need to process the pending power state request. if (!mPendingPowerStates.isEmpty()) { Slogf.i(TAG, "There is a pending power state change request. requesting the " + "processing..."); mHandler.handlePowerStateChange(); } return; } // now real power change happens. Whatever was queued before should be all cancelled. mPendingPowerStates.clear(); // Received updated SHUTDOWN_PREPARE there could be several reasons for that // 1. CPMS is in SHUTDOWN_PREPARE, and received state change to perform transition // from PRE_SHUTDOWN_PREPARE into SHUTDOWN_PREPARE // 2. New SHUTDOWN_PREPARE request is received, and it is different from existing one. if (newState.mState == CpmsState.SHUTDOWN_PREPARE && newState.mState == prevState.mState && newState.mCarPowerStateListenerState == STATE_PRE_SHUTDOWN_PREPARE) { // Nothing to do here, skipping clearing completion queue } else { clearWaitingForCompletion(/*clearQueue=*/false); } mCurrentState = newState; } mHandler.cancelProcessingComplete(); Slogf.i(TAG, "setCurrentState %s", newState); CarStatsLogHelper.logPowerState(newState.mState); EventLogHelper.writeCarPowerManagerStateChange(newState.mState); switch (newState.mState) { case CpmsState.WAIT_FOR_VHAL: handleWaitForVhal(newState); break; case CpmsState.ON: handleOn(); break; case CpmsState.SHUTDOWN_PREPARE: handleShutdownPrepare(newState, prevState); break; case CpmsState.SIMULATE_SLEEP: case CpmsState.SIMULATE_HIBERNATION: simulateShutdownPrepare(newState, prevState); break; case CpmsState.WAIT_FOR_FINISH: handleWaitForFinish(newState); break; case CpmsState.SUSPEND: // Received FINISH from VHAL handleFinish(); break; default: // Illegal state // TODO(b/202414427): Add handling of illegal state break; } } private void notifyPowerStateChangeToDaemon(@CarPowerManager.CarPowerState int powerState) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } if (daemon == null) { Slogf.e(TAG, "Failed to notify car power policy daemon of power state change, " + "daemon unavailable"); return; } notifyPowerStateChangeToDaemon(daemon, powerState); } private void notifyPowerStateChangeToDaemon(ICarPowerPolicyDelegate daemon, @CarPowerManager.CarPowerState int powerState) { Slogf.i(TAG, "Notifying CPPD of power state(%s)", powerStateToString(powerState)); String powerStateName = powerStateToString(powerState); if (!mReadyForCallback.get()) { Slogf.w(TAG, "Cannot notify power state(%S) of CPPD: not ready for calling to CPPD", powerStateName); return; } AsyncPolicyRequest request = generateAsyncPolicyRequest( DEFAULT_POWER_POLICY_REQUEST_TIMEOUT_MS); int requestId = request.getRequestId(); synchronized (mLock) { mRequestIdToPolicyRequest.put(requestId, request); } try { Slogf.i(TAG, "Request(%d) of applying power policy for power state(%s) to CPPD in " + "async", requestId, powerStateName); daemon.applyPowerPolicyPerPowerStateChangeAsync(requestId, (byte) powerState); boolean policyRequestServed = request.await(); if (!policyRequestServed) { Slogf.w(TAG, "Power policy request (ID: %d) for power state(%s) timed out after %d " + "ms", requestId, powerStateName, DEFAULT_POWER_POLICY_REQUEST_TIMEOUT_MS); return; } } catch (IllegalArgumentException e) { Slogf.w(TAG, e, "Failed to apply power policy for power state(%s)", powerStateName); return; } catch (InterruptedException e) { Slogf.w(TAG, e, "Wait for power policy change request for power state(%s) interrupted", powerStateName); Thread.currentThread().interrupt(); return; } catch (RemoteException e) { Slogf.w(TAG, e, "Failed to apply power policy for power state(%s), connection issue", powerStateName); return; } finally { synchronized (mLock) { mRequestIdToPolicyRequest.remove(requestId); } } if (!request.isSuccessful()) { Slogf.w(TAG, "Failed to apply power policy for power state(%s), failure reason = %d", powerStateName, request.getFailureReason()); return; } if (request.isDeferred()) { Slogf.i(TAG, "Applying power policy for power state(%s) is deferred", powerStateName); return; } CarPowerPolicy accumulatedPolicy = request.getAccumulatedPolicy(); updateCurrentPowerPolicy(accumulatedPolicy); notifyPowerPolicyChange(accumulatedPolicy); } private void handleWaitForVhal(CpmsState state) { @CarPowerManager.CarPowerState int carPowerStateListenerState = state.mCarPowerStateListenerState; // TODO(b/177478420): Restore Wifi, Audio, Location, and Bluetooth, if they are artificially // modified for S2R. mSilentModeHandler.querySilentModeHwState(); if (mFeatureFlags.carPowerPolicyRefactoring()) { notifyPowerStateChangeToDaemon(CarPowerManager.STATE_WAIT_FOR_VHAL); } else { applyDefaultPowerPolicyForState(CarPowerManager.STATE_WAIT_FOR_VHAL, PolicyReader.POWER_POLICY_ID_INITIAL_ON); if (!mSilentModeHandler.isSilentMode()) { cancelPreemptivePowerPolicy(); } } sendPowerManagerEvent(carPowerStateListenerState, INVALID_TIMEOUT); // Inspect CarPowerStateListenerState to decide which message to send via VHAL int lastShutdownState = CarRemoteAccessManager.NEXT_POWER_STATE_OFF; switch (carPowerStateListenerState) { case CarPowerManager.STATE_WAIT_FOR_VHAL: lastShutdownState = CarRemoteAccessManager.NEXT_POWER_STATE_OFF; mHal.sendWaitForVhal(); break; case CarPowerManager.STATE_SHUTDOWN_CANCELLED: synchronized (mLock) { mShutdownOnNextSuspend = false; // This cancels the "NextSuspend" mIsEmergencyShutdown = false; // TODO add cancel test } mHal.sendShutdownCancel(); Slogf.d(TAG, "reset mIsEmergencyShutdown"); break; case CarPowerManager.STATE_SUSPEND_EXIT: lastShutdownState = CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_RAM; mHal.sendSleepExit(); break; case CarPowerManager.STATE_HIBERNATION_EXIT: lastShutdownState = CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_DISK; mHal.sendHibernationExit(); break; default: Slogf.w(TAG, "Invalid action when handling wait for VHAL: %d", carPowerStateListenerState); break; } synchronized (mLock) { mLastShutdownState = lastShutdownState; } if (mWifiAdjustmentForSuspend) { restoreWifiFully(); } } private void updateCarUserNoticeServiceIfNecessary() { try { int currentUserId = ActivityManager.getCurrentUser(); UserHandleHelper userHandleHelper = new UserHandleHelper(mContext, mUserManager); UserHandle currentUser = userHandleHelper.getExistingUserHandle(currentUserId); CarUserNoticeService carUserNoticeService = CarLocalServices.getService(CarUserNoticeService.class); if (currentUser != null && userHandleHelper.isGuestUser(currentUser) && carUserNoticeService != null) { Slogf.i(TAG, "Car user notice service will ignore all messages before user " + "switch."); Intent intent = new Intent(); intent.setComponent(ComponentName.unflattenFromString( mContext.getResources().getString(R.string.continuousBlankActivity))); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(intent, UserHandle.CURRENT); carUserNoticeService.ignoreUserNotice(currentUserId); } } catch (Exception e) { Slogf.w(TAG, e, "Cannot ignore user notice for current user"); } } @VisibleForTesting void handleOn() { if (factoryResetIfNeeded()) return; // If current user is a Guest User, we want to inform CarUserNoticeService not to show // notice for current user, and show user notice only for the target user. if (!mSwitchGuestUserBeforeSleep) { updateCarUserNoticeServiceIfNecessary(); } if (mFeatureFlags.carPowerPolicyRefactoring()) { notifyPowerStateChangeToDaemon(CarPowerManager.STATE_ON); } else { if (!mSilentModeHandler.isSilentMode()) { cancelPreemptivePowerPolicy(); } applyDefaultPowerPolicyForState(VehicleApPowerStateReport.ON, PolicyReader.POWER_POLICY_ID_ALL_ON); } sendPowerManagerEvent(CarPowerManager.STATE_ON, INVALID_TIMEOUT); mHal.sendOn(); synchronized (mLock) { if (!mShouldResumeUserService) { Slogf.d(TAG, "handleOn(): called on boot"); return; } else { mShouldResumeUserService = false; } } try { mUserService.onResume(); } catch (Exception e) { Slogf.e(TAG, e, "Could not switch user on resume"); } } private boolean factoryResetIfNeeded() { ICarResultReceiver callback; synchronized (mLock) { if (mFactoryResetCallback == null) return false; callback = mFactoryResetCallback; } try { Slogf.i(TAG, "Factory resetting as it was delayed by user"); callback.send(/* resultCode= */ 0, /* resultData= */ null); return true; } catch (Exception e) { Slogf.wtf(TAG, e, "Should have factory reset, but failed"); return false; } } // TODO(b/286303350): remove once power policy refactor is complete, CPPD will handle applying // default power policies according to state private void applyDefaultPowerPolicyForState(@CarPowerManager.CarPowerState int state, @Nullable String fallbackPolicyId) { Slogf.i(TAG, "Applying the default power policy for %s", powerStateToString(state)); CarPowerPolicy policy; synchronized (mLock) { policy = mPolicyReader .getDefaultPowerPolicyForState(mCurrentPowerPolicyGroupId, state); } if (policy == null && fallbackPolicyId == null) { Slogf.w(TAG, "No default power policy for %s is found", powerStateToString(state)); return; } String policyId = policy == null ? fallbackPolicyId : policy.getPolicyId(); applyPowerPolicy(policyId, /* delayNotification= */ false, /* upToDaemon= */ true, /* force= */ false); } /** * Sets the callback used to factory reset the device on resume when the user delayed it. */ public void setFactoryResetCallback(ICarResultReceiver callback) { synchronized (mLock) { mFactoryResetCallback = callback; } } /** * Tells Garage Mode if it should run normally, or just * exit immediately without indicating 'idle' * @return True if no idle jobs should be run * @hide */ public boolean garageModeShouldExitImmediately() { synchronized (mLock) { return mGarageModeShouldExitImmediately; } } /** * This method is required for testing * Gets {@code PolicyReader} that reads {@code power_policy.xml}. * @return {@link PolicyReader} */ @VisibleForTesting public PolicyReader getPolicyReader() { return mPolicyReader; } private void handleShutdownPrepare(CpmsState currentState, CpmsState prevState) { switch (currentState.mCarPowerStateListenerState) { case CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE: updateShutdownPrepareStatus(currentState); if (prevState.mCarPowerStateListenerState == STATE_SHUTDOWN_PREPARE) { // Received request to update SHUTDOWN target currentState = new CpmsState(currentState.mState, prevState.mCarPowerStateListenerState, prevState.mCanPostpone, currentState.mShutdownType); synchronized (mLock) { mCurrentState = currentState; } clearWaitingForCompletion(/*clearQueue=*/true); } else if (prevState.mCarPowerStateListenerState == STATE_PRE_SHUTDOWN_PREPARE) { // Update of state occurred while in PRE_SHUTDOWN_PREPARE boolean areListenersEmpty; synchronized (mLock) { areListenersEmpty = mListenersWeAreWaitingFor.isEmpty(); } if (areListenersEmpty) { handleCoreShutdownPrepare(); } else { // PRE_SHUTDOWN_PREPARE is still being processed, no actions required return; } } else { handlePreShutdownPrepare(); } break; case CarPowerManager.STATE_SHUTDOWN_PREPARE: handleCoreShutdownPrepare(); break; default: Slogf.w(TAG, "Not supported listener state(%d)", currentState.mCarPowerStateListenerState); } } private void updateShutdownPrepareStatus(CpmsState newState) { // Shutdown on finish if the system doesn't support deep sleep/hibernation // or doesn't allow it. synchronized (mLock) { if (mShutdownOnNextSuspend || ( newState.mShutdownType == PowerState.SHUTDOWN_TYPE_POWER_OFF || newState.mShutdownType == PowerState.SHUTDOWN_TYPE_EMERGENCY)) { mActionOnFinish = ACTION_ON_FINISH_SHUTDOWN; } else if (newState.mShutdownType == PowerState.SHUTDOWN_TYPE_DEEP_SLEEP) { boolean isDeepSleepOnFinish = isDeepSleepAvailable() || newState.mState == CpmsState.SIMULATE_SLEEP; mActionOnFinish = isDeepSleepOnFinish ? ACTION_ON_FINISH_DEEP_SLEEP : ACTION_ON_FINISH_SHUTDOWN; } else if (newState.mShutdownType == PowerState.SHUTDOWN_TYPE_HIBERNATION) { boolean isHibernationOnFinish = isHibernationAvailable() || newState.mState == CpmsState.SIMULATE_HIBERNATION; mActionOnFinish = isHibernationOnFinish ? ACTION_ON_FINISH_HIBERNATION : ACTION_ON_FINISH_SHUTDOWN; } else { Slogf.wtf(TAG, "handleShutdownPrepare - incorrect state " + newState); } mGarageModeShouldExitImmediately = !newState.mCanPostpone; if (!mIsEmergencyShutdown) { mIsEmergencyShutdown = newState.mShutdownType == PowerState.SHUTDOWN_TYPE_EMERGENCY; Slogf.d(TAG, "set mIsEmergencyShutdown to " + mIsEmergencyShutdown); } else { // Emergency shutdown can be cancelled only via SHUTDOWN_CANCEL request"); Slogf.d(TAG, "mIsEmergencyShutdown is already set"); } } } private void handlePreShutdownPrepare() { int intervalMs; long timeoutMs; synchronized (mLock) { intervalMs = mShutdownPollingIntervalMs; Slogf.i(TAG, mGarageModeShouldExitImmediately ? "starting shutdown prepare without Garage Mode" : "starting shutdown prepare with Garage Mode"); // in case of emergency shutdown CPMS will not wait for timeoutMs = mIsEmergencyShutdown ? 0 : getPreShutdownPrepareTimeoutConfig(); } int state = CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE; sendPowerManagerEvent(state, timeoutMs); Runnable taskAtCompletion = () -> { // The next power state is still SHUTDOWN_PREPARE, and the listener state is // SHUTDOW_PREPARE. Slogf.i(TAG, "All listeners completed for %s", powerStateToString(state)); onApPowerStateChange(CpmsState.SHUTDOWN_PREPARE, CarPowerManager.STATE_SHUTDOWN_PREPARE); }; waitForCompletionWithShutdownPostpone(state, timeoutMs, taskAtCompletion, intervalMs); } private void handleCoreShutdownPrepare() { Slogf.i(TAG, "Handling core part of shutdown prepare"); doShutdownPrepare(); } // Simulates system shutdown to suspend private void simulateShutdownPrepare(CpmsState newState, CpmsState oldState) { Slogf.i(TAG, "Simulating shutdown prepare"); handleShutdownPrepare(newState, oldState); } private void doShutdownPrepare() { long timeoutMs; long intervalMs; boolean isEmergencyShutdown; synchronized (mLock) { timeoutMs = mShutdownPrepareTimeMs; intervalMs = mShutdownPollingIntervalMs; mShutdownStartTime = SystemClock.elapsedRealtime(); isEmergencyShutdown = mIsEmergencyShutdown; } if (isEmergencyShutdown) { timeoutMs = 0; // do not wait for listeners to complete during emergency shutdown } else if (BuildHelper.isUserDebugBuild() || BuildHelper.isEngBuild()) { int shutdownPrepareTimeOverrideInSecs = SystemProperties.getInt(PROP_MAX_GARAGE_MODE_DURATION_OVERRIDE, -1); if (shutdownPrepareTimeOverrideInSecs >= 0) { timeoutMs = shutdownPrepareTimeOverrideInSecs * 1000L; } } makeSureNoUserInteraction(); sendPowerManagerEvent(CarPowerManager.STATE_SHUTDOWN_PREPARE, timeoutMs); mHal.sendShutdownPrepare(); waitForShutdownPrepareListenersToComplete(timeoutMs, intervalMs); } private void forceSimulatedCancel() { synchronized (mLock) { mPendingPowerStates.addFirst(new CpmsState(CpmsState.WAIT_FOR_VHAL, CarPowerManager.STATE_SHUTDOWN_CANCELLED, /* canPostpone= */ false)); } mHandler.handlePowerStateChange(); synchronized (mSimulationWaitObject) { mBlockFromSimulatedCancelEvent = true; mSimulationWaitObject.notifyAll(); } } private void handleWaitForFinish(CpmsState state) { int timeoutMs = getShutdownEnterTimeoutConfig(); sendPowerManagerEvent(state.mCarPowerStateListenerState, timeoutMs); Runnable taskAtCompletion = () -> { Slogf.i(TAG, "All listeners completed for %s", powerStateToString(state.mCarPowerStateListenerState)); if (mFeatureFlags.carPowerCancelShellCommand()) { synchronized (mSimulationWaitObject) { if (mInSimulatedDeepSleepMode && mCancelDelayFromSimulatedSuspendSec >= 0) { mHandler.postDelayed(() -> forceSimulatedCancel(), mCancelDelayFromSimulatedSuspendSec * 1000L); while (!mBlockFromSimulatedCancelEvent) { try { mSimulationWaitObject.wait(); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); // Restore interrupted status } } mInSimulatedDeepSleepMode = false; return; } } } int wakeupSec; synchronized (mLock) { // If we're shutting down immediately, don't schedule a wakeup time. wakeupSec = mGarageModeShouldExitImmediately ? 0 : mNextWakeupSec; } switch (state.mCarPowerStateListenerState) { case CarPowerManager.STATE_SUSPEND_ENTER: mHal.sendSleepEntry(wakeupSec); break; case CarPowerManager.STATE_SHUTDOWN_ENTER: mHal.sendShutdownStart(wakeupSec); break; case CarPowerManager.STATE_HIBERNATION_ENTER: mHal.sendHibernationEntry(wakeupSec); break; default: Slogf.w(TAG, "Invalid action when handling wait for finish: %d", state.mCarPowerStateListenerState); break; } }; int intervalMs; synchronized (mLock) { intervalMs = mShutdownPollingIntervalMs; } waitForCompletionWithShutdownPostpone(state.mCarPowerStateListenerState, timeoutMs, taskAtCompletion, intervalMs); } private void handleFinish() { int listenerState; synchronized (mLock) { switch (mActionOnFinish) { case ACTION_ON_FINISH_SHUTDOWN: listenerState = CarPowerManager.STATE_POST_SHUTDOWN_ENTER; break; case ACTION_ON_FINISH_DEEP_SLEEP: listenerState = CarPowerManager.STATE_POST_SUSPEND_ENTER; break; case ACTION_ON_FINISH_HIBERNATION: listenerState = CarPowerManager.STATE_POST_HIBERNATION_ENTER; break; default: Slogf.w(TAG, "Invalid action on finish: %d", mActionOnFinish); return; } } int timeoutMs = getPostShutdownEnterTimeoutConfig(); sendPowerManagerEvent(listenerState, timeoutMs); Runnable taskAtCompletion = () -> { Slogf.i(TAG, "All listeners completed for %s", powerStateToString(listenerState)); doHandleFinish(); }; Slogf.i(TAG, "Start waiting for listener completion for %s", powerStateToString(listenerState)); waitForCompletion(taskAtCompletion, /* taskAtInterval= */ null, timeoutMs, /* intervalMs= */ -1); } private void doHandleFinish() { boolean simulatedMode; synchronized (mSimulationWaitObject) { simulatedMode = mInSimulatedDeepSleepMode; } boolean mustShutDown; boolean forceReboot; synchronized (mLock) { mustShutDown = (mActionOnFinish == ACTION_ON_FINISH_SHUTDOWN) && !simulatedMode; forceReboot = mRebootAfterGarageMode; mRebootAfterGarageMode = false; } if (forceReboot) { PowerManager powerManager = mContext.getSystemService(PowerManager.class); if (powerManager == null) { Slogf.wtf(TAG, "No PowerManager. Cannot reboot."); } else { Slogf.i(TAG, "GarageMode has completed. Forcing reboot."); powerManager.reboot("GarageModeReboot"); throw new AssertionError("Should not return from PowerManager.reboot()"); } } // To make Kernel implementation simpler when going into sleep. if (mWifiAdjustmentForSuspend) { disableWifiFully(); } if (mustShutDown) { // shutdown HU mSystemInterface.shutdown(); } else { doHandleSuspend(simulatedMode); } synchronized (mLock) { mShutdownOnNextSuspend = false; } } private void disableWifiFully() { disableWifi(); disableTethering(); } private void restoreWifiFully() { restoreTethering(); restoreWifi(); } private void restoreWifi() { boolean needToRestore = readWifiModifiedState(mWifiStateFile); if (!needToRestore) return; if (!mWifiManager.isWifiEnabled()) { Slogf.i(TAG, "Wifi has been enabled to restore the last setting"); mWifiManager.setWifiEnabled(true); } // Update the persistent data as wifi is not modified by car framework. saveWifiModifiedState(mWifiStateFile, /* forciblyDisabled= */ false); } private void disableWifi() { boolean wifiEnabled = mWifiManager.isWifiEnabled(); boolean wifiModifiedState = readWifiModifiedState(mWifiStateFile); if (wifiEnabled != wifiModifiedState) { Slogf.i(TAG, "Saving the current Wifi state"); saveWifiModifiedState(mWifiStateFile, wifiEnabled); } // In some devices, enabling a tether temporarily turns off Wifi. To make sure that Wifi is // disabled, we call this method in all cases. mWifiManager.setWifiEnabled(false); Slogf.i(TAG, "Wifi has been disabled and the last setting was saved"); } private void restoreTethering() { boolean needToRestore = readWifiModifiedState(mTetheringStateFile); if (!needToRestore) return; if (!mWifiManager.isWifiApEnabled()) { Slogf.i(TAG, "Tethering has been enabled to restore the last setting"); startTethering(); } // Update the persistent data as wifi is not modified by car framework. saveWifiModifiedState(mTetheringStateFile, /*forciblyDisabled= */ false); } private void disableTethering() { boolean tetheringEnabled = mWifiManager.isWifiApEnabled(); boolean tetheringModifiedState = readWifiModifiedState(mTetheringStateFile); if (tetheringEnabled != tetheringModifiedState) { Slogf.i(TAG, "Saving the current tethering state: tetheringEnabled=%b", tetheringEnabled); saveWifiModifiedState(mTetheringStateFile, tetheringEnabled); } if (!tetheringEnabled) return; mTetheringManager.stopTethering(TETHERING_WIFI); Slogf.i(TAG, "Tethering has been disabled and the last setting was saved"); } private void saveWifiModifiedState(AtomicFile file, boolean forciblyDisabled) { FileOutputStream fos; try { fos = file.startWrite(); } catch (IOException e) { Slogf.e(TAG, e, "Cannot create %s", file); return; } try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(fos, StandardCharsets.UTF_8))) { writer.write(forciblyDisabled ? COMPONENT_STATE_MODIFIED : COMPONENT_STATE_ORIGINAL); writer.newLine(); writer.flush(); file.finishWrite(fos); } catch (IOException e) { file.failWrite(fos); Slogf.e(TAG, e, "Writing %s failed", file); } } private boolean readWifiModifiedState(AtomicFile file) { boolean needToRestore = false; boolean invalidState = false; try (BufferedReader reader = new BufferedReader( new InputStreamReader(file.openRead(), StandardCharsets.UTF_8))) { String line = reader.readLine(); if (line == null) { needToRestore = false; invalidState = true; } else { line = line.trim(); needToRestore = COMPONENT_STATE_MODIFIED.equals(line); invalidState = !(needToRestore || COMPONENT_STATE_ORIGINAL.equals(line)); } } catch (IOException e) { // If a file named wifi_state doesn't exist, we will not modify Wifi at system start. Slogf.w(TAG, "Failed to read %s: %s", file, e); return false; } if (invalidState) { file.delete(); } return needToRestore; } private void startTethering() { TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI) .setShouldShowEntitlementUi(false).build(); mTetheringManager.startTethering(request, mContext.getMainExecutor(), new TetheringManager.StartTetheringCallback() { @Override public void onTetheringFailed(int error) { Slogf.w(TAG, "Starting tethering failed: %d", error); } }); } private void waitForShutdownPrepareListenersToComplete(long timeoutMs, long intervalMs) { int state = CarPowerManager.STATE_SHUTDOWN_PREPARE; Runnable taskAtCompletion = () -> { finishShutdownPrepare(); Slogf.i(TAG, "All listeners completed for %s", powerStateToString(state)); }; waitForCompletionWithShutdownPostpone(state, timeoutMs, taskAtCompletion, intervalMs); // allowUserSwitch value doesn't matter for onSuspend = true mUserService.onSuspend(); synchronized (mLock) { mShouldResumeUserService = true; } } private void waitForCompletion(Runnable taskAtCompletion, Runnable taskAtInterval, long timeoutMs, long intervalMs) { boolean isComplete = false; synchronized (mLock) { isComplete = mListenersWeAreWaitingFor.isEmpty(); } if (isComplete) { taskAtCompletion.run(); } else { // Reset a flag to signal that waiting for completion is cancelled. mIsListenerWaitingCancelled.set(false); waitForCompletionAsync(taskAtCompletion, taskAtInterval, timeoutMs, intervalMs); } } // Waits for listeners to complete. // If {@code intervalMs} is non-positive value, it is ignored and the method waits up to // {@code timeoutMs}. private void waitForCompletionAsync(Runnable taskAtCompletion, Runnable taskAtInterval, long timeoutMs, long intervalMs) { ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { long startTimeMs = SystemClock.elapsedRealtime(); while (true) { try { long waitTimeMs = timeoutMs - (SystemClock.elapsedRealtime() - startTimeMs); boolean isLastWait = true; if (intervalMs > 0 && waitTimeMs > intervalMs) { isLastWait = false; waitTimeMs = intervalMs; } boolean isNotified = mListenerCompletionSem.tryAcquire(waitTimeMs, TimeUnit.MILLISECONDS); mListenerCompletionSem.drainPermits(); if (!isNotified) { if (isLastWait) { Slogf.w(TAG, "Waiting for listener completion is timeout(%d)", waitTimeMs); taskAtCompletion.run(); return; } else if (taskAtInterval != null) { taskAtInterval.run(); } } boolean isComplete = false; synchronized (mLock) { if (mIsListenerWaitingCancelled.get()) { Slogf.i(TAG, "Waiting for listener completion is cancelled"); mIsListenerWaitingCancelled.set(false); return; } isComplete = mListenersWeAreWaitingFor.isEmpty(); } if (isComplete) { Slogf.i(TAG, "All listeners completed"); taskAtCompletion.run(); mIsListenerWaitingCancelled.set(false); return; } } catch (InterruptedException e) { Slogf.w(TAG, e, "Thread interrupted while waiting for listener completion"); Thread.currentThread().interrupt(); } } }); executor.shutdown(); } private void clearWaitingForCompletion(boolean clearQueue) { if (clearQueue) { synchronized (mLock) { mListenersWeAreWaitingFor.clear(); } } else { mIsListenerWaitingCancelled.set(true); } mListenerCompletionSem.release(); } private void sendPowerManagerEvent(@CarPowerManager.CarPowerState int newState, long timeoutMs) { // Broadcasts to the listeners that do not signal completion. notifyListeners(mPowerManagerListeners, newState, INVALID_TIMEOUT); boolean allowCompletion; boolean isShutdownPrepare = newState == CarPowerManager.STATE_SHUTDOWN_PREPARE; long internalListenerExpirationTimeMs = INVALID_TIMEOUT; long binderListenerExpirationTimeMs = INVALID_TIMEOUT; // Fully populates mListenersWeAreWaitingFor before calling any onStateChanged() // for the listeners that signal completion. // Otherwise, if the first listener calls finish() synchronously, we will // see the list go empty and we will think that we are done. PowerManagerCallbackList completingInternalListeners = new PowerManagerCallbackList(l -> { }); PowerManagerCallbackList completingBinderListeners = new PowerManagerCallbackList(l -> { }); synchronized (mLock) { if (isCompletionAllowed(newState)) { if (timeoutMs < 0) { Slogf.wtf(TAG, "Completion timeout(%d) for state(%d) should be " + "non-negative", timeoutMs, newState); return; } mStateForCompletion = newState; allowCompletion = true; internalListenerExpirationTimeMs = SystemClock.elapsedRealtime() + timeoutMs; binderListenerExpirationTimeMs = isShutdownPrepare ? INVALID_TIMEOUT : internalListenerExpirationTimeMs; } else { allowCompletion = false; mStateForCompletion = CarPowerManager.STATE_INVALID; } mListenersWeAreWaitingFor.clear(); for (int i = 0; i < mInternalPowerListeners.size(); i++) { ICarPowerStateListener listener = mInternalPowerListeners.get(i); completingInternalListeners.register(listener); if (allowCompletion) { mListenersWeAreWaitingFor.add(listener.asBinder()); } } mBroadcastHandler.post(() -> { int idx = mPowerManagerListenersWithCompletion.beginBroadcast(); while (idx-- > 0) { ICarPowerStateListener listener = mPowerManagerListenersWithCompletion.getBroadcastItem(idx); completingBinderListeners.register(listener); // For binder listeners, listener completion is not allowed for SHUTDOWN_PREPARE if (allowCompletion && !isShutdownPrepare) { synchronized (mLock) { mListenersWeAreWaitingFor.add(listener.asBinder()); } } } mPowerManagerListenersWithCompletion.finishBroadcast(); }); } // Resets the semaphore's available permits to 0. mListenerCompletionSem.drainPermits(); // Broadcasts to the listeners that DO signal completion. notifyListeners(completingInternalListeners, newState, internalListenerExpirationTimeMs); notifyListeners(completingBinderListeners, newState, binderListenerExpirationTimeMs); // Call unlinkToDeath inside RemoteCallbackList completingInternalListeners.kill(); completingBinderListeners.kill(); } private void notifyListeners(PowerManagerCallbackList listenerList, @CarPowerManager.CarPowerState int newState, long expirationTimeMs) { CountDownLatch listenerLatch = new CountDownLatch(1); mBroadcastHandler.post(() -> { int idx = listenerList.beginBroadcast(); while (idx-- > 0) { ICarPowerStateListener listener = listenerList.getBroadcastItem(idx); try { listener.onStateChanged(newState, expirationTimeMs); } catch (RemoteException e) { // It's likely the connection snapped. Let binder death handle the situation. Slogf.e(TAG, e, "onStateChanged() call failed"); } } listenerList.finishBroadcast(); listenerLatch.countDown(); }); try { listenerLatch.await(DEFAULT_COMPLETION_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Slogf.w(TAG, e, "Wait for power state listener completion interrupted"); Thread.currentThread().interrupt(); } } private void doHandleSuspend(boolean simulatedMode) { int status; if (mFeatureFlags.carPowerPolicyRefactoring()) { status = applyPowerPolicy(PolicyReader.POWER_POLICY_ID_SUSPEND_PREP, /* delayNotification= */ false, /* upToDaemon= */ false, /* force= */ true); } else { status = applyPreemptivePowerPolicy(PolicyReader.POWER_POLICY_ID_SUSPEND_PREP); } if (status != PolicyOperationStatus.OK) { Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(status)); } // Keeps holding partial wakelock to prevent entering sleep before enterDeepSleep/ // enterHibernation call. enterDeepSleep/enterHibernation should force sleep entry even if // wake lock is kept. forEachDisplay(mContext, mSystemInterface::switchToPartialWakeLock); mHandler.cancelProcessingComplete(); synchronized (mLock) { mLastSleepEntryTime = SystemClock.elapsedRealtime(); } if (simulatedMode) { simulateSleepByWaiting(); } else { boolean sleepSucceeded = suspendWithRetries(); if (!sleepSucceeded) { // Suspend failed and we shut down instead. // We either won't get here at all or we will power off very soon. return; } } @CarPowerManager.CarPowerState int nextListenerState; synchronized (mLock) { nextListenerState = (mActionOnFinish == ACTION_ON_FINISH_DEEP_SLEEP) ? CarPowerManager.STATE_SUSPEND_EXIT : CarPowerManager.STATE_HIBERNATION_EXIT; // Any wakeup time from before is no longer valid. mNextWakeupSec = 0; } Slogf.i(TAG, "Resuming after suspending"); forEachDisplay(mContext, mSystemInterface::refreshDisplayBrightness); onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState); } @GuardedBy("mLock") private boolean needPowerStateChangeLocked(@NonNull CpmsState newState) { if (mCurrentState == null) { return true; } else if (mCurrentState.equals(newState)) { Slogf.d(TAG, "Requested state is already in effect: %s", newState.name()); return false; } // The following switch/case enforces the allowed state transitions. boolean transitionAllowed = false; switch (mCurrentState.mState) { case CpmsState.WAIT_FOR_VHAL: transitionAllowed = (newState.mState == CpmsState.ON) || (newState.mState == CpmsState.SHUTDOWN_PREPARE); break; case CpmsState.SUSPEND: transitionAllowed = newState.mState == CpmsState.WAIT_FOR_VHAL; break; case CpmsState.ON: transitionAllowed = (newState.mState == CpmsState.SHUTDOWN_PREPARE) || (newState.mState == CpmsState.SIMULATE_SLEEP) || (newState.mState == CpmsState.SIMULATE_HIBERNATION); break; case CpmsState.SHUTDOWN_PREPARE: // If VHAL sends SHUTDOWN_IMMEDIATELY or SLEEP_IMMEDIATELY while in // SHUTDOWN_PREPARE state, do it. transitionAllowed = ((newState.mState == CpmsState.SHUTDOWN_PREPARE) && !newState.mCanPostpone) || (newState.mState == CpmsState.WAIT_FOR_FINISH) || (newState.mState == CpmsState.WAIT_FOR_VHAL); break; case CpmsState.SIMULATE_SLEEP: case CpmsState.SIMULATE_HIBERNATION: transitionAllowed = true; break; case CpmsState.WAIT_FOR_FINISH: transitionAllowed = (newState.mState == CpmsState.SUSPEND || newState.mState == CpmsState.WAIT_FOR_VHAL); break; default: Slogf.e(TAG, "Unexpected current state: currentState=%s, newState=%s", mCurrentState.name(), newState.name()); transitionAllowed = true; } if (!transitionAllowed) { Slogf.e(TAG, "Requested power transition is not allowed: %s --> %s", mCurrentState.name(), newState.name()); } return transitionAllowed; } private void doHandleProcessingComplete() { int listenerState = CarPowerManager.STATE_SHUTDOWN_ENTER; synchronized (mLock) { clearWaitingForCompletion(/*clearQueue=*/false); boolean shutdownOnFinish = (mActionOnFinish == ACTION_ON_FINISH_SHUTDOWN); if (!shutdownOnFinish && mLastSleepEntryTime > mShutdownStartTime) { // entered sleep after processing start. So this could be duplicate request. Slogf.w(TAG, "Duplicate sleep entry request, ignore"); return; } if (shutdownOnFinish) { listenerState = CarPowerManager.STATE_SHUTDOWN_ENTER; } else if (mActionOnFinish == ACTION_ON_FINISH_DEEP_SLEEP) { listenerState = CarPowerManager.STATE_SUSPEND_ENTER; } else if (mActionOnFinish == ACTION_ON_FINISH_HIBERNATION) { listenerState = CarPowerManager.STATE_HIBERNATION_ENTER; } } onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, listenerState); } @Override public void onDisplayBrightnessChange(int brightness) { mHandler.handleDisplayBrightnessChange(Display.DEFAULT_DISPLAY, brightness); } @Override public void onDisplayBrightnessChange(int displayId, int brightness) { mHandler.handleDisplayBrightnessChange(displayId, brightness); } private void doHandleDisplayBrightnessChange(int displayId, int brightness) { mSystemInterface.setDisplayBrightness(displayId, brightness); } private void doHandleDisplayStateChange(int displayId, boolean on) { mScreenOffHandler.handleDisplayStateChange(displayId, on); } private void doHandlePowerPolicyNotification(String policyId) { // Sending notification of power policy change triggered through CarPowerManager API. notifyPowerPolicyChange(policyId, /* upToDaemon= */ true, /* force= */ false); } private void doHandlePowerPolicyNotification(CarPowerPolicy accumulatedPolicy) { notifyPowerPolicyChange(accumulatedPolicy); } /** * Handles when display changes. */ public void handleDisplayChanged(int displayId, boolean on) { mHandler.handleDisplayStateChange(displayId, on); } /** * Gets display display power mode to turn on display. * * @return {@code false}, if the display power mode is OFF. Otherwise, {@code true}. */ public boolean canTurnOnDisplay(int displayId) { return mScreenOffHandler.canTurnOnDisplay(displayId); } /** * Notifies that user activity has happened. */ public void notifyUserActivity(int displayId, long eventTime) { mScreenOffHandler.updateUserActivity(displayId, eventTime); } @Override public void notifyUserActivity(int displayId) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); notifyUserActivity(displayId, SystemClock.uptimeMillis()); } /** * Sends display brightness to VHAL. * @param brightness value 0-100% */ public void sendDisplayBrightness(int brightness) { mHal.sendDisplayBrightness(brightness); } /** * Sends display brightness to VHAL. * @param displayId the target display * @param brightness value 0-100% */ public void sendDisplayBrightness(int displayId, int brightness) { mHal.sendDisplayBrightness(displayId, brightness); } /** * Gets the PowerHandler that we use to change power states */ public Handler getHandler() { return mHandler; } /** * Registers power state change listeners running in CarService, which is not a binder * interfaces. */ public void registerInternalListener(ICarPowerStateListener listener) { CarServiceUtils.assertCallingFromSystemProcessOrSelf(); synchronized (mLock) { mInternalPowerListeners.add(listener); } } /** * Unregisters power state change listeners running in CarService, which is not a binder * interface. */ public void unregisterInternalListener(ICarPowerStateListener listener) { CarServiceUtils.assertCallingFromSystemProcessOrSelf(); boolean found = false; synchronized (mLock) { found = mInternalPowerListeners.remove(listener); } if (found) { removeListenerFromWaitingList(listener.asBinder()); } } /** * Tells {@link CarPowerManagementService} that the listener running in CarService completes * handling power state change. */ public void completeHandlingPowerStateChange(int state, ICarPowerStateListener listener) { CarServiceUtils.assertCallingFromSystemProcessOrSelf(); handleListenerCompletion(state, listener, new ArraySet(new Integer[] {CarPowerManager.STATE_INVALID})); } // Binder interface for general use. // The listener is not required (or allowed) to call finished(). @Override public void registerListener(ICarPowerStateListener listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); mPowerManagerListeners.register(listener); } // Binder interface for Car services only. // After the listener completes its processing, it must call finished(). @Override public void registerListenerWithCompletion(ICarPowerStateListener listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_SHUTDOWN_PROCESS); mPowerManagerListenersWithCompletion.register(listener); // TODO: Need to send current state to newly registered listener? If so, need to handle // completion for SHUTDOWN_PREPARE state } @Override public void unregisterListener(ICarPowerStateListener listener) { CarServiceUtils.assertAnyPermission(mContext, Car.PERMISSION_CAR_POWER, Car.PERMISSION_CONTROL_SHUTDOWN_PROCESS); doUnregisterListener(listener); } @Override public void requestShutdownOnNextSuspend() { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); synchronized (mLock) { mShutdownOnNextSuspend = true; } } @Override public void finished(int state, ICarPowerStateListener listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_SHUTDOWN_PROCESS); handleListenerCompletion(state, listener, new ArraySet(new Integer[] {CarPowerManager.STATE_INVALID, CarPowerManager.STATE_SHUTDOWN_PREPARE})); } @Override public void scheduleNextWakeupTime(int seconds) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); if (seconds < 0) { Slogf.w(TAG, "Next wake up time is negative. Ignoring!"); return; } boolean timedWakeupAllowed = mHal.isTimedWakeupAllowed(); synchronized (mLock) { if (!timedWakeupAllowed) { Slogf.w(TAG, "Setting timed wakeups are disabled in HAL. Skipping"); mNextWakeupSec = 0; return; } if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) { // The new value is sooner than the old value. Take the new value. mNextWakeupSec = seconds; } else { Slogf.d(TAG, "Tried to schedule next wake up, but already had shorter " + "scheduled time"); } } } @Override public @CarPowerManager.CarPowerState int getPowerState() { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); synchronized (mLock) { return (mCurrentState == null) ? CarPowerManager.STATE_INVALID : mCurrentState.mCarPowerStateListenerState; } } /** * @see android.car.hardware.power.CarPowerManager#getCurrentPowerPolicy */ @Override public CarPowerPolicy getCurrentPowerPolicy() { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_READ_CAR_POWER_POLICY); if (mFeatureFlags.carPowerPolicyRefactoring()) { synchronized (mLock) { return mCurrentAccumulatedPowerPolicy; } } else { return mPowerComponentHandler.getAccumulatedPolicy(); } } /** * @see android.car.hardware.power.CarPowerManager#applyPowerPolicy */ @Override public void applyPowerPolicy(String policyId) { Slogf.i(TAG, "applyPowerPolicy(%s) from binder", policyId); CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_POWER_POLICY); Preconditions.checkArgument(policyId != null, "policyId cannot be null"); Preconditions.checkArgument(!policyId.startsWith(PolicyReader.SYSTEM_POWER_POLICY_PREFIX), "System power policy cannot be applied by apps"); // notify daemon of power policy change not needed after policy refactor boolean upToDaemon = !mFeatureFlags.carPowerPolicyRefactoring(); int status = applyPowerPolicy(policyId, /* delayNotification= */ true, upToDaemon, /* force= */ false); if (status != PolicyOperationStatus.OK) { throw new IllegalArgumentException(PolicyOperationStatus.errorCodeToString(status)); } } /** * @see android.car.hardware.power.CarPowerManager#setPowerPolicyGroup */ @Override public void setPowerPolicyGroup(String policyGroupId) throws RemoteException { Slogf.i(TAG, "setPowerPolicyGroup(%s)", policyGroupId); CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_POWER_POLICY); Preconditions.checkArgument(policyGroupId != null, "policyGroupId cannot be null"); if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } try { daemon.setPowerPolicyGroup(policyGroupId); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Policy group ID is invalid"); } catch (SecurityException e) { Slogf.e(TAG, e, "Failed to set power policy group, insufficient permissions"); } synchronized (mLock) { mCurrentPowerPolicyGroupId = policyGroupId; } } else { int status = setCurrentPowerPolicyGroup(policyGroupId); if (status != PolicyOperationStatus.OK) { throw new IllegalArgumentException(PolicyOperationStatus.errorCodeToString(status)); } } } @VisibleForTesting String getCurrentPowerPolicyGroupId() { synchronized (mLock) { return mCurrentPowerPolicyGroupId; } } @VisibleForTesting int getNumberOfCurrentPolicyRequests() { synchronized (mLock) { return mRequestIdToPolicyRequest.size(); } } /** * @see android.car.hardware.power.CarPowerManager#addPowerPolicyListener */ @Override public void addPowerPolicyListener(CarPowerPolicyFilter filter, ICarPowerPolicyListener listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_READ_CAR_POWER_POLICY); mPowerPolicyListeners.register(listener, filter); } /** * @see android.car.hardware.power.CarPowerManager#removePowerPolicyListener */ @Override public void removePowerPolicyListener(ICarPowerPolicyListener listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_READ_CAR_POWER_POLICY); mPowerPolicyListeners.unregister(listener); } /** * @see android.car.hardware.power.CarPowerManager#setDisplayPowerState */ @Override public void setDisplayPowerState(int displayId, boolean enable) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); boolean isNotSelf = Binder.getCallingUid() != Process.myUid(); CarOccupantZoneService occupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class); long token = Binder.clearCallingIdentity(); try { int driverDisplayId = occupantZoneService.getDisplayIdForDriver( CarOccupantZoneManager.DISPLAY_TYPE_MAIN); if (displayId == driverDisplayId && isNotSelf) { throw new UnsupportedOperationException("Driver display control is not supported"); } } finally { Binder.restoreCallingIdentity(token); } mSystemInterface.setDisplayState(displayId, enable); } // TODO(b/286303350): remove once power policy refactor complete; CPPD will handle power policy // change in response to silent mode status changes void notifySilentModeChange(boolean silent) { Slogf.i(TAG, "Silent mode is set to %b", silent); if (silent) { applyPreemptivePowerPolicy(PolicyReader.POWER_POLICY_ID_NO_USER_INTERACTION); } else { cancelPreemptivePowerPolicy(); } } private void handleListenerCompletion(int state, ICarPowerStateListener listener, ArraySet notAllowedStates) { synchronized (mLock) { if (notAllowedStates.contains(mStateForCompletion)) { Slogf.w(TAG, "The current state(%d) doesn't allow listener completion", mStateForCompletion); return; } if (state != mStateForCompletion) { Slogf.w(TAG, "Given state(%d) doesn't match the current state(%d) for completion", state, mStateForCompletion); return; } } removeListenerFromWaitingList(listener.asBinder()); } private void doUnregisterListener(ICarPowerStateListener listener) { mPowerManagerListeners.unregister(listener); boolean found = mPowerManagerListenersWithCompletion.unregister(listener); if (found) { // Remove this from the completion list (if it's there) removeListenerFromWaitingList(listener.asBinder()); } } private void removeListenerFromWaitingList(IBinder binderListener) { synchronized (mLock) { mListenersWeAreWaitingFor.remove(binderListener); } // Signals a thread to check if all listeners complete. mListenerCompletionSem.release(); } private void finishShutdownPrepare() { boolean shouldHandleProcessingComplete = false; synchronized (mLock) { if (mCurrentState != null && (mCurrentState.mState == CpmsState.SHUTDOWN_PREPARE || mCurrentState.mState == CpmsState.SIMULATE_SLEEP || mCurrentState.mState == CpmsState.SIMULATE_HIBERNATION)) { // All apps are ready to shutdown/suspend. if (mActionOnFinish != ACTION_ON_FINISH_SHUTDOWN) { if (mLastSleepEntryTime > mShutdownStartTime && mLastSleepEntryTime < SystemClock.elapsedRealtime()) { Slogf.d(TAG, "finishShutdownPrepare: Already slept!"); return; } } shouldHandleProcessingComplete = true; } } if (shouldHandleProcessingComplete) { Slogf.i(TAG, "Apps are finished, call handleProcessingComplete()"); mHandler.handleProcessingComplete(); } } /** * Converts a given power policy from power policy type returned by the car power policy daemon * to the one used within car service. * @param policy of type android.frameworks.automotive.powerpolicy.CarPowerPolicy * @return policy converted to type android.car.hardware.power.CarPowerPolicy */ private CarPowerPolicy convertPowerPolicyFromDaemon( android.frameworks.automotive.powerpolicy.CarPowerPolicy policy) { return new CarPowerPolicy(policy.policyId, policy.enabledComponents, policy.disabledComponents); } private final class PowerPolicyCallback extends ICarPowerPolicyDelegateCallback.Stub { @Override public void updatePowerComponents( android.frameworks.automotive.powerpolicy.CarPowerPolicy policy) { mPowerComponentHandler.applyPowerPolicy(convertPowerPolicyFromDaemon(policy)); } @Override public void onApplyPowerPolicySucceeded(int requestId, android.frameworks.automotive.powerpolicy.CarPowerPolicy accumulatedPolicy, boolean deferred) { Slogf.i(TAG, "onApplyPowerPolicySucceeded: requestId = %d, policyId = %s, " + "deferred = %b", requestId, accumulatedPolicy.policyId, deferred); AsyncPolicyRequest policyRequest; synchronized (mLock) { policyRequest = mRequestIdToPolicyRequest.get(requestId); } if (policyRequest == null) { Slogf.e(TAG, "No power policy request exists for request ID %d", requestId); return; } CarPowerPolicy accumulatedPowerPolicy = convertPowerPolicyFromDaemon(accumulatedPolicy); policyRequest.onPolicyRequestSucceeded(accumulatedPowerPolicy, deferred); } @Override public void onApplyPowerPolicyFailed(int requestId, int reason) { Slogf.i(TAG, "onApplyPowerPolicyFailed: requestId = %d, reason = %d", requestId, reason); AsyncPolicyRequest policyRequest; synchronized (mLock) { policyRequest = mRequestIdToPolicyRequest.get(requestId); } if (policyRequest == null) { Slogf.e(TAG, "No power policy request exists for request ID %d", requestId); return; } policyRequest.onPolicyRequestFailed(reason); } @Override public void onPowerPolicyChanged( android.frameworks.automotive.powerpolicy.CarPowerPolicy accumulatedPolicy) { CarPowerPolicy currentAccumulatedPolicy = convertPowerPolicyFromDaemon(accumulatedPolicy); updateCurrentPowerPolicy(currentAccumulatedPolicy); String policyId = accumulatedPolicy.policyId; Slogf.i(TAG, "Queueing power policy notification (ID: %s) in the handler", policyId); mHandler.handlePowerPolicyNotification(currentAccumulatedPolicy); } @Override public String getInterfaceHash() { return ICarPowerPolicyDelegateCallback.HASH; } @Override public int getInterfaceVersion() { return ICarPowerPolicyDelegateCallback.VERSION; } } private static CarPowerPolicy getInitialAccumulatedPowerPolicy() { String initialPolicyId = ""; int[] enabledComponents = new int[0]; int[] disabledComponents = new int[LAST_POWER_COMPONENT - FIRST_POWER_COMPONENT + 1]; int disabledIndex = 0; for (int component = FIRST_POWER_COMPONENT; component <= LAST_POWER_COMPONENT; component++) { disabledComponents[disabledIndex++] = component; } return new CarPowerPolicy(initialPolicyId, enabledComponents, disabledComponents); } private void initializeRegisteredPowerPolicies( android.frameworks.automotive.powerpolicy.CarPowerPolicy[] policies) { for (int i = 0; i < policies.length; i++) { CarPowerPolicy policy = convertPowerPolicyFromDaemon(policies[i]); List enabledComponents = PowerComponentUtil.powerComponentsToStrings( Lists.asImmutableList(policy.getEnabledComponents())); List disabledComponents = PowerComponentUtil.powerComponentsToStrings( Lists.asImmutableList(policy.getDisabledComponents())); mPolicyReader.definePowerPolicy(policy.getPolicyId(), enabledComponents.toArray(String[]::new), disabledComponents.toArray(String[]::new)); } mPowerPoliciesInitialized = true; mPowerPolicyInitializationLatch.countDown(); } @VisibleForTesting public void initializePowerPolicy() { if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } PowerPolicyInitData powerPolicyInitData; if (daemon != null) { try { PowerPolicyCallback powerPolicyCallback = new PowerPolicyCallback(); powerPolicyInitData = daemon.notifyCarServiceReady(powerPolicyCallback); } catch (RemoteException e) { Slogf.e(TAG, e, "Failed to tell car power policy daemon that CarService is" + " ready"); return; } } else { Slogf.w(TAG, "Failed to notify car service is ready, car power policy daemon" + " is not available"); return; } mReadyForCallback.set(true); int[] registeredCustomComponents = powerPolicyInitData.registeredCustomComponents; Integer[] customComponents = new Integer[registeredCustomComponents.length]; for (int i = 0; i < customComponents.length; i++) { customComponents[i] = registeredCustomComponents[i]; } mPowerComponentHandler.registerCustomComponents(customComponents); initializeRegisteredPowerPolicies(powerPolicyInitData.registeredPolicies); CarPowerPolicy currentPowerPolicy = convertPowerPolicyFromDaemon( powerPolicyInitData.currentPowerPolicy); updateCurrentPowerPolicy(currentPowerPolicy); mPowerComponentHandler.applyPowerPolicy(currentPowerPolicy); notifyPowerPolicyChange(currentPowerPolicy); // To cover the case where power state changed before connecting to CPPD. int currentPowerState = getPowerState(); if (currentPowerState != CarPowerManager.STATE_WAIT_FOR_VHAL && currentPowerState != CarPowerManager.STATE_ON) { Slogf.w(TAG, "Current power state is %s, doesn't correspond to wait for VHAL " + "or on state, skipping notification of power state to daemon.", powerStateToString(currentPowerState)); } else { notifyPowerStateChangeToDaemon(daemon, currentPowerState); } } else { Slogf.i(TAG, "CPMS is taking control from carpowerpolicyd"); ICarPowerPolicySystemNotification daemon; synchronized (mLock) { daemon = mCarPowerPolicyDaemon; } PolicyState state; if (daemon != null) { try { state = daemon.notifyCarServiceReady(); } catch (RemoteException e) { Slogf.e(TAG, e, "Failed to tell car power policy daemon that CarService is ready"); return; } } else { Slogf.w(TAG, "Failed to notify car service is ready. car power policy daemon is not " + "available"); return; } String currentPowerPolicyId; String currentPolicyGroupId; synchronized (mLock) { mHasControlOverDaemon = true; currentPowerPolicyId = mCurrentPowerPolicyId; currentPolicyGroupId = mCurrentPowerPolicyGroupId; } // If the current power policy or the policy group has been modified by CPMS, we ignore // the power policy or the policy group passed from car power policy daemon, and // notifies the current power policy to the daemon. if (currentPowerPolicyId == null || currentPowerPolicyId.isEmpty()) { Slogf.i(TAG, "Attempting to apply the power policy(%s) from the daemon", state.policyId); int status = applyPowerPolicy(state.policyId, /* delayNotification= */ false, /* upToDaemon= */ false, /* force= */ false); if (status != PolicyOperationStatus.OK) { Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(status)); } } else { Slogf.i(TAG, "CPMS applied power policy(%s) before connecting to the daemon. Notifying " + "to the daemon...", currentPowerPolicyId); notifyPowerPolicyChangeToDaemon(currentPowerPolicyId, /* force= */ true); } if (currentPolicyGroupId == null || currentPolicyGroupId.isEmpty()) { int status = setCurrentPowerPolicyGroup(state.policyGroupId); if (status != PolicyOperationStatus.OK) { Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(status)); } } } mSilentModeHandler.init(); } @PolicyOperationStatus.ErrorCode private int setCurrentPowerPolicyGroup(String policyGroupId) { if (!mPolicyReader.isPowerPolicyGroupAvailable(policyGroupId)) { int error = PolicyOperationStatus.ERROR_SET_POWER_POLICY_GROUP; Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyGroupId + " is not registered")); return error; } synchronized (mLock) { mCurrentPowerPolicyGroupId = policyGroupId; } return PolicyOperationStatus.OK; } private static final class AsyncPolicyRequest { private final Object mLock = new Object(); private final int mRequestId; private final long mTimeoutMs; private final CountDownLatch mPolicyRequestLatch = new CountDownLatch(1); @GuardedBy("mLock") private boolean mPolicyRequestSucceeded; @GuardedBy("mLock") @Nullable private CarPowerPolicy mAccumulatedPolicy; @GuardedBy("mLock") @PowerPolicyFailureReason private int mFailureReason; @GuardedBy("mLock") private boolean mIsDeferred; AsyncPolicyRequest(int requestId, long timeoutMs) { mRequestId = requestId; mTimeoutMs = timeoutMs; } public int getRequestId() { return mRequestId; } public boolean isSuccessful() { synchronized (mLock) { return mPolicyRequestSucceeded; } } @Nullable public CarPowerPolicy getAccumulatedPolicy() { synchronized (mLock) { return mAccumulatedPolicy; } } @PowerPolicyFailureReason public int getFailureReason() { synchronized (mLock) { return mFailureReason; } } public boolean isDeferred() { synchronized (mLock) { return mIsDeferred; } } public boolean await() throws InterruptedException { return mPolicyRequestLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS); } public void onPolicyRequestSucceeded(CarPowerPolicy accumulatedPolicy, boolean deferred) { synchronized (mLock) { mPolicyRequestSucceeded = true; if (!deferred) { mAccumulatedPolicy = accumulatedPolicy; } mIsDeferred = deferred; } mPolicyRequestLatch.countDown(); } public void onPolicyRequestFailed(@PowerPolicyFailureReason int reason) { synchronized (mLock) { mFailureReason = reason; } mPolicyRequestLatch.countDown(); } } private AsyncPolicyRequest generateAsyncPolicyRequest(long timeoutMs) { int requestId = mPolicyRequestIdCounter.getAndIncrement(); return new AsyncPolicyRequest(requestId, timeoutMs); } @PolicyOperationStatus.ErrorCode private int getPolicyRequestError(int requestId, @PowerPolicyFailureReason int reason) { switch(reason) { case PowerPolicyFailureReason.POWER_POLICY_FAILURE_UNKNOWN: Slogf.w(TAG, "Power policy request %d failed for unknown reason", requestId); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; case PowerPolicyFailureReason.POWER_POLICY_FAILURE_NOT_REGISTERED_ID: Slogf.w(TAG, "Power policy request %d failed due to unregistered" + "power policy ID", requestId); return PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID; case PowerPolicyFailureReason.POWER_POLICY_FAILURE_CANNOT_OVERRIDE: Slogf.w(TAG, "Power policy request %d failed because current non-" + "preemptive power policy cannot be overridden", requestId); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; default: Slogf.w(TAG, "Reason for power policy request %d failing is " + "undefined", requestId); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } } @PolicyOperationStatus.ErrorCode private int applyPowerPolicy(@Nullable String policyId, boolean delayNotification, boolean upToDaemon, boolean force) { if (mFeatureFlags.carPowerPolicyRefactoring()) { AsyncPolicyRequest request = generateAsyncPolicyRequest( DEFAULT_POWER_POLICY_REQUEST_TIMEOUT_MS); int requestId = request.getRequestId(); ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; mRequestIdToPolicyRequest.put(requestId, request); } if (daemon == null) { Slogf.w(TAG, "Cannot call applyPowerPolicyAsync(requestId=%d, policyId=%s) to CPPD:" + " CPPD is not available", requestId, policyId); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } if (!mReadyForCallback.get()) { Slogf.w(TAG, "Cannot call applyPowerPolicyAsync(requestId=%d, policyId=%s) to CPPD:" + " not ready for calling to CPPD", requestId, policyId); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } try { Slogf.i(TAG, "Request(%d) of applying power policy(%s) to CPPD in async", requestId, policyId); daemon.applyPowerPolicyAsync(requestId, policyId, force); boolean policyRequestServed = request.await(); if (!policyRequestServed) { Slogf.w(TAG, "Power policy request (ID: %d) successful application timed out" + " after %d ms", requestId, DEFAULT_POWER_POLICY_REQUEST_TIMEOUT_MS); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } } catch (IllegalArgumentException e) { int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); return error; } catch (IllegalStateException e) { int error = PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); return error; } catch (SecurityException e) { Slogf.w(TAG, e, "Failed to apply power policy, insufficient permissions"); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } catch (InterruptedException e) { Slogf.w(TAG, e, "Wait for power policy change request interrupted"); Thread.currentThread().interrupt(); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } catch (RemoteException e) { Slogf.w(TAG, e, "Failed to apply power policy, connection issue"); return PolicyOperationStatus.ERROR_APPLY_POWER_POLICY; } finally { synchronized (mLock) { mRequestIdToPolicyRequest.remove(requestId); } } if (!request.isSuccessful()) { Slogf.w(TAG, "Failed to apply power policy, failure reason = %d", request.getFailureReason()); return getPolicyRequestError(requestId, request.getFailureReason()); } if (request.isDeferred()) { Slogf.i(TAG, "Applying power policy(%s) is deferred", policyId); return PolicyOperationStatus.OK; } CarPowerPolicy accumulatedPolicy = request.getAccumulatedPolicy(); updateCurrentPowerPolicy(accumulatedPolicy); if (delayNotification) { Slogf.d(TAG, "Queueing power policy notification (id: %s) in the handler", policyId); mHandler.handlePowerPolicyNotification(accumulatedPolicy); } else { notifyPowerPolicyChange(accumulatedPolicy); } } else { CarPowerPolicy policy = mPolicyReader.getPowerPolicy(policyId); if (policy == null) { int error = PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID; Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); return error; } synchronized (mLock) { if (mIsPowerPolicyLocked) { Slogf.i(TAG, "Power policy is locked. The request policy(%s) will be applied " + "when power policy becomes unlocked", policyId); mPendingPowerPolicyId = policyId; return PolicyOperationStatus.OK; } mCurrentPowerPolicyId = policyId; } mPowerComponentHandler.applyPowerPolicy(policy); if (delayNotification) { Slogf.d(TAG, "Queueing power policy notification (id: %s) in the handler", policyId); mHandler.handlePowerPolicyNotification(policyId); } else { notifyPowerPolicyChange(policyId, upToDaemon, force); } } Slogf.i(TAG, "The current power policy is %s", policyId); return PolicyOperationStatus.OK; } @PolicyOperationStatus.ErrorCode private int applyPreemptivePowerPolicy(String policyId) { CarPowerPolicy policy = mPolicyReader.getPreemptivePowerPolicy(policyId); if (policy == null) { int error = PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID; Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); return error; } synchronized (mLock) { mIsPowerPolicyLocked = true; if (!mPolicyReader.isPreemptivePowerPolicy(mCurrentPowerPolicyId)) { mPendingPowerPolicyId = mCurrentPowerPolicyId; } mCurrentPowerPolicyId = policyId; } mPowerComponentHandler.applyPowerPolicy(policy); notifyPowerPolicyChange(policyId, /* upToDaemon= */ true, /* force= */ true); Slogf.i(TAG, "The current power policy is %s", policyId); return PolicyOperationStatus.OK; } private void cancelPreemptivePowerPolicy() { Slogf.i(TAG, "Canceling preemptive power policy"); String policyId; synchronized (mLock) { if (!mIsPowerPolicyLocked) { Slogf.w(TAG, "Failed to cancel system power policy: the current policy is not the " + "system power policy"); return; } mIsPowerPolicyLocked = false; policyId = mPendingPowerPolicyId; mPendingPowerPolicyId = null; } if (policyId != null) { // Pending policy exist int status = applyPowerPolicy(policyId, /* delayNotification= */ false, /* upToDaemon= */ true, /* force= */ true); if (status != PolicyOperationStatus.OK) { Slogf.w(TAG, "Failed to cancel system power policy: %s", PolicyOperationStatus.errorCodeToString(status)); } } else { Slogf.w(TAG, "cancelPreemptivePowerPolicy(), no pending power policy"); } } private void notifyPowerPolicyChangeToDaemon(String policyId, boolean force) { ICarPowerPolicySystemNotification daemon; synchronized (mLock) { daemon = mCarPowerPolicyDaemon; if (daemon == null) { Slogf.e(TAG, "Failed to notify car power policy daemon: the daemon is not ready"); return; } if (!mHasControlOverDaemon) { Slogf.w(TAG, "Notifying policy change is deferred: CPMS has not yet taken control"); return; } } try { daemon.notifyPowerPolicyChange(policyId, force); } catch (RemoteException | IllegalStateException e) { Slogf.e(TAG, e, "Failed to notify car power policy daemon of a new power policy(%s)", policyId); } } // TODO(b/286303350): remove after power policy refactor is complete - will only use version // that takes 'accumulatedPowerPolicy' as input private void notifyPowerPolicyChange(String policyId, boolean upToDaemon, boolean force) { // Notify system clients if (upToDaemon) { notifyPowerPolicyChangeToDaemon(policyId, force); } // Notify Java clients CarPowerPolicy appliedPolicy = mPolicyReader.isPreemptivePowerPolicy(policyId) ? mPolicyReader.getPreemptivePowerPolicy(policyId) : mPolicyReader.getPowerPolicy(policyId); CarPowerPolicy accumulatedPolicy = mPowerComponentHandler.getAccumulatedPolicy(); notifyPowerPolicyChange(policyId, appliedPolicy, accumulatedPolicy); } @Nullable private CarPowerPolicy getPowerPolicyDefinition(String policyId, long timeoutMs) throws InterruptedException { if (!mPowerPoliciesInitialized) { boolean result = mPowerPolicyInitializationLatch.await(timeoutMs, TimeUnit.MILLISECONDS); if (!result) { Slogf.e(TAG, "Failed to get power policy initialization after waiting %d ms", timeoutMs); return null; } } return mPolicyReader.getPowerPolicy(policyId); } private void notifyPowerPolicyChange(CarPowerPolicy accumulatedPowerPolicy) { String policyId = accumulatedPowerPolicy.getPolicyId(); try { CarPowerPolicy appliedPolicy = getPowerPolicyDefinition(policyId, CAR_POWER_POLICY_DEFINITION_TIMEOUT_MS); if (appliedPolicy == null) { Slogf.wtf(TAG, "The new power policy(%s) should exist", policyId); return; } notifyPowerPolicyChange(policyId, appliedPolicy, accumulatedPowerPolicy); } catch (InterruptedException e) { Slogf.e(TAG, e, "Failed to get power policy definition for policy ID %s", policyId); } } // TODO(b/286303350): after power policy refactor is complete, remove this function and replace // the inner call to it by notifyPowerPolicyChange(accumulatedPowerPolicy) // with this function's code contents private void notifyPowerPolicyChange(String policyId, CarPowerPolicy appliedPolicy, CarPowerPolicy accumulatedPolicy) { EventLogHelper.writePowerPolicyChange(policyId); if (appliedPolicy == null) { Slogf.wtf(TAG, "The new power policy(%s) should exist", policyId); } Slogf.i(TAG, "Power policy change to %s is notified to apps", policyId); mBroadcastHandler.post(() -> { int idx = mPowerPolicyListeners.beginBroadcast(); while (idx-- > 0) { ICarPowerPolicyListener listener = mPowerPolicyListeners.getBroadcastItem(idx); CarPowerPolicyFilter filter = (CarPowerPolicyFilter) mPowerPolicyListeners.getBroadcastCookie(idx); if (!mPowerComponentHandler.isComponentChanged(filter)) { continue; } try { listener.onPolicyChanged(appliedPolicy, accumulatedPolicy); } catch (RemoteException e) { // It's likely the connection snapped. Let binder death handle the situation. Slogf.e(TAG, e, "onPolicyChanged() call failed: policyId = %s", policyId); } } mPowerPolicyListeners.finishBroadcast(); }); } private void makeSureNoUserInteraction() { mSilentModeHandler.updateKernelSilentMode(true); int status; if (mFeatureFlags.carPowerPolicyRefactoring()) { status = applyPowerPolicy(PolicyReader.POWER_POLICY_ID_NO_USER_INTERACTION, /* delayNotification= */ false, /* upToDaemon= */ false, /* force= */ true); } else { status = applyPreemptivePowerPolicy( PolicyReader.POWER_POLICY_ID_NO_USER_INTERACTION); } if (status != PolicyOperationStatus.OK) { Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(status)); } } @GuardedBy("mLock") private android.os.IInterface getPowerPolicyDaemonLocked() { if (mFeatureFlags.carPowerPolicyRefactoring()) { return mRefactoredCarPowerPolicyDaemon; } else { return mCarPowerPolicyDaemon; } } private void connectToPowerPolicyDaemon() { synchronized (mLock) { android.os.IInterface powerPolicyDaemon = getPowerPolicyDaemonLocked(); if (powerPolicyDaemon != null || mConnectionInProgress) { return; } mConnectionInProgress = true; } connectToDaemonHelper(CAR_POWER_POLICY_DAEMON_BIND_MAX_RETRY); } private void connectToDaemonHelper(int retryCount) { if (retryCount <= 0) { synchronized (mLock) { mConnectionInProgress = false; } Slogf.e(TAG, "Cannot reconnect to car power policyd daemon after retrying %d times", CAR_POWER_POLICY_DAEMON_BIND_MAX_RETRY); return; } if (makeBinderConnection()) { Slogf.i(TAG, "Connected to car power policy daemon"); initializePowerPolicy(); return; } final int numRetry = retryCount - 1; mHandler.postDelayed(() -> connectToDaemonHelper(numRetry), CAR_POWER_POLICY_DAEMON_BIND_RETRY_INTERVAL_MS); } private boolean makeBinderConnection() { long currentTimeMs = SystemClock.uptimeMillis(); IBinder binder; if (mFeatureFlags.carPowerPolicyRefactoring()) { binder = ServiceManagerHelper.getService(REFACTORED_CAR_POWER_POLICY_DAEMON_INTERFACE); } else { binder = ServiceManagerHelper.getService(CAR_POWER_POLICY_DAEMON_INTERFACE); } if (binder == null) { Slogf.w(TAG, "Finding car power policy daemon failed. Power policy management is not " + "supported"); return false; } long elapsedTimeMs = SystemClock.uptimeMillis() - currentTimeMs; if (elapsedTimeMs > CAR_POWER_POLICY_DAEMON_FIND_MARGINAL_TIME_MS) { Slogf.wtf(TAG, "Finding car power policy daemon took too long(%dms)", elapsedTimeMs); } if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon = ICarPowerPolicyDelegate.Stub.asInterface(binder); if (daemon == null) { Slogf.w(TAG, "Getting car power policy daemon interface failed. Power policy " + "management is not supported"); return false; } synchronized (mLock) { mRefactoredCarPowerPolicyDaemon = daemon; mConnectionInProgress = false; } mBinderHandler = new BinderHandler(daemon); } else { ICarPowerPolicySystemNotification daemon = ICarPowerPolicySystemNotification.Stub.asInterface(binder); if (daemon == null) { Slogf.w(TAG, "Getting car power policy daemon interface failed. Power policy " + "management is not supported"); return false; } synchronized (mLock) { mCarPowerPolicyDaemon = daemon; mConnectionInProgress = false; } mBinderHandler = new BinderHandler(daemon); } mBinderHandler.linkToDeath(); return true; } private void updateCurrentPowerPolicy(CarPowerPolicy accumulatedPolicy) { synchronized (mLock) { mCurrentPowerPolicyId = accumulatedPolicy.getPolicyId(); mCurrentAccumulatedPowerPolicy = accumulatedPolicy; } } private final class BinderHandler implements IBinder.DeathRecipient { // TODO(b/286303350): replace with refactored daemon once power policy refactor is complete private ICarPowerPolicySystemNotification mDaemon; private ICarPowerPolicyDelegate mRefactoredDaemon; private BinderHandler(ICarPowerPolicySystemNotification daemon) { mDaemon = daemon; } private BinderHandler(ICarPowerPolicyDelegate daemon) { mRefactoredDaemon = daemon; } @Override public void binderDied() { Slogf.w(TAG, "Car power policy daemon died: reconnecting"); unlinkToDeath(); if (mFeatureFlags.carPowerPolicyRefactoring()) { mRefactoredDaemon = null; } else { mDaemon = null; } mReadyForCallback.set(false); synchronized (mLock) { if (mFeatureFlags.carPowerPolicyRefactoring()) { mRefactoredCarPowerPolicyDaemon = null; } else { mCarPowerPolicyDaemon = null; mHasControlOverDaemon = false; } } mHandler.postDelayed( () -> connectToDaemonHelper(CAR_POWER_POLICY_DAEMON_BIND_MAX_RETRY), CAR_POWER_POLICY_DAEMON_BIND_RETRY_INTERVAL_MS); } private void linkToDeath() { if (mFeatureFlags.carPowerPolicyRefactoring()) { if (mRefactoredDaemon == null) { return; } } else { if (mDaemon == null) { return; } } IBinder binder; if (mFeatureFlags.carPowerPolicyRefactoring()) { binder = mRefactoredDaemon.asBinder(); } else { binder = mDaemon.asBinder(); } if (binder == null) { Slogf.w(TAG, "Linking to binder death recipient skipped"); return; } try { binder.linkToDeath(this, 0); } catch (RemoteException e) { if (mFeatureFlags.carPowerPolicyRefactoring()) { mRefactoredDaemon = null; } else { mDaemon = null; } Slogf.w(TAG, e, "Linking to binder death recipient failed: %s"); } } private void unlinkToDeath() { if (mFeatureFlags.carPowerPolicyRefactoring()) { if (mRefactoredDaemon == null) { return; } } else { if (mDaemon == null) { return; } } IBinder binder; if (mFeatureFlags.carPowerPolicyRefactoring()) { binder = mRefactoredDaemon.asBinder(); } else { binder = mDaemon.asBinder(); } if (binder == null) { Slogf.w(TAG, "Unlinking from binder death recipient skipped"); return; } binder.unlinkToDeath(this, 0); } } private final class PowerHandler extends Handler { private static final String TAG = PowerHandler.class.getSimpleName(); private static final int MSG_POWER_STATE_CHANGE = 0; private static final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1; private static final int MSG_DISPLAY_STATE_CHANGE = 2; private static final int MSG_PROCESSING_COMPLETE = 3; private static final int MSG_POWER_POLICY_NOTIFICATION = 4; // Do not handle this immediately but with some delay as there can be a race between // display off due to rear view camera and delivery to here. private static final long MAIN_DISPLAY_EVENT_DELAY_MS = 500; private final WeakReference mService; private PowerHandler(Looper looper, CarPowerManagementService service) { super(looper); mService = new WeakReference(service); } private void handlePowerStateChange() { Message msg = obtainMessage(MSG_POWER_STATE_CHANGE); sendMessage(msg); } private void handleDisplayBrightnessChange(int displayId, int brightness) { Message msg = obtainMessage(MSG_DISPLAY_BRIGHTNESS_CHANGE, displayId, brightness); sendMessage(msg); } private void handleDisplayStateChange(int displayId, boolean on) { HandlerHelper.removeEqualMessages(this, MSG_DISPLAY_STATE_CHANGE, displayId); Message msg = obtainMessage(MSG_DISPLAY_STATE_CHANGE, displayId); msg.arg1 = on ? Display.STATE_ON : Display.STATE_OFF; sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS); } private void handleProcessingComplete() { removeMessages(MSG_PROCESSING_COMPLETE); Message msg = obtainMessage(MSG_PROCESSING_COMPLETE); sendMessage(msg); } private void cancelProcessingComplete() { removeMessages(MSG_PROCESSING_COMPLETE); } private void handlePowerPolicyNotification(String policyId) { Message msg = obtainMessage(MSG_POWER_POLICY_NOTIFICATION, policyId); sendMessage(msg); } private void handlePowerPolicyNotification(CarPowerPolicy accumulatedPolicy) { Message msg = obtainMessage(MSG_POWER_POLICY_NOTIFICATION, accumulatedPolicy); sendMessage(msg); } private void cancelAll() { removeMessages(MSG_POWER_STATE_CHANGE); removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE); removeMessages(MSG_DISPLAY_STATE_CHANGE); removeMessages(MSG_PROCESSING_COMPLETE); removeMessages(MSG_POWER_POLICY_NOTIFICATION); } @Override public void handleMessage(Message msg) { CarPowerManagementService service = mService.get(); if (service == null) { Slogf.i(TAG, "handleMessage null service"); return; } switch (msg.what) { case MSG_POWER_STATE_CHANGE: service.doHandlePowerStateChange(); break; case MSG_DISPLAY_BRIGHTNESS_CHANGE: service.doHandleDisplayBrightnessChange( /* displayId= */ msg.arg1, /* brightness= */ msg.arg2); break; case MSG_DISPLAY_STATE_CHANGE: int displayId = (Integer) msg.obj; boolean on = msg.arg1 == Display.STATE_ON; service.doHandleDisplayStateChange(displayId, on); break; case MSG_PROCESSING_COMPLETE: service.doHandleProcessingComplete(); break; case MSG_POWER_POLICY_NOTIFICATION: if (mFeatureFlags.carPowerPolicyRefactoring()) { service.doHandlePowerPolicyNotification((CarPowerPolicy) msg.obj); } else { service.doHandlePowerPolicyNotification((String) msg.obj); } break; default: Slogf.w(TAG, "handleMessage invalid message type: %d", msg.what); break; } } } // Send the command to enter Suspend to RAM. // If the command is not successful, try again with an exponential back-off. // If it fails repeatedly, send the command to shut down. // If we decide to go to a different power state, abort this retry mechanism. // Returns true if we successfully suspended. private boolean suspendWithRetries() { boolean isSuspendToDisk; synchronized (mLock) { isSuspendToDisk = mActionOnFinish == ACTION_ON_FINISH_HIBERNATION; } String suspendTarget = isSuspendToDisk ? "Suspend-to-Disk" : "Suspend-to-RAM"; long retryIntervalMs = INITIAL_SUSPEND_RETRY_INTERVAL_MS; long totalWaitDurationMs = 0; while (true) { long suspendStartTime = SystemClock.elapsedRealtime(); Slogf.i(TAG, "Entering %s", suspendTarget); if (isSuspendToDisk) { freeMemory(); } boolean suspendSucceeded = isSuspendToDisk ? mSystemInterface.enterHibernation() : mSystemInterface.enterDeepSleep(); if (suspendSucceeded) { return true; } if (totalWaitDurationMs >= mMaxSuspendWaitDurationMs) { break; } // We failed to suspend. Block the thread briefly and try again. synchronized (mLock) { if (!mPendingPowerStates.isEmpty()) { // Check for a new power state now, before going around the loop again. CpmsState state = mPendingPowerStates.peekFirst(); if (state != null && needPowerStateChangeLocked(state)) { Slogf.i(TAG, "Terminating the attempt to suspend target = %s," + " currentState = %s, pendingState = %s", suspendTarget, mCurrentState.stateToString(), state.stateToString()); return false; } } long suspendStopTime = SystemClock.elapsedRealtime(); Slogf.w(TAG, "Failed to Suspend; will retry after %dms", retryIntervalMs); try { mLock.wait(retryIntervalMs); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } totalWaitDurationMs += retryIntervalMs; totalWaitDurationMs += (suspendStopTime - suspendStartTime); retryIntervalMs = Math.min(retryIntervalMs * 2, MAX_RETRY_INTERVAL_MS); } } // Too many failures trying to suspend. Shut down. Slogf.w(TAG, "Could not %s after %dms long trial. Shutting down.", suspendTarget, totalWaitDurationMs); mSystemInterface.shutdown(); return false; } private static final class CpmsState { // NOTE: When modifying states below, make sure to update CarPowerStateChanged.State in // frameworks/proto_logging/stats/atoms.proto also. public static final int WAIT_FOR_VHAL = 0; public static final int ON = 1; public static final int SHUTDOWN_PREPARE = 2; public static final int WAIT_FOR_FINISH = 3; public static final int SUSPEND = 4; public static final int SIMULATE_SLEEP = 5; public static final int SIMULATE_HIBERNATION = 6; /* Config values from AP_POWER_STATE_REQ */ public final boolean mCanPostpone; @PowerState.ShutdownType public final int mShutdownType; /* Message sent to CarPowerStateListener in response to this state */ @CarPowerManager.CarPowerState public final int mCarPowerStateListenerState; /* One of the above state variables */ public final int mState; /** * This constructor takes a PowerHalService.PowerState object and creates the corresponding * CPMS state from it. */ CpmsState(PowerState halPowerState) { switch (halPowerState.mState) { case VehicleApPowerStateReq.ON: this.mCanPostpone = false; this.mShutdownType = PowerState.SHUTDOWN_TYPE_UNDEFINED; this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(ON); this.mState = ON; break; case VehicleApPowerStateReq.SHUTDOWN_PREPARE: this.mCanPostpone = halPowerState.canPostponeShutdown(); this.mShutdownType = halPowerState.getShutdownType(); this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState( SHUTDOWN_PREPARE); this.mState = SHUTDOWN_PREPARE; break; case VehicleApPowerStateReq.CANCEL_SHUTDOWN: this.mCanPostpone = false; this.mShutdownType = PowerState.SHUTDOWN_TYPE_UNDEFINED; this.mCarPowerStateListenerState = CarPowerManager.STATE_SHUTDOWN_CANCELLED; this.mState = WAIT_FOR_VHAL; break; case VehicleApPowerStateReq.FINISHED: this.mCanPostpone = false; this.mShutdownType = PowerState.SHUTDOWN_TYPE_UNDEFINED; this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(SUSPEND); this.mState = SUSPEND; break; default: // Illegal state from PowerState. Throw an exception? // TODO(b/202414427): Add handling of illegal state this.mCanPostpone = false; this.mShutdownType = PowerState.SHUTDOWN_TYPE_UNDEFINED; this.mCarPowerStateListenerState = 0; this.mState = 0; break; } } CpmsState(int state, int carPowerStateListenerState, boolean canPostpone) { this.mCanPostpone = canPostpone; this.mCarPowerStateListenerState = carPowerStateListenerState; this.mState = state; this.mShutdownType = state == SIMULATE_SLEEP ? PowerState.SHUTDOWN_TYPE_DEEP_SLEEP : (state == SIMULATE_HIBERNATION ? PowerState.SHUTDOWN_TYPE_HIBERNATION : PowerState.SHUTDOWN_TYPE_POWER_OFF); } CpmsState(int state, int carPowerStateListenerState, boolean canPostpone, int shutdownType) { this.mCanPostpone = canPostpone; this.mCarPowerStateListenerState = carPowerStateListenerState; this.mState = state; this.mShutdownType = shutdownType; } public String name() { return new StringBuilder() .append(stateToString()) .append('(') .append(mState) .append(')') .toString(); } private String stateToString() { String baseName; switch(mState) { case WAIT_FOR_VHAL: baseName = "WAIT_FOR_VHAL"; break; case ON: baseName = "ON"; break; case SHUTDOWN_PREPARE: baseName = "SHUTDOWN_PREPARE"; break; case WAIT_FOR_FINISH: baseName = "WAIT_FOR_FINISH"; break; case SUSPEND: baseName = "SUSPEND"; break; case SIMULATE_SLEEP: baseName = "SIMULATE_SLEEP"; break; case SIMULATE_HIBERNATION: baseName = "SIMULATE_HIBERNATION"; break; default: baseName = ""; break; } return baseName; } private static int cpmsStateToPowerStateListenerState(int state) { int powerStateListenerState = 0; // Set the CarPowerStateListenerState based on current state switch (state) { case ON: powerStateListenerState = CarPowerManager.STATE_ON; break; case SHUTDOWN_PREPARE: powerStateListenerState = CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE; break; case SUSPEND: powerStateListenerState = CarPowerManager.STATE_SUSPEND_ENTER; break; case WAIT_FOR_VHAL: case WAIT_FOR_FINISH: default: // Illegal state for this constructor. Throw an exception? break; } return powerStateListenerState; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof CpmsState)) { return false; } CpmsState that = (CpmsState) o; return this.mState == that.mState && this.mShutdownType == that.mShutdownType && this.mCanPostpone == that.mCanPostpone && this.mCarPowerStateListenerState == that.mCarPowerStateListenerState; } @Override public int hashCode() { return Objects.hash(mCanPostpone, mShutdownType, mCarPowerStateListenerState, mState); } // PowerPolicyHostTest uses the dump output of {@code CarPowerManagementService}. If the // {@code CpmsState.toString} is modifed, PowerPolicyHostTest should be updated accordingly. // TODO(b/184862429): Remove the above comment once dump in proto buffer is done. @Override public String toString() { return "CpmsState canPostpone=" + mCanPostpone + ", carPowerStateListenerState=" + mCarPowerStateListenerState + ", mShutdownType=" + mShutdownType + ", CpmsState=" + name(); } } /** * Resume after a manually-invoked suspend. * Invoked using "adb shell dumpsys cmd car_service resume". */ public void forceSimulatedResume() { synchronized (mLock) { // Cancel Garage Mode in case it's running boolean isSuspendToDisk = mActionOnFinish == ACTION_ON_FINISH_HIBERNATION; mPendingPowerStates.addFirst(new CpmsState(CpmsState.WAIT_FOR_VHAL, isSuspendToDisk ? CarPowerManager.STATE_HIBERNATION_EXIT : CarPowerManager.STATE_SUSPEND_EXIT, /* canPostpone= */ false)); mLock.notifyAll(); } mHandler.handlePowerStateChange(); synchronized (mSimulationWaitObject) { mWakeFromSimulatedSleep = true; mSimulationWaitObject.notifyAll(); } } /** * Manually enters simulated suspend (deep sleep or hibernation) mode, trigging Garage mode. * *

If {@code shouldReboot} is 'true', reboots the system when Garage Mode completes. * * Can be invoked using * {@code "adb shell cmd car_service suspend --simulate"} or * {@code "adb shell cmd car_service hibernate --simulate"} or * {@code "adb shell cmd car_service garage-mode reboot"}. * * This is similar to {@code 'onApPowerStateChange()'} except that it needs to create a * {@code CpmsState} that is not directly derived from a {@code VehicleApPowerStateReq}. */ public void simulateSuspendAndMaybeReboot(@PowerState.ShutdownType int shutdownType, boolean shouldReboot, boolean skipGarageMode, int wakeupAfter, boolean freeMemory) { simulateSuspendAndMaybeReboot(shutdownType, shouldReboot, skipGarageMode, wakeupAfter, CarPowerManagementService.NO_WAKEUP_BY_TIMER, freeMemory); } /** * Manually enters simulated suspend (deep sleep or hibernation) mode, trigging Garage mode. * *

If {@code shouldReboot} is 'true', reboots the system when Garage Mode completes. * * Can be invoked using * {@code "adb shell cmd car_service suspend --simulate"} or * {@code "adb shell cmd car_service hibernate --simulate"} or * {@code "adb shell cmd car_service garage-mode reboot"}. * * This is similar to {@code 'onApPowerStateChange()'} except that it needs to create a * {@code CpmsState} that is not directly derived from a {@code VehicleApPowerStateReq}. */ // TODO(b/274895468): Add tests public void simulateSuspendAndMaybeReboot(@PowerState.ShutdownType int shutdownType, boolean shouldReboot, boolean skipGarageMode, int wakeupAfter, int cancelAfter, boolean freeMemory) { boolean isDeepSleep = shutdownType == PowerState.SHUTDOWN_TYPE_DEEP_SLEEP; if (cancelAfter >= 0) { Slogf.i(TAG, "Cancel after is: %d", cancelAfter); } if (wakeupAfter >= 0) { Slogf.i(TAG, "Wakeup after is: %d", wakeupAfter); } synchronized (mSimulationWaitObject) { mInSimulatedDeepSleepMode = true; mWakeFromSimulatedSleep = false; mBlockFromSimulatedCancelEvent = false; mResumeDelayFromSimulatedSuspendSec = wakeupAfter; mCancelDelayFromSimulatedSuspendSec = cancelAfter; mFreeMemoryBeforeSuspend = freeMemory; } synchronized (mLock) { mRebootAfterGarageMode = shouldReboot; mPendingPowerStates.addFirst(new CpmsState(isDeepSleep ? CpmsState.SIMULATE_SLEEP : CpmsState.SIMULATE_HIBERNATION, CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE, !skipGarageMode)); } mHandler.handlePowerStateChange(); } /** * Manually defines a power policy. * *

If the given ID already exists or specified power components are invalid, it fails. * * @return {@code true}, if successful. Otherwise, {@code false}. */ public boolean definePowerPolicyFromCommand(String[] args, IndentingPrintWriter writer) { if (args.length < 2) { writer.println("Too few arguments"); return false; } String powerPolicyId = args[1]; int index = 2; String[] enabledComponents = new String[0]; String[] disabledComponents = new String[0]; while (index < args.length) { switch (args[index]) { case "--enable": if (index == args.length - 1) { writer.println("No components for --enable"); return false; } enabledComponents = args[index + 1].split(","); break; case "--disable": if (index == args.length - 1) { writer.println("No components for --disabled"); return false; } disabledComponents = args[index + 1].split(","); break; default: writer.printf("Unrecognized argument: %s\n", args[index]); return false; } index += 2; } int status = definePowerPolicy(powerPolicyId, enabledComponents, disabledComponents); if (status != PolicyOperationStatus.OK) { writer.println(PolicyOperationStatus.errorCodeToString(status)); return false; } writer.printf("Power policy(%s) is successfully defined.\n", powerPolicyId); return true; } /** * Defines a power policy with the given id and components. * *

A policy defined with this method is valid until the system is rebooted/restarted. */ @VisibleForTesting @PolicyOperationStatus.ErrorCode public int definePowerPolicy(String powerPolicyId, String[] enabledComponents, String[] disabledComponents) { Preconditions.checkArgument(!powerPolicyId.startsWith( PolicyReader.SYSTEM_POWER_POLICY_PREFIX), "System power policy cannot be defined by apps"); int status = mPolicyReader.definePowerPolicy(powerPolicyId, enabledComponents, disabledComponents); if (status != PolicyOperationStatus.OK) { return status; } // Get custom power components and update components list in PowerComponentHandler Collection customComponents = mPolicyReader.getCustomComponents().values(); if (customComponents.size() > 0) { mPowerComponentHandler.registerCustomComponents( customComponents.toArray(new Integer[customComponents.size()])); } if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } try { daemon.notifyPowerPolicyDefinition(powerPolicyId, enabledComponents, disabledComponents); } catch (IllegalArgumentException e) { int policyError = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; int componentError = PolicyOperationStatus.ERROR_INVALID_POWER_COMPONENT; Slogf.w(TAG, e, "%s and/or %s", PolicyOperationStatus.errorCodeToString(policyError), PolicyOperationStatus.errorCodeToString(componentError)); return policyError; } catch (IllegalStateException | RemoteException e) { int error = PolicyOperationStatus.ERROR_DEFINE_POWER_POLICY; Slogf.w(TAG, e, PolicyOperationStatus.errorCodeToString(error)); return error; } catch (SecurityException e) { int error = PolicyOperationStatus.ERROR_DEFINE_POWER_POLICY; Slogf.w(TAG, e, "%s; insufficient permissions", PolicyOperationStatus.errorCodeToString(error)); return error; } } else { ICarPowerPolicySystemNotification daemon; synchronized (mLock) { daemon = mCarPowerPolicyDaemon; } try { daemon.notifyPowerPolicyDefinition(powerPolicyId, enabledComponents, disabledComponents); } catch (RemoteException e) { int error = PolicyOperationStatus.ERROR_DEFINE_POWER_POLICY; Slogf.w(TAG, e, PolicyOperationStatus.errorCodeToString(error)); return error; } } return PolicyOperationStatus.OK; } /** * Manually applies a power policy. * *

If the given ID is not defined, it fails. * * @return {@code true}, if successful. Otherwise, {@code false}. */ public boolean applyPowerPolicyFromCommand(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { writer.println("Power policy ID should be given"); return false; } String powerPolicyId = args[1]; if (powerPolicyId == null) { writer.println("Policy ID cannot be null"); return false; } int status; Slogf.i(TAG, "Applying power policy(%s) from shell command", powerPolicyId); if (mFeatureFlags.carPowerPolicyRefactoring()) { status = applyPowerPolicy(powerPolicyId, /* delayNotification= */ false, /* upToDaemon= */ false, /* force= */ false); } else { boolean isPreemptive = mPolicyReader.isPreemptivePowerPolicy(powerPolicyId); status = isPreemptive ? applyPreemptivePowerPolicy(powerPolicyId) : applyPowerPolicy(powerPolicyId, /* delayNotification= */ false, /* upToDaemon= */ true, /* force= */ false); } if (status != PolicyOperationStatus.OK) { writer.println(PolicyOperationStatus.errorCodeToString(status)); return false; } writer.printf("Power policy(%s) is successfully applied.\n", powerPolicyId); return true; } /** * Manually defines a power policy group. * *

If the given ID already exists, a wrong power state is given, or specified power policy ID * doesn't exist, it fails. * * @return {@code true}, if successful. Otherwise, {@code false}. */ public boolean definePowerPolicyGroupFromCommand(String[] args, IndentingPrintWriter writer) { if (args.length < 3 || args.length > 4) { writer.println("Invalid syntax"); return false; } String policyGroupId = args[1]; int index = 2; SparseArray defaultPolicyPerState = new SparseArray<>(); String[] powerPolicyPerState = {"", ""}; while (index < args.length) { String[] tokens = args[index].split(":"); if (tokens.length != 2) { writer.println("Invalid syntax"); return false; } int state = PolicyReader.toPowerState(tokens[0]); if (state == PolicyReader.INVALID_POWER_STATE) { writer.printf("Invalid power state: %s\n", tokens[0]); return false; } defaultPolicyPerState.put(state, tokens[1]); switch (state) { case VehicleApPowerStateReport.WAIT_FOR_VHAL: powerPolicyPerState[INDEX_WAIT_FOR_VHAL] = tokens[1]; break; case VehicleApPowerStateReport.ON: powerPolicyPerState[INDEX_ON] = tokens[1]; break; } index++; } if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } try { daemon.notifyPowerPolicyGroupDefinition(policyGroupId, powerPolicyPerState); } catch (IllegalArgumentException e) { Slogf.w(TAG, e, "The given policy group ID(%s) or mapping between power state and" + " policy is invalid", policyGroupId); return false; } catch (RemoteException e) { Slogf.w(TAG, e, "Calling ICarPowerPolicyDelegate.notifyPowerPolicyGroupDefinition" + " failed"); return false; } } int status = mPolicyReader.definePowerPolicyGroup(policyGroupId, defaultPolicyPerState); if (status != PolicyOperationStatus.OK) { writer.println(PolicyOperationStatus.errorCodeToString(status)); return false; } writer.printf("Power policy group(%s) is successfully defined.\n", policyGroupId); return true; } /** * Manually sets a power policy group. * *

If the given ID is not defined, it fails. * * @return {@code true}, if successful. Otherwise, {@code false}. */ public boolean setPowerPolicyGroupFromCommand(String[] args, IndentingPrintWriter writer) { if (args.length != 2) { writer.println("Power policy group ID should be given"); return false; } String policyGroupId = args[1]; if (mFeatureFlags.carPowerPolicyRefactoring()) { try { setPowerPolicyGroup(policyGroupId); } catch (RemoteException e) { writer.println("RemoteException encountered when setting power policy group"); return false; } } else { int status = setCurrentPowerPolicyGroup(policyGroupId); if (status != PolicyOperationStatus.OK) { writer.println(PolicyOperationStatus.errorCodeToString(status)); return false; } } writer.printf("Setting power policy group(%s) is successful.\n", policyGroupId); return true; } /** * Suspends the device. * *

According to the argument, the device is suspended to RAM or disk. */ public void suspendFromCommand(boolean isHibernation, boolean skipGarageMode) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); int param = 0; if (isHibernation) { if (!isHibernationAvailable()) { throw new IllegalStateException("The device doesn't support hibernation"); } param = skipGarageMode ? VehicleApPowerStateShutdownParam.HIBERNATE_IMMEDIATELY : VehicleApPowerStateShutdownParam.CAN_HIBERNATE; } else { if (!isDeepSleepAvailable()) { throw new IllegalStateException("The device doesn't support deep sleep"); } param = skipGarageMode ? VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY : VehicleApPowerStateShutdownParam.CAN_SLEEP; } PowerState state = new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, param); synchronized (mLock) { mRebootAfterGarageMode = false; mPendingPowerStates.addFirst(new CpmsState(state)); mLock.notifyAll(); } mHandler.handlePowerStateChange(); } /** * Powers off the device. */ public void powerOffFromCommand(boolean skipGarageMode, boolean reboot) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); Slogf.i(TAG, "%s %s Garage Mode", reboot ? "Rebooting" : "Powering off", skipGarageMode ? "with" : "without"); int param = skipGarageMode ? VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY : VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY; PowerState state = new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, param); synchronized (mLock) { mRebootAfterGarageMode = reboot; mPendingPowerStates.addFirst(new CpmsState(state)); mLock.notifyAll(); } mHandler.handlePowerStateChange(); } /** * Returns the last shutdown state. */ public int getLastShutdownState() { synchronized (mLock) { return mLastShutdownState; } } /** * Changes Silent Mode to the given mode. */ public void setSilentMode(String silentMode) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER); mSilentModeHandler.setSilentMode(silentMode); if (mFeatureFlags.carPowerPolicyRefactoring()) { ICarPowerPolicyDelegate daemon; synchronized (mLock) { daemon = mRefactoredCarPowerPolicyDaemon; } if (daemon != null) { try { daemon.setSilentMode(silentMode); } catch (RemoteException e) { Slogf.e(TAG, e, "Failed to notify car power policy daemon of the new silent " + "mode(%s)", silentMode); return; } } else { Slogf.w(TAG, "Failed to notify the new silent mode, car power policy daemon" + " is not available"); return; } Slogf.i(TAG, "Set the new silent mode(%s) to CPPD", silentMode); } } /** * Dumps the current Silent Mode. */ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) public void dumpSilentMode(IndentingPrintWriter writer) { mSilentModeHandler.dump(writer); } /** * Returns whether a listener completion is allowed for the given state. * *

This method is used internally and is different from * {@link CarPowerManager.isCompletionAllowed} in that listener completion is allowed for * SHUTDOWN_PREPARE. */ public static boolean isCompletionAllowed(@CarPowerManager.CarPowerState int state) { return CarPowerManager.isCompletionAllowed(state); } /** * Returns a corresponding string of the given power state. */ public static String powerStateToString(int state) { return DebugUtils.valueToString(CarPowerManager.class, "STATE_", state); } /** * Requests VHAL to shutdown the head unit. * * @throws UnsupportedOperationException If the system doesn't not support * {@code nextPowerState}. */ public void requestShutdownAp(int nextPowerState, boolean runGarageMode) { int shutdownParam = PowerState.SHUTDOWN_TYPE_POWER_OFF; switch (nextPowerState) { case CarRemoteAccessManager.NEXT_POWER_STATE_ON: // Do nothing. return; case CarRemoteAccessManager.NEXT_POWER_STATE_OFF: shutdownParam = PowerState.SHUTDOWN_TYPE_POWER_OFF; break; case CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_RAM: if (!isDeepSleepAvailable()) { throw new UnsupportedOperationException("Suspend-to-RAM is not supported"); } shutdownParam = PowerState.SHUTDOWN_TYPE_DEEP_SLEEP; break; case CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_DISK: if (!isHibernationAvailable()) { throw new UnsupportedOperationException("Suspend-to-disk is not supported"); } shutdownParam = PowerState.SHUTDOWN_TYPE_HIBERNATION; break; default: Slogf.w(TAG, "Unknown power state(%d)", nextPowerState); return; } mHal.requestShutdownAp(shutdownParam, runGarageMode); } /** * Returns whether suspend (deep sleep or hibernation) is available on the device. */ public boolean isSuspendAvailable(boolean isHibernation) { return isHibernation ? isHibernationAvailable() : isDeepSleepAvailable(); } /** * Enters garage mode if the bootup reason is ENTER_GARAGE_MODE. */ @Override public void onInitComplete() { if (mFeatureFlags.serverlessRemoteAccess()) { maybeEnterGarageModeOnBoot(); } } /** * Shutdown the device to run garage mode if the bootup reason is ENTER_GARAGE_MODE. */ private void maybeEnterGarageModeOnBoot() { @BootupReason int bootupReason = mHal.getVehicleApBootupReason(); Slogf.i(TAG, "Vehicle AP power bootup reason: " + bootupReason); if (bootupReason != BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE) { return; } if (mHal.isVehicleInUse()) { Slogf.i(TAG, "Bootup reason is ENTER_GARAGE_MODE but vehicle is currently in use" + ", skip entering garage mode"); return; } try { requestShutdownAp(getLastShutdownState(), /* runGarageMode= */ true); } catch (Exception e) { Slogf.wtf(TAG, "Failed to call requestShutdownAp", e); } } private boolean isDeepSleepAvailable() { return mHal.isDeepSleepAllowed() && mSystemInterface.isSystemSupportingDeepSleep(); } private boolean isHibernationAvailable() { return mHal.isHibernationAllowed() && mSystemInterface.isSystemSupportingHibernation(); } // In a real Deep Sleep, the hardware removes power from the CPU (but retains power // on the RAM). This puts the processor to sleep. Upon some external signal, power // is re-applied to the CPU, and processing resumes right where it left off. // We simulate this behavior by calling wait(). // We continue from wait() when forceSimulatedResume() is called. private void simulateSleepByWaiting() { Slogf.i(TAG, "Starting to simulate Deep Sleep by waiting"); synchronized (mSimulationWaitObject) { if (mFreeMemoryBeforeSuspend) { freeMemory(); } if (mResumeDelayFromSimulatedSuspendSec >= 0) { Slogf.i(TAG, "Scheduling a wakeup after %d seconds", mResumeDelayFromSimulatedSuspendSec); Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(() -> forceSimulatedResume(), mResumeDelayFromSimulatedSuspendSec * 1000L); } while (!mWakeFromSimulatedSleep) { try { mSimulationWaitObject.wait(); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); // Restore interrupted status } } mInSimulatedDeepSleepMode = false; } Slogf.i(TAG, "Exit Deep Sleep simulation"); } private int getMaxSuspendWaitDurationConfig() { return mContext.getResources().getInteger(R.integer.config_maxSuspendWaitDuration); } private boolean isWifiAdjustmentForSuspendConfig() { return mContext.getResources().getBoolean(R.bool.config_wifiAdjustmentForSuspend); } private int getPreShutdownPrepareTimeoutConfig() { return getCompletionWaitTimeoutConfig(R.integer.config_preShutdownPrepareTimeout); } private int getShutdownEnterTimeoutConfig() { return getCompletionWaitTimeoutConfig(R.integer.config_shutdownEnterTimeout); } private int getPostShutdownEnterTimeoutConfig() { return getCompletionWaitTimeoutConfig(R.integer.config_postShutdownEnterTimeout); } private int getS2dImportanceLevel() { return convertMemorySuspendConfigToValue(mContext.getResources().getString( R.string.config_suspend_to_disk_memory_savings)); } private ArraySet getS2dAllowList() { return new ArraySet<>(mContext.getResources().getStringArray( R.array.config_packages_not_to_stop_during_suspend)); } private int getCompletionWaitTimeoutConfig(int resourceId) { int timeout = mContext.getResources().getInteger(resourceId); return timeout >= 0 ? timeout : DEFAULT_COMPLETION_WAIT_TIMEOUT; } @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) private static String actionOnFinishToString(int actionOnFinish) { switch (actionOnFinish) { case ACTION_ON_FINISH_SHUTDOWN: return "Shutdown"; case ACTION_ON_FINISH_DEEP_SLEEP: return "Deep sleep"; case ACTION_ON_FINISH_HIBERNATION: return "Hibernation"; default: return "Unknown"; } } private void waitForCompletionWithShutdownPostpone( @CarPowerManager.CarPowerState int carPowerStateListenerState, long timeoutMs, Runnable taskAtCompletion, long intervalMs) { Runnable taskAtInterval = () -> { mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS); }; Slogf.i(TAG, "Start waiting for listener completion for %s", powerStateToString(carPowerStateListenerState)); waitForCompletion(taskAtCompletion, taskAtInterval, timeoutMs, intervalMs); } /** * Utility method to help with memory freeing before entering Suspend-To-Disk */ private void freeMemory() { ActivityManagerHelper.killAllBackgroundProcesses(); if (!mFeatureFlags.stopProcessBeforeSuspendToDisk()) { return; } List allRunningAppProcesses = ActivityManagerHelper.getRunningAppProcesses(); ArraySet safeUids = new ArraySet<>(); ArraySet suspendToDiskAllowList = getS2dAllowList(); int suspendToDiskImportanceLevel = getS2dImportanceLevel(); for (int i = 0; i < allRunningAppProcesses.size(); i++) { ActivityManager.RunningAppProcessInfo info = allRunningAppProcesses.get(i); boolean isCarServiceOrMyPid = ICarImpl.class.getPackage().getName() .equals(info.processName) || info.pid == android.os.Process.myPid(); boolean isSystemOrShellUid = info.uid == Process.SYSTEM_UID || info.uid == Process.SHELL_UID; boolean isProcessPersistent = (ActivityManagerHelper .getFlagsForRunningAppProcessInfo(info) & ActivityManagerHelper.PROCESS_INFO_PERSISTENT_FLAG) != 0; boolean isWithinConfig = suspendToDiskImportanceLevel > info.importance; boolean isProcessAllowListed = suspendToDiskAllowList.contains(info.processName); if (isCarServiceOrMyPid || isSystemOrShellUid || isProcessPersistent || isWithinConfig || isProcessAllowListed) { safeUids.add(info.uid); } } for (int i = 0; i < allRunningAppProcesses.size(); i++) { ActivityManager.RunningAppProcessInfo info = allRunningAppProcesses.get(i); if (!safeUids.contains(info.uid)) { for (int j = 0; j < info.pkgList.length; j++) { String pkgToStop = info.pkgList[j]; PackageManagerHelper.forceStopPackageAsUser(mContext, pkgToStop, UserManagerHelper.USER_ALL); } } } } /** * Helper method to accept function with one displayId argument to run on all displays. */ private static void forEachDisplay(@NonNull Context context, @NonNull Consumer consumer) { DisplayManager displayManager = context.getSystemService(DisplayManager.class); for (Display display : displayManager.getDisplays()) { int displayId = display.getDisplayId(); consumer.accept(displayId); } } private int convertMemorySuspendConfigToValue(String configValue) { return switch (configValue) { case "low" -> ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; case "medium" -> ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; case "high" -> ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; // none will fallthrough default -> ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; }; } }