blob: 6ae33cce8912d4520b33635f3a7f7786c8ebb289 [file] [log] [blame]
keunyoung4b0212c2015-10-29 17:11:57 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car;
17
Steve Paik87b36fd2018-03-13 19:25:47 -070018import android.car.Car;
19import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
20import android.car.hardware.power.ICarPower;
21import android.car.hardware.power.ICarPowerStateListener;
22import android.content.Context;
Steve Paik07db5ed2018-09-24 16:48:52 -070023import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
Steve Paik87b36fd2018-03-13 19:25:47 -070024import android.os.Handler;
25import android.os.HandlerThread;
26import android.os.IBinder;
27import android.os.Looper;
28import android.os.Message;
29import android.os.RemoteCallbackList;
30import android.os.RemoteException;
31import android.os.SystemClock;
32import android.util.Log;
keunyoung4b0212c2015-10-29 17:11:57 -070033
34import com.android.car.hal.PowerHalService;
35import com.android.car.hal.PowerHalService.PowerState;
Enrico Granatab19bc322017-10-12 12:25:06 -070036import com.android.car.systeminterface.SystemInterface;
keunyoung4b0212c2015-10-29 17:11:57 -070037import com.android.internal.annotations.GuardedBy;
Yao Chendacd7242016-01-26 14:42:42 -080038import com.android.internal.annotations.VisibleForTesting;
keunyoung4b0212c2015-10-29 17:11:57 -070039
Steve Paik87b36fd2018-03-13 19:25:47 -070040import java.io.PrintWriter;
41import java.util.LinkedList;
42import java.util.Map;
43import java.util.Timer;
44import java.util.TimerTask;
45import java.util.concurrent.ConcurrentHashMap;
keunyoung4b0212c2015-10-29 17:11:57 -070046
Serik Beketayev1d9e0d82018-09-11 08:19:33 -070047/**
48 * Power Management service class for cars. Controls the power states and interacts with other
49 * parts of the system to ensure its own state.
50 */
51public class CarPowerManagementService extends ICarPower.Stub implements
52 CarServiceBase, PowerHalService.PowerEventListener {
Steve Paik388d7772018-02-12 10:54:51 -080053 private final Context mContext;
keunyoung4b0212c2015-10-29 17:11:57 -070054 private final PowerHalService mHal;
Pavel Maltsev0d07c762016-11-03 16:40:15 -070055 private final SystemInterface mSystemInterface;
Steve Paik03124082018-02-16 11:19:26 -080056 private final PowerManagerCallbackList mPowerManagerListeners = new PowerManagerCallbackList();
57 private final Map<IBinder, Integer> mPowerManagerListenerTokens = new ConcurrentHashMap<>();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080058
59 @GuardedBy("this")
Steve Paik07db5ed2018-09-24 16:48:52 -070060 private CpmsState mCurrentState;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080061 @GuardedBy("this")
keunyoung4b0212c2015-10-29 17:11:57 -070062 private Timer mTimer;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080063 @GuardedBy("this")
64 private long mProcessingStartTime;
65 @GuardedBy("this")
66 private long mLastSleepEntryTime;
67 @GuardedBy("this")
Steve Paik07db5ed2018-09-24 16:48:52 -070068 private final LinkedList<CpmsState> mPendingPowerStates = new LinkedList<>();
Pavel Maltsevddbff982017-03-22 14:49:02 -070069 @GuardedBy("this")
70 private HandlerThread mHandlerThread;
71 @GuardedBy("this")
72 private PowerHandler mHandler;
Steve Paik07db5ed2018-09-24 16:48:52 -070073 private int mNextWakeupSec = 0;
Serik Beketayev1d9e0d82018-09-11 08:19:33 -070074 private int mTokenValue = 1;
Steve Paik07db5ed2018-09-24 16:48:52 -070075 private boolean mShutdownOnFinish = false;
keunyoung4b0212c2015-10-29 17:11:57 -070076
Steve Paik03124082018-02-16 11:19:26 -080077 // TODO: Make this OEM configurable.
Steve Paik07db5ed2018-09-24 16:48:52 -070078 private static final int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
79 private static final int SHUTDOWN_EXTEND_MAX_MS = 5000;
80
81 // Use one hour for now
82 private static int sShutdownPrepareTimeMs = 60 * 60 * 1000;
keunyoung4b0212c2015-10-29 17:11:57 -070083
Steve Paik03124082018-02-16 11:19:26 -080084 private class PowerManagerCallbackList extends RemoteCallbackList<ICarPowerStateListener> {
85 /**
86 * Old version of {@link #onCallbackDied(E, Object)} that
87 * does not provide a cookie.
88 */
89 @Override
90 public void onCallbackDied(ICarPowerStateListener listener) {
91 Log.i(CarLog.TAG_POWER, "binderDied " + listener.asBinder());
92 CarPowerManagementService.this.doUnregisterListener(listener);
93 }
94 }
95
Serik Beketayev1d9e0d82018-09-11 08:19:33 -070096 public CarPowerManagementService(
97 Context context, PowerHalService powerHal, SystemInterface systemInterface) {
Steve Paik388d7772018-02-12 10:54:51 -080098 mContext = context;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080099 mHal = powerHal;
100 mSystemInterface = systemInterface;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800101 }
102
103 /**
104 * Create a dummy instance for unit testing purpose only. Instance constructed in this way
105 * is not safe as members expected to be non-null are null.
Yao Chendacd7242016-01-26 14:42:42 -0800106 */
107 @VisibleForTesting
108 protected CarPowerManagementService() {
Steve Paik388d7772018-02-12 10:54:51 -0800109 mContext = null;
Yao Chendacd7242016-01-26 14:42:42 -0800110 mHal = null;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800111 mSystemInterface = null;
Yao Chendacd7242016-01-26 14:42:42 -0800112 mHandlerThread = null;
Keun-young Parkca013ae2016-02-10 16:22:25 -0800113 mHandler = new PowerHandler(Looper.getMainLooper());
Yao Chendacd7242016-01-26 14:42:42 -0800114 }
115
Steve Paik07db5ed2018-09-24 16:48:52 -0700116 @VisibleForTesting
117 protected static void setShutdownPrepareTimeout(int timeoutMs) {
118 // Override the timeout to keep testing time short
119 if (timeoutMs < SHUTDOWN_EXTEND_MAX_MS) {
120 sShutdownPrepareTimeMs = SHUTDOWN_EXTEND_MAX_MS;
121 } else {
122 sShutdownPrepareTimeMs = timeoutMs;
123 }
124 }
125
keunyoung4b0212c2015-10-29 17:11:57 -0700126 @Override
127 public void init() {
Pavel Maltsevddbff982017-03-22 14:49:02 -0700128 synchronized (this) {
129 mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
130 mHandlerThread.start();
131 mHandler = new PowerHandler(mHandlerThread.getLooper());
132 }
133
keunyoung4b0212c2015-10-29 17:11:57 -0700134 mHal.setListener(this);
135 if (mHal.isPowerStateSupported()) {
Steve Paik07db5ed2018-09-24 16:48:52 -0700136 // Initialize CPMS in WAIT_FOR_VHAL state
137 onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, CarPowerStateListener.WAIT_FOR_VHAL);
keunyoung4b0212c2015-10-29 17:11:57 -0700138 } else {
139 Log.w(CarLog.TAG_POWER, "Vehicle hal does not support power state yet.");
Steve Paik07db5ed2018-09-24 16:48:52 -0700140 onApPowerStateChange(CpmsState.ON, CarPowerStateListener.ON);
keunyoung4b0212c2015-10-29 17:11:57 -0700141 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800142 mSystemInterface.startDisplayStateMonitoring(this);
keunyoung4b0212c2015-10-29 17:11:57 -0700143 }
144
145 @Override
146 public void release() {
Pavel Maltsevddbff982017-03-22 14:49:02 -0700147 HandlerThread handlerThread;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800148 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700149 releaseTimerLocked();
150 mCurrentState = null;
Pavel Maltsevddbff982017-03-22 14:49:02 -0700151 mHandler.cancelAll();
152 handlerThread = mHandlerThread;
153 }
154 handlerThread.quitSafely();
155 try {
156 handlerThread.join(1000);
157 } catch (InterruptedException e) {
158 Log.e(CarLog.TAG_POWER, "Timeout while joining for handler thread to join.");
keunyoung4b0212c2015-10-29 17:11:57 -0700159 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800160 mSystemInterface.stopDisplayStateMonitoring();
Steve Paik03124082018-02-16 11:19:26 -0800161 mPowerManagerListeners.kill();
162 mPowerManagerListenerTokens.clear();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800163 mSystemInterface.releaseAllWakeLocks();
keunyoung4b0212c2015-10-29 17:11:57 -0700164 }
165
keunyoung4b0212c2015-10-29 17:11:57 -0700166 @Override
167 public void dump(PrintWriter writer) {
168 writer.println("*PowerManagementService*");
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800169 writer.print("mCurrentState:" + mCurrentState);
170 writer.print(",mProcessingStartTime:" + mProcessingStartTime);
Steve Paik07db5ed2018-09-24 16:48:52 -0700171 writer.print(",mLastSleepEntryTime:" + mLastSleepEntryTime);
172 writer.print(",mNextWakeupSec:" + mNextWakeupSec);
173 writer.print(",mTokenValue:" + mTokenValue);
174 writer.println(",mShutdownOnFinish:" + mShutdownOnFinish);
Steve Paik110f95f2017-11-22 14:44:05 -0800175 }
176
177 @Override
keunyoung4b0212c2015-10-29 17:11:57 -0700178 public void onApPowerStateChange(PowerState state) {
Pavel Maltsevddbff982017-03-22 14:49:02 -0700179 PowerHandler handler;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800180 synchronized (this) {
Steve Paik07db5ed2018-09-24 16:48:52 -0700181 mPendingPowerStates.addFirst(new CpmsState(state));
182 handler = mHandler;
183 }
184 handler.handlePowerStateChange();
185 }
186
187 /**
188 * Initiate state change from CPMS directly.
189 */
190 private void onApPowerStateChange(int apState, int carPowerStateListenerState) {
191 CpmsState newState = new CpmsState(apState, carPowerStateListenerState);
192 PowerHandler handler;
193 synchronized (this) {
194 mPendingPowerStates.addFirst(newState);
Pavel Maltsevddbff982017-03-22 14:49:02 -0700195 handler = mHandler;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800196 }
Pavel Maltsevddbff982017-03-22 14:49:02 -0700197 handler.handlePowerStateChange();
keunyoung4b0212c2015-10-29 17:11:57 -0700198 }
199
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800200 private void doHandlePowerStateChange() {
Steve Paik07db5ed2018-09-24 16:48:52 -0700201 CpmsState state;
Pavel Maltsevddbff982017-03-22 14:49:02 -0700202 PowerHandler handler;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800203 synchronized (this) {
204 state = mPendingPowerStates.peekFirst();
205 mPendingPowerStates.clear();
206 if (state == null) {
207 return;
208 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700209 Log.i(CarLog.TAG_POWER, "doHandlePowerStateChange: newState=" + state.mState);
210 if (!needPowerStateChangeLocked(state)) {
211 Log.d(CarLog.TAG_POWER, "doHandlePowerStateChange no change needed");
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800212 return;
213 }
214 // now real power change happens. Whatever was queued before should be all cancelled.
215 releaseTimerLocked();
Pavel Maltsevddbff982017-03-22 14:49:02 -0700216 handler = mHandler;
keunyoung4b0212c2015-10-29 17:11:57 -0700217 }
Pavel Maltsevddbff982017-03-22 14:49:02 -0700218 handler.cancelProcessingComplete();
Steve Paik07db5ed2018-09-24 16:48:52 -0700219 Log.i(CarLog.TAG_POWER, "setCurrentState " + state.toString());
220 mCurrentState = state;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700221 switch (state.mState) {
Steve Paik07db5ed2018-09-24 16:48:52 -0700222 case CpmsState.WAIT_FOR_VHAL:
223 handleWaitForVhal(state);
keunyoung4b0212c2015-10-29 17:11:57 -0700224 break;
Steve Paik07db5ed2018-09-24 16:48:52 -0700225 case CpmsState.ON:
226 handleOn();
keunyoung4b0212c2015-10-29 17:11:57 -0700227 break;
Steve Paik07db5ed2018-09-24 16:48:52 -0700228 case CpmsState.SHUTDOWN_PREPARE:
keunyoung4b0212c2015-10-29 17:11:57 -0700229 handleShutdownPrepare(state);
230 break;
Steve Paik07db5ed2018-09-24 16:48:52 -0700231 case CpmsState.WAIT_FOR_FINISH:
232 handleWaitForFinish(state);
233 break;
234 case CpmsState.SUSPEND:
235 // Received FINISH from VHAL
236 handleFinish();
237 break;
238 default:
239 // Illegal state
240 // TODO: Throw exception?
241 break;
keunyoung4b0212c2015-10-29 17:11:57 -0700242 }
243 }
244
Steve Paik07db5ed2018-09-24 16:48:52 -0700245 private void handleWaitForVhal(CpmsState state) {
246 int carPowerStateListenerState = state.mCarPowerStateListenerState;
247 sendPowerManagerEvent(carPowerStateListenerState, false);
248 // Inspect CarPowerStateListenerState to decide which message to send via VHAL
249 switch (carPowerStateListenerState) {
250 case CarPowerStateListener.WAIT_FOR_VHAL:
251 mHal.sendWaitForVhal();
252 break;
253 case CarPowerStateListener.SHUTDOWN_CANCELLED:
254 mHal.sendShutdownCancel();
255 break;
256 case CarPowerStateListener.SUSPEND_EXIT:
257 mHal.sendSleepExit();
258 break;
259 }
keunyoung4b0212c2015-10-29 17:11:57 -0700260 }
261
Steve Paik07db5ed2018-09-24 16:48:52 -0700262 private void handleOn() {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800263 mSystemInterface.setDisplayState(true);
Steve Paik07db5ed2018-09-24 16:48:52 -0700264 sendPowerManagerEvent(CarPowerStateListener.ON, false);
265 mHal.sendOn();
keunyoung4b0212c2015-10-29 17:11:57 -0700266 }
267
Steve Paik07db5ed2018-09-24 16:48:52 -0700268 private void handleShutdownPrepare(CpmsState newState) {
269 mSystemInterface.setDisplayState(false);
270 // Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
271 mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
272 || !mSystemInterface.isSystemSupportingDeepSleep()
273 || !newState.mCanSleep;
274 if (newState.mCanPostpone) {
275 Log.i(CarLog.TAG_POWER, "starting shutdown postpone");
276 sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE, true);
277 mHal.sendShutdownPrepare();
278 doHandlePreprocessing();
keunyoung4b0212c2015-10-29 17:11:57 -0700279 } else {
280 Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800281 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700282 releaseTimerLocked();
283 }
284 doHandleShutdown();
285 }
286 }
287
Steve Paik07db5ed2018-09-24 16:48:52 -0700288 private void handleWaitForFinish(CpmsState state) {
289 sendPowerManagerEvent(state.mCarPowerStateListenerState, false);
290 switch (state.mCarPowerStateListenerState) {
291 case CarPowerStateListener.SUSPEND_ENTER:
292 mHal.sendSleepEntry(mNextWakeupSec);
293 break;
294 case CarPowerStateListener.SHUTDOWN_ENTER:
295 mHal.sendShutdownStart(mNextWakeupSec);
296 break;
297 }
298 }
299
300 private void handleFinish() {
301 if (mShutdownOnFinish) {
302 doHandleShutdown();
303 } else {
304 doHandleDeepSleep();
305 }
306 }
307
Andreas Gampe985ca2f2018-02-09 12:57:54 -0800308 @GuardedBy("this")
keunyoung4b0212c2015-10-29 17:11:57 -0700309 private void releaseTimerLocked() {
310 if (mTimer != null) {
311 mTimer.cancel();
312 }
313 mTimer = null;
314 }
315
Steve Paik07db5ed2018-09-24 16:48:52 -0700316 private void doHandlePreprocessing() {
317 int pollingCount = (sShutdownPrepareTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
318 Log.i(CarLog.TAG_POWER, "processing before shutdown expected for: "
319 + sShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);
320 synchronized (this) {
321 mProcessingStartTime = SystemClock.elapsedRealtime();
322 releaseTimerLocked();
323 mTimer = new Timer();
324 mTimer.scheduleAtFixedRate(
325 new ShutdownProcessingTimerTask(pollingCount),
326 0 /*delay*/,
327 SHUTDOWN_POLLING_INTERVAL_MS);
keunyoung4b0212c2015-10-29 17:11:57 -0700328 }
329 }
330
Steve Paik07db5ed2018-09-24 16:48:52 -0700331 private void sendPowerManagerEvent(int newState, boolean useTokens) {
Steve Paik03124082018-02-16 11:19:26 -0800332 synchronized (mPowerManagerListenerTokens) {
Steve Paik07db5ed2018-09-24 16:48:52 -0700333 if (useTokens) {
334 mPowerManagerListenerTokens.clear();
335 }
Steve Paik03124082018-02-16 11:19:26 -0800336 int i = mPowerManagerListeners.beginBroadcast();
337 while (i-- > 0) {
338 try {
Steve Paik07db5ed2018-09-24 16:48:52 -0700339 int token = 0;
Steve Paik03124082018-02-16 11:19:26 -0800340 ICarPowerStateListener listener = mPowerManagerListeners.getBroadcastItem(i);
Steve Paik07db5ed2018-09-24 16:48:52 -0700341 if (useTokens) {
342 listener.onStateChanged(newState, mTokenValue);
343 mPowerManagerListenerTokens.put(listener.asBinder(), mTokenValue);
344 mTokenValue++;
345 } else {
346 listener.onStateChanged(newState, 0);
347 }
Steve Paik03124082018-02-16 11:19:26 -0800348 } catch (RemoteException e) {
349 // Its likely the connection snapped. Let binder death handle the situation.
350 Log.e(CarLog.TAG_POWER, "onStateChanged calling failed: " + e);
351 }
352 }
353 mPowerManagerListeners.finishBroadcast();
Steve Paik03124082018-02-16 11:19:26 -0800354 }
Steve Paik03124082018-02-16 11:19:26 -0800355 }
356
keunyoung4b0212c2015-10-29 17:11:57 -0700357 private void doHandleDeepSleep() {
Keun-young Park9d4cf622016-08-04 13:24:28 -0700358 // keep holding partial wakelock to prevent entering sleep before enterDeepSleep call
359 // enterDeepSleep should force sleep entry even if wake lock is kept.
360 mSystemInterface.switchToPartialWakeLock();
Pavel Maltsevddbff982017-03-22 14:49:02 -0700361 PowerHandler handler;
362 synchronized (this) {
363 handler = mHandler;
364 }
365 handler.cancelProcessingComplete();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800366 synchronized (this) {
367 mLastSleepEntryTime = SystemClock.elapsedRealtime();
368 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700369 if (!mSystemInterface.enterDeepSleep()) {
370 // System did not suspend. VHAL should transition CPMS to shutdown.
Steve Paik03124082018-02-16 11:19:26 -0800371 Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Need to shutdown");
Steve Paik0f9fc002018-02-09 17:42:00 -0800372 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700373 // On wake, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
Serik Beketayev6e4c7822018-08-27 08:20:09 -0700374 mNextWakeupSec = 0;
Steve Paik03124082018-02-16 11:19:26 -0800375
Steve Paik07db5ed2018-09-24 16:48:52 -0700376 onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, CarPowerStateListener.SUSPEND_EXIT);
keunyoung4b0212c2015-10-29 17:11:57 -0700377 }
378
Steve Paik07db5ed2018-09-24 16:48:52 -0700379 private boolean needPowerStateChangeLocked(CpmsState newState) {
380 if (newState == null) {
381 return false;
382 } else if (mCurrentState == null) {
keunyoung4b0212c2015-10-29 17:11:57 -0700383 return true;
Steve Paik07db5ed2018-09-24 16:48:52 -0700384 } else if (mCurrentState.equals(newState)) {
385 return false;
386 }
387
388 // The following switch/case enforces the allowed state transitions.
389 switch (mCurrentState.mState) {
390 case CpmsState.WAIT_FOR_VHAL:
391 return (newState.mState == CpmsState.ON)
392 || (newState.mState == CpmsState.SHUTDOWN_PREPARE);
393 case CpmsState.SUSPEND:
394 return newState.mState == CpmsState.WAIT_FOR_VHAL;
395 case CpmsState.ON:
396 return newState.mState == CpmsState.SHUTDOWN_PREPARE;
397 case CpmsState.SHUTDOWN_PREPARE:
398 // If VHAL sends SHUTDOWN_IMMEDIATELY while in SHUTDOWN_PREPARE state, do it.
399 return ((newState.mState == CpmsState.SHUTDOWN_PREPARE) && !newState.mCanPostpone)
400 || (newState.mState == CpmsState.WAIT_FOR_FINISH)
401 || (newState.mState == CpmsState.WAIT_FOR_VHAL);
402 case CpmsState.WAIT_FOR_FINISH:
403 return newState.mState == CpmsState.SUSPEND;
404 default:
405 Log.e(CarLog.TAG_POWER, "Unhandled state transition: currentState="
406 + mCurrentState.mState + ", newState=" + newState.mState);
407 return false;
keunyoung4b0212c2015-10-29 17:11:57 -0700408 }
409 }
410
keunyoung4b0212c2015-10-29 17:11:57 -0700411 private void doHandleShutdown() {
412 // now shutdown
Serik Beketayev6e4c7822018-08-27 08:20:09 -0700413 mHal.sendShutdownStart(mHal.isTimedWakeupAllowed() ? mNextWakeupSec : 0);
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700414 mSystemInterface.shutdown();
keunyoung4b0212c2015-10-29 17:11:57 -0700415 }
416
Steve Paik07db5ed2018-09-24 16:48:52 -0700417 private void doHandleProcessingComplete() {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800418 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700419 releaseTimerLocked();
Steve Paik07db5ed2018-09-24 16:48:52 -0700420 if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800421 // entered sleep after processing start. So this could be duplicate request.
422 Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
423 return;
424 }
keunyoung4b0212c2015-10-29 17:11:57 -0700425 }
keunyoung4b0212c2015-10-29 17:11:57 -0700426
Steve Paik07db5ed2018-09-24 16:48:52 -0700427 if (mShutdownOnFinish) {
428 onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SHUTDOWN_ENTER);
429 } else {
430 onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SUSPEND_ENTER);
431 }
keunyoung4b0212c2015-10-29 17:11:57 -0700432 }
433
434 @Override
435 public void onDisplayBrightnessChange(int brightness) {
Steve Paik87b36fd2018-03-13 19:25:47 -0700436 PowerHandler handler;
437 synchronized (this) {
438 handler = mHandler;
439 }
440 handler.handleDisplayBrightnessChange(brightness);
keunyoung4b0212c2015-10-29 17:11:57 -0700441 }
442
443 private void doHandleDisplayBrightnessChange(int brightness) {
Steve Paik87b36fd2018-03-13 19:25:47 -0700444 mSystemInterface.setDisplayBrightness(brightness);
keunyoung4b0212c2015-10-29 17:11:57 -0700445 }
446
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800447 private void doHandleMainDisplayStateChange(boolean on) {
Steve Paik47cdf7b2018-03-16 17:27:43 -0700448 Log.w(CarLog.TAG_POWER, "Unimplemented: doHandleMainDisplayStateChange() - on = " + on);
keunyoung4b0212c2015-10-29 17:11:57 -0700449 }
450
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800451 public void handleMainDisplayChanged(boolean on) {
Pavel Maltsevddbff982017-03-22 14:49:02 -0700452 PowerHandler handler;
453 synchronized (this) {
454 handler = mHandler;
455 }
456 handler.handleMainDisplayStateChange(on);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800457 }
458
Steve Paik47cdf7b2018-03-16 17:27:43 -0700459 /**
460 * Send display brightness to VHAL.
461 * @param brightness value 0-100%
462 */
463 public void sendDisplayBrightness(int brightness) {
464 mHal.sendDisplayBrightness(brightness);
465 }
466
Pavel Maltsevddbff982017-03-22 14:49:02 -0700467 public synchronized Handler getHandler() {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800468 return mHandler;
469 }
470
Steve Paik388d7772018-02-12 10:54:51 -0800471 // Binder interface for CarPowerManager
472 @Override
473 public void registerListener(ICarPowerStateListener listener) {
474 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
Steve Paik03124082018-02-16 11:19:26 -0800475 mPowerManagerListeners.register(listener);
Steve Paik07db5ed2018-09-24 16:48:52 -0700476 // TODO: Need to send current state to newly registered listener? If so, need to handle
477 // token for SHUTDOWN_PREPARE state
Steve Paik388d7772018-02-12 10:54:51 -0800478 }
479
480 @Override
481 public void unregisterListener(ICarPowerStateListener listener) {
482 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
Steve Paik03124082018-02-16 11:19:26 -0800483 doUnregisterListener(listener);
484 }
485
486 private void doUnregisterListener(ICarPowerStateListener listener) {
487 boolean found = mPowerManagerListeners.unregister(listener);
Steve Paik03124082018-02-16 11:19:26 -0800488 if (found) {
489 // Remove outstanding token if there is one
490 IBinder binder = listener.asBinder();
491 synchronized (mPowerManagerListenerTokens) {
492 if (mPowerManagerListenerTokens.containsKey(binder)) {
493 int token = mPowerManagerListenerTokens.get(binder);
494 finishedLocked(binder, token);
495 }
496 }
497 }
Steve Paik388d7772018-02-12 10:54:51 -0800498 }
499
500 @Override
501 public void requestShutdownOnNextSuspend() {
502 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
Steve Paik07db5ed2018-09-24 16:48:52 -0700503 mShutdownOnFinish = true;
Steve Paik388d7772018-02-12 10:54:51 -0800504 }
505
506 @Override
Steve Paik03124082018-02-16 11:19:26 -0800507 public void finished(ICarPowerStateListener listener, int token) {
Steve Paik388d7772018-02-12 10:54:51 -0800508 ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
Steve Paik03124082018-02-16 11:19:26 -0800509 synchronized (mPowerManagerListenerTokens) {
510 finishedLocked(listener.asBinder(), token);
511 }
512 }
513
Serik Beketayev6e4c7822018-08-27 08:20:09 -0700514 @Override
515 public synchronized void scheduleNextWakeupTime(int seconds) {
516 if (seconds < 0) {
517 Log.w(CarLog.TAG_POWER, "Next wake up can not be in negative time. Ignoring!");
518 return;
519 }
520 if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) {
521 mNextWakeupSec = seconds;
522 } else {
523 Log.d(CarLog.TAG_POWER, "Tried to schedule next wake up, but already had shorter "
524 + " scheduled time");
525 }
526 }
527
Steve Paik03124082018-02-16 11:19:26 -0800528 private void finishedLocked(IBinder binder, int token) {
529 int currentToken = mPowerManagerListenerTokens.get(binder);
530 if (currentToken == token) {
531 mPowerManagerListenerTokens.remove(binder);
532 if (mPowerManagerListenerTokens.isEmpty() &&
Steve Paik07db5ed2018-09-24 16:48:52 -0700533 (mCurrentState.mState == CpmsState.SHUTDOWN_PREPARE)) {
534 PowerHandler powerHandler;
Steve Paik03124082018-02-16 11:19:26 -0800535 // All apps are ready to shutdown/suspend.
Steve Paik07db5ed2018-09-24 16:48:52 -0700536 synchronized (this) {
537 if (!mShutdownOnFinish) {
538 if (mLastSleepEntryTime > mProcessingStartTime
539 && mLastSleepEntryTime < SystemClock.elapsedRealtime()) {
540 Log.i(CarLog.TAG_POWER, "finishedLocked: Already slept!");
541 return;
542 }
543 }
544 powerHandler = mHandler;
545 }
546 Log.i(CarLog.TAG_POWER, "Apps are finished, call handleProcessingComplete()");
547 powerHandler.handleProcessingComplete();
Steve Paik03124082018-02-16 11:19:26 -0800548 }
549 }
Steve Paik388d7772018-02-12 10:54:51 -0800550 }
551
keunyoung4b0212c2015-10-29 17:11:57 -0700552 private class PowerHandler extends Handler {
keunyoung4b0212c2015-10-29 17:11:57 -0700553 private final int MSG_POWER_STATE_CHANGE = 0;
554 private final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1;
555 private final int MSG_MAIN_DISPLAY_STATE_CHANGE = 2;
556 private final int MSG_PROCESSING_COMPLETE = 3;
557
558 // Do not handle this immediately but with some delay as there can be a race between
559 // display off due to rear view camera and delivery to here.
560 private final long MAIN_DISPLAY_EVENT_DELAY_MS = 500;
561
562 private PowerHandler(Looper looper) {
563 super(looper);
564 }
565
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800566 private void handlePowerStateChange() {
567 Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);
keunyoung4b0212c2015-10-29 17:11:57 -0700568 sendMessage(msg);
569 }
570
571 private void handleDisplayBrightnessChange(int brightness) {
572 Message msg = obtainMessage(MSG_DISPLAY_BRIGHTNESS_CHANGE, brightness, 0);
573 sendMessage(msg);
574 }
575
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800576 private void handleMainDisplayStateChange(boolean on) {
keunyoung4b0212c2015-10-29 17:11:57 -0700577 removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800578 Message msg = obtainMessage(MSG_MAIN_DISPLAY_STATE_CHANGE, Boolean.valueOf(on));
keunyoung4b0212c2015-10-29 17:11:57 -0700579 sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS);
580 }
581
Steve Paik07db5ed2018-09-24 16:48:52 -0700582 private void handleProcessingComplete() {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800583 removeMessages(MSG_PROCESSING_COMPLETE);
Steve Paik07db5ed2018-09-24 16:48:52 -0700584 Message msg = obtainMessage(MSG_PROCESSING_COMPLETE);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800585 sendMessage(msg);
586 }
587
588 private void cancelProcessingComplete() {
589 removeMessages(MSG_PROCESSING_COMPLETE);
590 }
591
keunyoung4b0212c2015-10-29 17:11:57 -0700592 private void cancelAll() {
593 removeMessages(MSG_POWER_STATE_CHANGE);
594 removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE);
595 removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
596 removeMessages(MSG_PROCESSING_COMPLETE);
597 }
598
599 @Override
600 public void handleMessage(Message msg) {
601 switch (msg.what) {
602 case MSG_POWER_STATE_CHANGE:
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800603 doHandlePowerStateChange();
keunyoung4b0212c2015-10-29 17:11:57 -0700604 break;
605 case MSG_DISPLAY_BRIGHTNESS_CHANGE:
606 doHandleDisplayBrightnessChange(msg.arg1);
607 break;
608 case MSG_MAIN_DISPLAY_STATE_CHANGE:
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800609 doHandleMainDisplayStateChange((Boolean) msg.obj);
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800610 break;
keunyoung4b0212c2015-10-29 17:11:57 -0700611 case MSG_PROCESSING_COMPLETE:
Steve Paik07db5ed2018-09-24 16:48:52 -0700612 doHandleProcessingComplete();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800613 break;
keunyoung4b0212c2015-10-29 17:11:57 -0700614 }
615 }
616 }
617
keunyoung4b0212c2015-10-29 17:11:57 -0700618 private class ShutdownProcessingTimerTask extends TimerTask {
keunyoung4b0212c2015-10-29 17:11:57 -0700619 private final int mExpirationCount;
620 private int mCurrentCount;
621
Steve Paik07db5ed2018-09-24 16:48:52 -0700622 private ShutdownProcessingTimerTask(int expirationCount) {
keunyoung4b0212c2015-10-29 17:11:57 -0700623 mExpirationCount = expirationCount;
624 mCurrentCount = 0;
625 }
626
627 @Override
628 public void run() {
629 mCurrentCount++;
630 if (mCurrentCount > mExpirationCount) {
Pavel Maltsevddbff982017-03-22 14:49:02 -0700631 PowerHandler handler;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800632 synchronized (CarPowerManagementService.this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700633 releaseTimerLocked();
Pavel Maltsevddbff982017-03-22 14:49:02 -0700634 handler = mHandler;
keunyoung4b0212c2015-10-29 17:11:57 -0700635 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700636 handler.handleProcessingComplete();
keunyoung4b0212c2015-10-29 17:11:57 -0700637 } else {
638 mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);
639 }
640 }
641 }
Steve Paik07db5ed2018-09-24 16:48:52 -0700642
643 private static class CpmsState {
644 public static final int WAIT_FOR_VHAL = 0;
645 public static final int ON = 1;
646 public static final int SHUTDOWN_PREPARE = 2;
647 public static final int WAIT_FOR_FINISH = 3;
648 public static final int SUSPEND = 4;
649
650 /* Config values from AP_POWER_STATE_REQ */
651 public final boolean mCanPostpone;
652 public final boolean mCanSleep;
653 /* Message sent to CarPowerStateListener in response to this state */
654 public final int mCarPowerStateListenerState;
655 /* One of the above state variables */
656 public final int mState;
657
658 /**
659 * This constructor takes a PowerHalService.PowerState object and creates the corresponding
660 * CPMS state from it.
661 */
662 CpmsState(PowerState halPowerState) {
663 switch (halPowerState.mState) {
664 case VehicleApPowerStateReq.ON:
665 this.mCanPostpone = false;
666 this.mCanSleep = false;
667 this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(ON);
668 this.mState = ON;
669 break;
670 case VehicleApPowerStateReq.SHUTDOWN_PREPARE:
671 this.mCanPostpone = halPowerState.canPostponeShutdown();
672 this.mCanSleep = halPowerState.canEnterDeepSleep();
673 this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(
674 SHUTDOWN_PREPARE);
675 this.mState = SHUTDOWN_PREPARE;
676 break;
677 case VehicleApPowerStateReq.CANCEL_SHUTDOWN:
678 this.mCanPostpone = false;
679 this.mCanSleep = false;
680 this.mCarPowerStateListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
681 this.mState = WAIT_FOR_VHAL;
682 break;
683 case VehicleApPowerStateReq.FINISHED:
684 this.mCanPostpone = false;
685 this.mCanSleep = false;
686 this.mCarPowerStateListenerState = cpmsStateToPowerStateListenerState(SUSPEND);
687 this.mState = SUSPEND;
688 break;
689 default:
690 // Illegal state from PowerState. Throw an exception?
691 this.mCanPostpone = false;
692 this.mCanSleep = false;
693 this.mCarPowerStateListenerState = 0;
694 this.mState = 0;
695 break;
696 }
697 }
698
699 CpmsState(int state) {
700 this(state, cpmsStateToPowerStateListenerState(state));
701 }
702
703 CpmsState(int state, int carPowerStateListenerState) {
704 this.mCanPostpone = false;
705 this.mCanSleep = false;
706 this.mCarPowerStateListenerState = carPowerStateListenerState;
707 this.mState = state;
708 }
709
710 private static int cpmsStateToPowerStateListenerState(int state) {
711 int powerStateListenerState = 0;
712
713 // Set the CarPowerStateListenerState based on current state
714 switch (state) {
715 case ON:
716 powerStateListenerState = CarPowerStateListener.ON;
717 break;
718 case SHUTDOWN_PREPARE:
719 powerStateListenerState = CarPowerStateListener.SHUTDOWN_PREPARE;
720 break;
721 case SUSPEND:
722 powerStateListenerState = CarPowerStateListener.SUSPEND_ENTER;
723 break;
724 case WAIT_FOR_VHAL:
725 case WAIT_FOR_FINISH:
726 default:
727 // Illegal state for this constructor. Throw an exception?
728 break;
729 }
730 return powerStateListenerState;
731 }
732
733 @Override
734 public boolean equals(Object o) {
735 if (this == o) {
736 return true;
737 }
738 if (!(o instanceof CpmsState)) {
739 return false;
740 }
741 CpmsState that = (CpmsState) o;
742 return this.mState == that.mState
743 && this.mCanSleep == that.mCanSleep
744 && this.mCanPostpone == that.mCanPostpone
745 && this.mCarPowerStateListenerState == that.mCarPowerStateListenerState;
746 }
747
748 @Override
749 public String toString() {
750 return "CpmsState canSleep:" + mCanSleep + ", canPostpone=" + mCanPostpone
751 + ", carPowerStateListenerState=" + mCarPowerStateListenerState
752 + ", CpmsState=" + mState;
753 }
754 }
755
keunyoung4b0212c2015-10-29 17:11:57 -0700756}