Refactor CPMS
- Remove Boot Reason
- Rework state machine according to updated design document
- Update CarLocationService with new CPMS states
- Update GarageMode.Controller with new CPMS states
- Update unit tests for CarLocationManager, GarageMode, and CPMS
Bug: 112548962
Test: vhal_emulator.py
(cherry picked from pi-car-dev)
Change-Id: I0e262311626c5032695ec766c34b36f40dc931ec
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index e0a55c9..a16c3e3 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -161,10 +161,9 @@
@Override
public void onStateChanged(int state, CompletableFuture<Void> future) {
+ logd("onStateChanged: " + state);
switch (state) {
- case CarPowerStateListener.SHUTDOWN_ENTER:
- case CarPowerStateListener.SUSPEND_ENTER:
- logd("onStateChanged: " + state);
+ case CarPowerStateListener.SHUTDOWN_PREPARE:
asyncOperation(() -> {
storeLocation();
// Notify the CarPowerManager that it may proceed to shutdown or suspend.
@@ -173,8 +172,7 @@
}
});
break;
- case CarPowerStateListener.SHUTDOWN_CANCELLED:
- case CarPowerStateListener.SUSPEND_EXIT:
+ default:
// This service does not need to do any work for these events but should still
// notify the CarPowerManager that it may proceed.
if (future != null) {
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index 1514056..6ae33cc 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -20,6 +20,7 @@
import android.car.hardware.power.ICarPower;
import android.car.hardware.power.ICarPowerStateListener;
import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -56,7 +57,7 @@
private final Map<IBinder, Integer> mPowerManagerListenerTokens = new ConcurrentHashMap<>();
@GuardedBy("this")
- private PowerState mCurrentState;
+ private CpmsState mCurrentState;
@GuardedBy("this")
private Timer mTimer;
@GuardedBy("this")
@@ -64,20 +65,21 @@
@GuardedBy("this")
private long mLastSleepEntryTime;
@GuardedBy("this")
- private final LinkedList<PowerState> mPendingPowerStates = new LinkedList<>();
+ private final LinkedList<CpmsState> mPendingPowerStates = new LinkedList<>();
@GuardedBy("this")
private HandlerThread mHandlerThread;
@GuardedBy("this")
private PowerHandler mHandler;
- private int mBootReason;
- private boolean mShutdownOnNextSuspend = false;
- private int mNextWakeupSec;
+ private int mNextWakeupSec = 0;
private int mTokenValue = 1;
+ private boolean mShutdownOnFinish = false;
// TODO: Make this OEM configurable.
- private static final int APP_EXTEND_MAX_MS = 86400000; // 1 day
- private final static int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
- private final static int SHUTDOWN_EXTEND_MAX_MS = 5000;
+ private static final int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
+ private static final int SHUTDOWN_EXTEND_MAX_MS = 5000;
+
+ // Use one hour for now
+ private static int sShutdownPrepareTimeMs = 60 * 60 * 1000;
private class PowerManagerCallbackList extends RemoteCallbackList<ICarPowerStateListener> {
/**
@@ -111,6 +113,16 @@
mHandler = new PowerHandler(Looper.getMainLooper());
}
+ @VisibleForTesting
+ protected static void setShutdownPrepareTimeout(int timeoutMs) {
+ // Override the timeout to keep testing time short
+ if (timeoutMs < SHUTDOWN_EXTEND_MAX_MS) {
+ sShutdownPrepareTimeMs = SHUTDOWN_EXTEND_MAX_MS;
+ } else {
+ sShutdownPrepareTimeMs = timeoutMs;
+ }
+ }
+
@Override
public void init() {
synchronized (this) {
@@ -121,18 +133,11 @@
mHal.setListener(this);
if (mHal.isPowerStateSupported()) {
- mHal.sendBootComplete();
- PowerState currentState = mHal.getCurrentPowerState();
- if (currentState != null) {
- onApPowerStateChange(currentState);
- } else {
- Log.w(CarLog.TAG_POWER, "Unable to get get current power state during "
- + "initialization");
- }
+ // Initialize CPMS in WAIT_FOR_VHAL state
+ onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, CarPowerStateListener.WAIT_FOR_VHAL);
} else {
Log.w(CarLog.TAG_POWER, "Vehicle hal does not support power state yet.");
- onApPowerStateChange(new PowerState(PowerHalService.STATE_ON_FULL, 0));
- mSystemInterface.switchToFullWakeLock();
+ onApPowerStateChange(CpmsState.ON, CarPowerStateListener.ON);
}
mSystemInterface.startDisplayStateMonitoring(this);
}
@@ -158,71 +163,42 @@
mSystemInterface.releaseAllWakeLocks();
}
- /**
- * Notifies earlier completion of power event processing. If some user modules need pre-shutdown
- * processing time, they will get up to #APP_EXTEND_MAX_MS to complete their tasks. Modules
- * are expected to finish earlier than that, and this call can be called in such case to trigger
- * shutdown without waiting further.
- */
- public void notifyPowerEventProcessingCompletion() {
- long processingTime = 0;
- synchronized (mPowerManagerListenerTokens) {
- if (!mPowerManagerListenerTokens.isEmpty()) {
- processingTime += APP_EXTEND_MAX_MS;
- }
- }
- long now = SystemClock.elapsedRealtime();
- long startTime;
- boolean shouldShutdown = true;
- PowerHandler powerHandler;
- synchronized (this) {
- startTime = mProcessingStartTime;
- if (mCurrentState == null) {
- return;
- }
- if (mCurrentState.mState != PowerHalService.STATE_SHUTDOWN_PREPARE) {
- return;
- }
- if (mCurrentState.canEnterDeepSleep() && !mShutdownOnNextSuspend) {
- shouldShutdown = false;
- if (mLastSleepEntryTime > mProcessingStartTime && mLastSleepEntryTime < now) {
- // already slept
- return;
- }
- }
- powerHandler = mHandler;
- }
- if ((startTime + processingTime) <= now) {
- Log.i(CarLog.TAG_POWER, "Processing all done");
- powerHandler.handleProcessingComplete(shouldShutdown);
- }
- }
-
@Override
public void dump(PrintWriter writer) {
writer.println("*PowerManagementService*");
writer.print("mCurrentState:" + mCurrentState);
writer.print(",mProcessingStartTime:" + mProcessingStartTime);
- writer.println(",mLastSleepEntryTime:" + mLastSleepEntryTime);
- }
-
- @Override
- public void onBootReasonReceived(int bootReason) {
- mBootReason = bootReason;
+ writer.print(",mLastSleepEntryTime:" + mLastSleepEntryTime);
+ writer.print(",mNextWakeupSec:" + mNextWakeupSec);
+ writer.print(",mTokenValue:" + mTokenValue);
+ writer.println(",mShutdownOnFinish:" + mShutdownOnFinish);
}
@Override
public void onApPowerStateChange(PowerState state) {
PowerHandler handler;
synchronized (this) {
- mPendingPowerStates.addFirst(state);
+ mPendingPowerStates.addFirst(new CpmsState(state));
+ handler = mHandler;
+ }
+ handler.handlePowerStateChange();
+ }
+
+ /**
+ * Initiate state change from CPMS directly.
+ */
+ private void onApPowerStateChange(int apState, int carPowerStateListenerState) {
+ CpmsState newState = new CpmsState(apState, carPowerStateListenerState);
+ PowerHandler handler;
+ synchronized (this) {
+ mPendingPowerStates.addFirst(newState);
handler = mHandler;
}
handler.handlePowerStateChange();
}
private void doHandlePowerStateChange() {
- PowerState state = null;
+ CpmsState state;
PowerHandler handler;
synchronized (this) {
state = mPendingPowerStates.peekFirst();
@@ -230,7 +206,9 @@
if (state == null) {
return;
}
- if (!needPowerStateChange(state)) {
+ Log.i(CarLog.TAG_POWER, "doHandlePowerStateChange: newState=" + state.mState);
+ if (!needPowerStateChangeLocked(state)) {
+ Log.d(CarLog.TAG_POWER, "doHandlePowerStateChange no change needed");
return;
}
// now real power change happens. Whatever was queued before should be all cancelled.
@@ -238,44 +216,66 @@
handler = mHandler;
}
handler.cancelProcessingComplete();
-
- Log.i(CarLog.TAG_POWER, "Power state change:" + state);
+ Log.i(CarLog.TAG_POWER, "setCurrentState " + state.toString());
+ mCurrentState = state;
switch (state.mState) {
- case PowerHalService.STATE_ON_DISP_OFF:
- handleDisplayOff(state);
+ case CpmsState.WAIT_FOR_VHAL:
+ handleWaitForVhal(state);
break;
- case PowerHalService.STATE_ON_FULL:
- handleFullOn(state);
+ case CpmsState.ON:
+ handleOn();
break;
- case PowerHalService.STATE_SHUTDOWN_PREPARE:
+ case CpmsState.SHUTDOWN_PREPARE:
handleShutdownPrepare(state);
break;
+ case CpmsState.WAIT_FOR_FINISH:
+ handleWaitForFinish(state);
+ break;
+ case CpmsState.SUSPEND:
+ // Received FINISH from VHAL
+ handleFinish();
+ break;
+ default:
+ // Illegal state
+ // TODO: Throw exception?
+ break;
}
}
- private void handleDisplayOff(PowerState newState) {
- setCurrentState(newState);
- mSystemInterface.setDisplayState(false);
+ private void handleWaitForVhal(CpmsState state) {
+ int carPowerStateListenerState = state.mCarPowerStateListenerState;
+ sendPowerManagerEvent(carPowerStateListenerState, false);
+ // Inspect CarPowerStateListenerState to decide which message to send via VHAL
+ switch (carPowerStateListenerState) {
+ case CarPowerStateListener.WAIT_FOR_VHAL:
+ mHal.sendWaitForVhal();
+ break;
+ case CarPowerStateListener.SHUTDOWN_CANCELLED:
+ mHal.sendShutdownCancel();
+ break;
+ case CarPowerStateListener.SUSPEND_EXIT:
+ mHal.sendSleepExit();
+ break;
+ }
}
- private void handleFullOn(PowerState newState) {
- setCurrentState(newState);
+ private void handleOn() {
mSystemInterface.setDisplayState(true);
+ sendPowerManagerEvent(CarPowerStateListener.ON, false);
+ mHal.sendOn();
}
- private void handleShutdownPrepare(PowerState newState) {
- setCurrentState(newState);
- mSystemInterface.setDisplayState(false);;
- boolean shouldShutdown = true;
- if (mHal.isDeepSleepAllowed() && mSystemInterface.isSystemSupportingDeepSleep() &&
- newState.canEnterDeepSleep() && !mShutdownOnNextSuspend) {
- Log.i(CarLog.TAG_POWER, "starting sleep");
- shouldShutdown = false;
- doHandlePreprocessing(shouldShutdown);
- return;
- } else if (newState.canPostponeShutdown()) {
- Log.i(CarLog.TAG_POWER, "starting shutdown with processing");
- doHandlePreprocessing(shouldShutdown);
+ private void handleShutdownPrepare(CpmsState newState) {
+ mSystemInterface.setDisplayState(false);
+ // Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
+ mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
+ || !mSystemInterface.isSystemSupportingDeepSleep()
+ || !newState.mCanSleep;
+ if (newState.mCanPostpone) {
+ Log.i(CarLog.TAG_POWER, "starting shutdown postpone");
+ sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE, true);
+ mHal.sendShutdownPrepare();
+ doHandlePreprocessing();
} else {
Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
synchronized (this) {
@@ -285,6 +285,26 @@
}
}
+ private void handleWaitForFinish(CpmsState state) {
+ sendPowerManagerEvent(state.mCarPowerStateListenerState, false);
+ switch (state.mCarPowerStateListenerState) {
+ case CarPowerStateListener.SUSPEND_ENTER:
+ mHal.sendSleepEntry(mNextWakeupSec);
+ break;
+ case CarPowerStateListener.SHUTDOWN_ENTER:
+ mHal.sendShutdownStart(mNextWakeupSec);
+ break;
+ }
+ }
+
+ private void handleFinish() {
+ if (mShutdownOnFinish) {
+ doHandleShutdown();
+ } else {
+ doHandleDeepSleep();
+ }
+ }
+
@GuardedBy("this")
private void releaseTimerLocked() {
if (mTimer != null) {
@@ -293,57 +313,45 @@
mTimer = null;
}
- private void doHandlePreprocessing(boolean shuttingDown) {
- // Set time for powerManager events
- long processingTimeMs = sendPowerManagerEvent(shuttingDown);
- if (processingTimeMs > 0) {
- int pollingCount = (int)(processingTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
- Log.i(CarLog.TAG_POWER, "processing before shutdown expected for: "
- + processingTimeMs + " ms, adding polling:" + pollingCount);
- synchronized (this) {
- mProcessingStartTime = SystemClock.elapsedRealtime();
- releaseTimerLocked();
- mTimer = new Timer();
- mTimer.scheduleAtFixedRate(
- new ShutdownProcessingTimerTask(shuttingDown, pollingCount),
- 0 /*delay*/,
- SHUTDOWN_POLLING_INTERVAL_MS);
- }
- } else {
- PowerHandler handler;
- synchronized (this) {
- handler = mHandler;
- }
- handler.handleProcessingComplete(shuttingDown);
+ private void doHandlePreprocessing() {
+ int pollingCount = (sShutdownPrepareTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
+ Log.i(CarLog.TAG_POWER, "processing before shutdown expected for: "
+ + sShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);
+ synchronized (this) {
+ mProcessingStartTime = SystemClock.elapsedRealtime();
+ releaseTimerLocked();
+ mTimer = new Timer();
+ mTimer.scheduleAtFixedRate(
+ new ShutdownProcessingTimerTask(pollingCount),
+ 0 /*delay*/,
+ SHUTDOWN_POLLING_INTERVAL_MS);
}
}
- private long sendPowerManagerEvent(boolean shuttingDown) {
- long processingTimeMs = 0;
- int newState = shuttingDown ? CarPowerStateListener.SHUTDOWN_ENTER :
- CarPowerStateListener.SUSPEND_ENTER;
+ private void sendPowerManagerEvent(int newState, boolean useTokens) {
synchronized (mPowerManagerListenerTokens) {
- mPowerManagerListenerTokens.clear();
+ if (useTokens) {
+ mPowerManagerListenerTokens.clear();
+ }
int i = mPowerManagerListeners.beginBroadcast();
while (i-- > 0) {
try {
+ int token = 0;
ICarPowerStateListener listener = mPowerManagerListeners.getBroadcastItem(i);
- listener.onStateChanged(newState, mTokenValue);
- mPowerManagerListenerTokens.put(listener.asBinder(), mTokenValue);
- mTokenValue++;
+ if (useTokens) {
+ listener.onStateChanged(newState, mTokenValue);
+ mPowerManagerListenerTokens.put(listener.asBinder(), mTokenValue);
+ mTokenValue++;
+ } else {
+ listener.onStateChanged(newState, 0);
+ }
} catch (RemoteException e) {
// Its likely the connection snapped. Let binder death handle the situation.
Log.e(CarLog.TAG_POWER, "onStateChanged calling failed: " + e);
}
}
mPowerManagerListeners.finishBroadcast();
- if (!mPowerManagerListenerTokens.isEmpty()) {
- Log.i(CarLog.TAG_POWER,
- "mPowerManagerListenerTokens not empty, add APP_EXTEND_MAX_MS");
- processingTimeMs += APP_EXTEND_MAX_MS;
- }
}
- return processingTimeMs;
}
private void doHandleDeepSleep() {
@@ -355,61 +363,48 @@
handler = mHandler;
}
handler.cancelProcessingComplete();
- mHal.sendSleepEntry();
synchronized (this) {
mLastSleepEntryTime = SystemClock.elapsedRealtime();
}
- if (!mSystemInterface.enterDeepSleep(mNextWakeupSec)) {
- // System did not suspend. Need to shutdown
- // TODO: Shutdown gracefully
+ if (!mSystemInterface.enterDeepSleep()) {
+ // System did not suspend. VHAL should transition CPMS to shutdown.
Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Need to shutdown");
}
- // When we wake up, we reset the next wake up time and if no one will set it
- // System will suspend / shutdown forever.
+ // On wake, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
mNextWakeupSec = 0;
- mHal.sendSleepExit();
- // Notify applications
- int i = mPowerManagerListeners.beginBroadcast();
- while (i-- > 0) {
- try {
- ICarPowerStateListener listener = mPowerManagerListeners.getBroadcastItem(i);
- listener.onStateChanged(CarPowerStateListener.SUSPEND_EXIT, 0);
- } catch (RemoteException e) {
- // Its likely the connection snapped. Let binder death handle the situation.
- Log.e(CarLog.TAG_POWER, "onStateChanged calling failed: " + e);
- }
- }
- mPowerManagerListeners.finishBroadcast();
- if (mSystemInterface.isWakeupCausedByTimer()) {
- doHandlePreprocessing(false /*shuttingDown*/);
- } else {
- PowerState currentState = mHal.getCurrentPowerState();
- if (currentState != null && needPowerStateChange(currentState)) {
- onApPowerStateChange(currentState);
- } else { // power controller woke-up but no power state change. Just shutdown.
- Log.w(CarLog.TAG_POWER, "external sleep wake up, but no power state change:" +
- currentState);
- doHandleShutdown();
- }
- }
+ onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, CarPowerStateListener.SUSPEND_EXIT);
}
- private void doHandleNotifyPowerOn() {
- boolean displayOn = false;
- synchronized (this) {
- if (mCurrentState != null && mCurrentState.mState == PowerHalService.STATE_ON_FULL) {
- displayOn = true;
- }
- }
- }
-
- private boolean needPowerStateChange(PowerState newState) {
- synchronized (this) {
- if (mCurrentState != null && mCurrentState.equals(newState)) {
- return false;
- }
+ private boolean needPowerStateChangeLocked(CpmsState newState) {
+ if (newState == null) {
+ return false;
+ } else if (mCurrentState == null) {
return true;
+ } else if (mCurrentState.equals(newState)) {
+ return false;
+ }
+
+ // The following switch/case enforces the allowed state transitions.
+ switch (mCurrentState.mState) {
+ case CpmsState.WAIT_FOR_VHAL:
+ return (newState.mState == CpmsState.ON)
+ || (newState.mState == CpmsState.SHUTDOWN_PREPARE);
+ case CpmsState.SUSPEND:
+ return newState.mState == CpmsState.WAIT_FOR_VHAL;
+ case CpmsState.ON:
+ return newState.mState == CpmsState.SHUTDOWN_PREPARE;
+ case CpmsState.SHUTDOWN_PREPARE:
+ // If VHAL sends SHUTDOWN_IMMEDIATELY while in SHUTDOWN_PREPARE state, do it.
+ return ((newState.mState == CpmsState.SHUTDOWN_PREPARE) && !newState.mCanPostpone)
+ || (newState.mState == CpmsState.WAIT_FOR_FINISH)
+ || (newState.mState == CpmsState.WAIT_FOR_VHAL);
+ case CpmsState.WAIT_FOR_FINISH:
+ return newState.mState == CpmsState.SUSPEND;
+ default:
+ Log.e(CarLog.TAG_POWER, "Unhandled state transition: currentState="
+ + mCurrentState.mState + ", newState=" + newState.mState);
+ return false;
}
}
@@ -419,24 +414,21 @@
mSystemInterface.shutdown();
}
- private void doHandleProcessingComplete(boolean shutdownWhenCompleted) {
+ private void doHandleProcessingComplete() {
synchronized (this) {
releaseTimerLocked();
- if (!shutdownWhenCompleted && mLastSleepEntryTime > mProcessingStartTime) {
+ if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {
// entered sleep after processing start. So this could be duplicate request.
Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
return;
}
}
- if (shutdownWhenCompleted) {
- doHandleShutdown();
- } else {
- doHandleDeepSleep();
- }
- }
- private synchronized void setCurrentState(PowerState state) {
- mCurrentState = state;
+ if (mShutdownOnFinish) {
+ onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SHUTDOWN_ENTER);
+ } else {
+ onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SUSPEND_ENTER);
+ }
}
@Override
@@ -481,6 +473,8 @@
public void registerListener(ICarPowerStateListener listener) {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
mPowerManagerListeners.register(listener);
+ // TODO: Need to send current state to newly registered listener? If so, need to handle
+ // token for SHUTDOWN_PREPARE state
}
@Override
@@ -491,7 +485,6 @@
private void doUnregisterListener(ICarPowerStateListener listener) {
boolean found = mPowerManagerListeners.unregister(listener);
-
if (found) {
// Remove outstanding token if there is one
IBinder binder = listener.asBinder();
@@ -507,14 +500,7 @@
@Override
public void requestShutdownOnNextSuspend() {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
- mShutdownOnNextSuspend = true;
- }
-
- @Override
- public int getBootReason() {
- ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
- // Return the most recent bootReason value
- return mBootReason;
+ mShutdownOnFinish = true;
}
@Override
@@ -544,22 +530,30 @@
if (currentToken == token) {
mPowerManagerListenerTokens.remove(binder);
if (mPowerManagerListenerTokens.isEmpty() &&
- (mCurrentState.mState == PowerHalService.STATE_SHUTDOWN_PREPARE)) {
+ (mCurrentState.mState == CpmsState.SHUTDOWN_PREPARE)) {
+ PowerHandler powerHandler;
// All apps are ready to shutdown/suspend.
- Log.i(CarLog.TAG_POWER,
- "Apps are finished, call notifyPowerEventProcessingCompletion");
- notifyPowerEventProcessingCompletion();
+ synchronized (this) {
+ if (!mShutdownOnFinish) {
+ if (mLastSleepEntryTime > mProcessingStartTime
+ && mLastSleepEntryTime < SystemClock.elapsedRealtime()) {
+ Log.i(CarLog.TAG_POWER, "finishedLocked: Already slept!");
+ return;
+ }
+ }
+ powerHandler = mHandler;
+ }
+ Log.i(CarLog.TAG_POWER, "Apps are finished, call handleProcessingComplete()");
+ powerHandler.handleProcessingComplete();
}
}
}
private class PowerHandler extends Handler {
-
private final int MSG_POWER_STATE_CHANGE = 0;
private final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1;
private final int MSG_MAIN_DISPLAY_STATE_CHANGE = 2;
private final int MSG_PROCESSING_COMPLETE = 3;
- private final int MSG_NOTIFY_POWER_ON = 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.
@@ -585,14 +579,9 @@
sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS);
}
- private void handleProcessingComplete(boolean shutdownWhenCompleted) {
+ private void handleProcessingComplete() {
removeMessages(MSG_PROCESSING_COMPLETE);
- Message msg = obtainMessage(MSG_PROCESSING_COMPLETE, shutdownWhenCompleted ? 1 : 0, 0);
- sendMessage(msg);
- }
-
- private void handlePowerOn() {
- Message msg = obtainMessage(MSG_NOTIFY_POWER_ON);
+ Message msg = obtainMessage(MSG_PROCESSING_COMPLETE);
sendMessage(msg);
}
@@ -605,7 +594,6 @@
removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE);
removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
removeMessages(MSG_PROCESSING_COMPLETE);
- removeMessages(MSG_NOTIFY_POWER_ON);
}
@Override
@@ -621,22 +609,17 @@
doHandleMainDisplayStateChange((Boolean) msg.obj);
break;
case MSG_PROCESSING_COMPLETE:
- doHandleProcessingComplete(msg.arg1 == 1);
- break;
- case MSG_NOTIFY_POWER_ON:
- doHandleNotifyPowerOn();
+ doHandleProcessingComplete();
break;
}
}
}
private class ShutdownProcessingTimerTask extends TimerTask {
- private final boolean mShutdownWhenCompleted;
private final int mExpirationCount;
private int mCurrentCount;
- private ShutdownProcessingTimerTask(boolean shutdownWhenCompleted, int expirationCount) {
- mShutdownWhenCompleted = shutdownWhenCompleted;
+ private ShutdownProcessingTimerTask(int expirationCount) {
mExpirationCount = expirationCount;
mCurrentCount = 0;
}
@@ -650,10 +633,124 @@
releaseTimerLocked();
handler = mHandler;
}
- handler.handleProcessingComplete(mShutdownWhenCompleted);
+ handler.handleProcessingComplete();
} else {
mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);
}
}
}
+
+ private static class CpmsState {
+ 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;
+
+ /* Config values from AP_POWER_STATE_REQ */
+ public final boolean mCanPostpone;
+ public final boolean mCanSleep;
+ /* Message sent to CarPowerStateListener in response to this state */
+ 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.mCanSleep = false;
+ this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(ON);
+ this.mState = ON;
+ break;
+ case VehicleApPowerStateReq.SHUTDOWN_PREPARE:
+ this.mCanPostpone = halPowerState.canPostponeShutdown();
+ this.mCanSleep = halPowerState.canEnterDeepSleep();
+ this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(
+ SHUTDOWN_PREPARE);
+ this.mState = SHUTDOWN_PREPARE;
+ break;
+ case VehicleApPowerStateReq.CANCEL_SHUTDOWN:
+ this.mCanPostpone = false;
+ this.mCanSleep = false;
+ this.mCarPowerStateListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
+ this.mState = WAIT_FOR_VHAL;
+ break;
+ case VehicleApPowerStateReq.FINISHED:
+ this.mCanPostpone = false;
+ this.mCanSleep = false;
+ this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(SUSPEND);
+ this.mState = SUSPEND;
+ break;
+ default:
+ // Illegal state from PowerState. Throw an exception?
+ this.mCanPostpone = false;
+ this.mCanSleep = false;
+ this.mCarPowerStateListenerState = 0;
+ this.mState = 0;
+ break;
+ }
+ }
+
+ CpmsState(int state) {
+ this(state, cpmsStateToPowerStateListenerState(state));
+ }
+
+ CpmsState(int state, int carPowerStateListenerState) {
+ this.mCanPostpone = false;
+ this.mCanSleep = false;
+ this.mCarPowerStateListenerState = carPowerStateListenerState;
+ this.mState = state;
+ }
+
+ private static int cpmsStateToPowerStateListenerState(int state) {
+ int powerStateListenerState = 0;
+
+ // Set the CarPowerStateListenerState based on current state
+ switch (state) {
+ case ON:
+ powerStateListenerState = CarPowerStateListener.ON;
+ break;
+ case SHUTDOWN_PREPARE:
+ powerStateListenerState = CarPowerStateListener.SHUTDOWN_PREPARE;
+ break;
+ case SUSPEND:
+ powerStateListenerState = CarPowerStateListener.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.mCanSleep == that.mCanSleep
+ && this.mCanPostpone == that.mCanPostpone
+ && this.mCarPowerStateListenerState == that.mCarPowerStateListenerState;
+ }
+
+ @Override
+ public String toString() {
+ return "CpmsState canSleep:" + mCanSleep + ", canPostpone=" + mCanPostpone
+ + ", carPowerStateListenerState=" + mCarPowerStateListenerState
+ + ", CpmsState=" + mState;
+ }
+ }
+
}
diff --git a/service/src/com/android/car/garagemode/Controller.java b/service/src/com/android/car/garagemode/Controller.java
index e9b2888..e08ad2f 100644
--- a/service/src/com/android/car/garagemode/Controller.java
+++ b/service/src/com/android/car/garagemode/Controller.java
@@ -101,11 +101,15 @@
break;
case CarPowerStateListener.SHUTDOWN_ENTER:
LOG.d("CPM state changed to SHUTDOWN_ENTER");
- handleShutdownEnter(future);
+ handleShutdownEnter();
+ break;
+ case CarPowerStateListener.SHUTDOWN_PREPARE:
+ LOG.d("CPM state changed to SHUTDOWN_PREPARE");
+ handleShutdownPrepare(future);
break;
case CarPowerStateListener.SUSPEND_ENTER:
LOG.d("CPM state changed to SUSPEND_ENTER");
- handleSuspendEnter(future);
+ handleSuspendEnter();
break;
case CarPowerStateListener.SUSPEND_EXIT:
LOG.d("CPM state changed to SUSPEND_EXIT");
@@ -190,11 +194,15 @@
resetGarageMode();
}
- private void handleSuspendEnter(CompletableFuture<Void> future) {
- initiateGarageMode(future);
+ private void handleSuspendEnter() {
+ resetGarageMode();
}
- private void handleShutdownEnter(CompletableFuture<Void> future) {
+ private void handleShutdownEnter() {
+ resetGarageMode();
+ }
+
+ private void handleShutdownPrepare(CompletableFuture<Void> future) {
initiateGarageMode(future);
}
diff --git a/service/src/com/android/car/hal/PowerHalService.java b/service/src/com/android/car/hal/PowerHalService.java
index 504fbf2..3972984 100644
--- a/service/src/com/android/car/hal/PowerHalService.java
+++ b/service/src/com/android/car/hal/PowerHalService.java
@@ -16,13 +16,11 @@
package com.android.car.hal;
-import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AP_POWER_BOOTUP_REASON;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AP_POWER_STATE_REPORT;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AP_POWER_STATE_REQ;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.DISPLAY_BRIGHTNESS;
import android.annotation.Nullable;
-import android.hardware.automotive.vehicle.V2_0.VehicleApPowerBootupReason;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateConfigFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReport;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
@@ -43,23 +41,11 @@
import java.util.List;
public class PowerHalService extends HalServiceBase {
- // AP Power State constants set by HAL implementation
- public static final int STATE_OFF = VehicleApPowerStateReq.OFF;
- public static final int STATE_DEEP_SLEEP = VehicleApPowerStateReq.DEEP_SLEEP;
- public static final int STATE_ON_DISP_OFF = VehicleApPowerStateReq.ON_DISP_OFF;
- public static final int STATE_ON_FULL = VehicleApPowerStateReq.ON_FULL;
- public static final int STATE_SHUTDOWN_PREPARE = VehicleApPowerStateReq.SHUTDOWN_PREPARE;
-
- // Boot reason set by VMCU
- public static final int BOOT_REASON_USER_POWER_ON = VehicleApPowerBootupReason.USER_POWER_ON;
- public static final int BOOT_REASON_USER_UNLOCK = VehicleApPowerBootupReason.USER_UNLOCK;
- public static final int BOOT_REASON_TIMER = VehicleApPowerBootupReason.TIMER;
-
// Set display brightness from 0-100%
public static final int MAX_BRIGHTNESS = 100;
@VisibleForTesting
- public static final int SET_BOOT_COMPLETE = VehicleApPowerStateReport.BOOT_COMPLETE;
+ public static final int SET_WAIT_FOR_VHAL = VehicleApPowerStateReport.WAIT_FOR_VHAL;
@VisibleForTesting
public static final int SET_DEEP_SLEEP_ENTRY = VehicleApPowerStateReport.DEEP_SLEEP_ENTRY;
@VisibleForTesting
@@ -69,9 +55,11 @@
@VisibleForTesting
public static final int SET_SHUTDOWN_START = VehicleApPowerStateReport.SHUTDOWN_START;
@VisibleForTesting
- public static final int SET_DISPLAY_ON = VehicleApPowerStateReport.DISPLAY_ON;
+ public static final int SET_ON = VehicleApPowerStateReport.ON;
@VisibleForTesting
- public static final int SET_DISPLAY_OFF = VehicleApPowerStateReport.DISPLAY_OFF;
+ public static final int SET_SHUTDOWN_PREPARE = VehicleApPowerStateReport.SHUTDOWN_PREPARE;
+ @VisibleForTesting
+ public static final int SET_SHUTDOWN_CANCELLED = VehicleApPowerStateReport.SHUTDOWN_CANCELLED;
@VisibleForTesting
public static final int SHUTDOWN_CAN_SLEEP = VehicleApPowerStateShutdownParam.CAN_SLEEP;
@@ -92,11 +80,6 @@
* @param brightness in percentile. 100% full.
*/
void onDisplayBrightnessChange(int brightness);
- /**
- * Received boot reason.
- * @param boot reason.
- */
- void onBootReasonReceived(int bootReason);
}
public static final class PowerState {
@@ -118,7 +101,7 @@
* @throws IllegalStateException
*/
public boolean canEnterDeepSleep() {
- if (mState != STATE_SHUTDOWN_PREPARE) {
+ if (mState != VehicleApPowerStateReq.SHUTDOWN_PREPARE) {
throw new IllegalStateException("wrong state");
}
return (mParam == VehicleApPowerStateShutdownParam.CAN_SLEEP);
@@ -131,7 +114,7 @@
* @throws IllegalStateException
*/
public boolean canPostponeShutdown() {
- if (mState != STATE_SHUTDOWN_PREPARE) {
+ if (mState != VehicleApPowerStateReq.SHUTDOWN_PREPARE) {
throw new IllegalStateException("wrong state");
}
return (mParam != VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY);
@@ -180,32 +163,73 @@
}
}
- public void sendBootComplete() {
- Log.i(CarLog.TAG_POWER, "send boot complete");
- setPowerState(VehicleApPowerStateReport.BOOT_COMPLETE, 0);
+ /**
+ * Send WaitForVhal message to VHAL
+ */
+ public void sendWaitForVhal() {
+ Log.i(CarLog.TAG_POWER, "send wait for vhal");
+ setPowerState(VehicleApPowerStateReport.WAIT_FOR_VHAL, 0);
}
- public void sendSleepEntry() {
+ /**
+ * Send SleepEntry message to VHAL
+ * @param wakeupTimeSec Notify VHAL when system wants to be woken from sleep.
+ */
+ public void sendSleepEntry(int wakeupTimeSec) {
Log.i(CarLog.TAG_POWER, "send sleep entry");
- setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_ENTRY, 0);
+ setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_ENTRY, wakeupTimeSec);
}
+ /**
+ * Send SleepExit message to VHAL
+ * Notifies VHAL when SOC has woken.
+ */
public void sendSleepExit() {
Log.i(CarLog.TAG_POWER, "send sleep exit");
setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_EXIT, 0);
}
+ /**
+ * Send Shutdown Postpone message to VHAL
+ */
public void sendShutdownPostpone(int postponeTimeMs) {
Log.i(CarLog.TAG_POWER, "send shutdown postpone, time:" + postponeTimeMs);
setPowerState(VehicleApPowerStateReport.SHUTDOWN_POSTPONE, postponeTimeMs);
}
+ /**
+ * Send Shutdown Start message to VHAL
+ */
public void sendShutdownStart(int wakeupTimeSec) {
Log.i(CarLog.TAG_POWER, "send shutdown start");
setPowerState(VehicleApPowerStateReport.SHUTDOWN_START, wakeupTimeSec);
}
/**
+ * Send On message to VHAL
+ */
+ public void sendOn() {
+ Log.i(CarLog.TAG_POWER, "send on");
+ setPowerState(VehicleApPowerStateReport.ON, 0);
+ }
+
+ /**
+ * Send Shutdown Prepare message to VHAL
+ */
+ public void sendShutdownPrepare() {
+ Log.i(CarLog.TAG_POWER, "send shutdown prepare");
+ setPowerState(VehicleApPowerStateReport.SHUTDOWN_PREPARE, 0);
+ }
+
+ /**
+ * Send Shutdown Cancel message to VHAL
+ */
+ public void sendShutdownCancel() {
+ Log.i(CarLog.TAG_POWER, "send shutdown cancel");
+ setPowerState(VehicleApPowerStateReport.SHUTDOWN_CANCELLED, 0);
+ }
+
+ /**
* Sets the display brightness for the vehicle.
* @param brightness value from 0 to 100.
*/
@@ -223,23 +247,15 @@
}
}
- public void sendDisplayOn() {
- Log.i(CarLog.TAG_POWER, "send display on");
- setPowerState(VehicleApPowerStateReport.DISPLAY_ON, 0);
- }
-
- public void sendDisplayOff() {
- Log.i(CarLog.TAG_POWER, "send display off");
- setPowerState(VehicleApPowerStateReport.DISPLAY_OFF, 0);
- }
-
private void setPowerState(int state, int additionalParam) {
- int[] values = { state, additionalParam };
- try {
- mHal.set(VehicleProperty.AP_POWER_STATE_REPORT, 0).to(values);
- Log.i(CarLog.TAG_POWER, "setPowerState=" + state + " param=" + additionalParam);
- } catch (PropertyTimeoutException e) {
- Log.e(CarLog.TAG_POWER, "cannot set to AP_POWER_STATE_REPORT", e);
+ if (isPowerStateSupported()) {
+ int[] values = { state, additionalParam };
+ try {
+ mHal.set(VehicleProperty.AP_POWER_STATE_REPORT, 0).to(values);
+ Log.i(CarLog.TAG_POWER, "setPowerState=" + state + " param=" + additionalParam);
+ } catch (PropertyTimeoutException e) {
+ Log.e(CarLog.TAG_POWER, "cannot set to AP_POWER_STATE_REPORT", e);
+ }
}
}
@@ -257,8 +273,8 @@
}
public synchronized boolean isPowerStateSupported() {
- VehiclePropConfig config = mProperties.get(VehicleProperty.AP_POWER_STATE_REQ);
- return config != null;
+ return (mProperties.get(VehicleProperty.AP_POWER_STATE_REQ) != null)
+ && (mProperties.get(VehicleProperty.AP_POWER_STATE_REPORT) != null);
}
private synchronized boolean isConfigFlagSet(int flag) {
@@ -308,7 +324,6 @@
Collection<VehiclePropConfig> allProperties) {
for (VehiclePropConfig config : allProperties) {
switch (config.prop) {
- case AP_POWER_BOOTUP_REASON:
case AP_POWER_STATE_REQ:
case AP_POWER_STATE_REPORT:
case DISPLAY_BRIGHTNESS:
@@ -338,10 +353,8 @@
private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) {
for (VehiclePropValue v : values) {
switch (v.prop) {
- case AP_POWER_BOOTUP_REASON:
- int reason = v.value.int32Values.get(0);
- Log.i(CarLog.TAG_POWER, "Received AP_POWER_BOOTUP_REASON=" + reason);
- listener.onBootReasonReceived(reason);
+ case AP_POWER_STATE_REPORT:
+ // Should never see this; write-only property
break;
case AP_POWER_STATE_REQ:
int state = v.value.int32Values.get(VehicleApPowerStateReqIndex.STATE);
diff --git a/service/src/com/android/car/systeminterface/SystemInterface.java b/service/src/com/android/car/systeminterface/SystemInterface.java
index b508988..dd7d961 100644
--- a/service/src/com/android/car/systeminterface/SystemInterface.java
+++ b/service/src/com/android/car/systeminterface/SystemInterface.java
@@ -153,8 +153,8 @@
}
@Override
- public boolean enterDeepSleep(int wakeupTimeSec) {
- return mSystemStateInterface.enterDeepSleep(wakeupTimeSec);
+ public boolean enterDeepSleep() {
+ return mSystemStateInterface.enterDeepSleep();
}
@Override
diff --git a/service/src/com/android/car/systeminterface/SystemStateInterface.java b/service/src/com/android/car/systeminterface/SystemStateInterface.java
index 5fb7185..c963ac0 100644
--- a/service/src/com/android/car/systeminterface/SystemStateInterface.java
+++ b/service/src/com/android/car/systeminterface/SystemStateInterface.java
@@ -16,34 +16,36 @@
package com.android.car.systeminterface;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import com.android.car.procfsinspector.ProcessInfo;
-import com.android.car.procfsinspector.ProcfsInspector;
-import com.android.internal.car.ICarServiceHelper;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
+import com.android.car.procfsinspector.ProcessInfo;
+import com.android.car.procfsinspector.ProcfsInspector;
+import com.android.internal.car.ICarServiceHelper;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
/**
* Interface that abstracts system status (booted, sleeping, ...) operations
*/
public interface SystemStateInterface {
static final String TAG = SystemStateInterface.class.getSimpleName();
void shutdown();
- boolean enterDeepSleep(int sleepDurationSec);
+ /**
+ * Put the device into Suspend to RAM mode
+ * @return boolean true if suspend succeeded
+ */
+ boolean enterDeepSleep();
void scheduleActionForBootCompleted(Runnable action, Duration delay);
default boolean isWakeupCausedByTimer() {
@@ -56,7 +58,7 @@
default boolean isSystemSupportingDeepSleep() {
//TODO should return by checking some kernel suspend control sysfs, bug: 32061842
- return false;
+ return true;
}
default List<ProcessInfo> getRunningProcesses() {
@@ -99,7 +101,7 @@
}
@Override
- public boolean enterDeepSleep(int sleepDurationSec) {
+ public boolean enterDeepSleep() {
boolean deviceEnteredSleep;
//TODO set wake up time via VHAL, bug: 32061842
try {