blob: 1563deeccfdfb906c602b5659f2d15a9757c57e7 [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
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080018import android.annotation.NonNull;
Keun-young Parke54ac272016-02-16 19:02:18 -080019import android.car.Car;
keunyoung4b0212c2015-10-29 17:11:57 -070020import android.content.Context;
21import android.hardware.display.DisplayManager;
22import android.os.Handler;
23import android.os.HandlerThread;
24import android.os.Looper;
25import android.os.Message;
26import android.os.PowerManager;
27import android.os.PowerManager.WakeLock;
28import android.os.SystemClock;
keunyoung4b0212c2015-10-29 17:11:57 -070029import android.util.Log;
30import android.view.Display;
31
32import com.android.car.hal.PowerHalService;
33import com.android.car.hal.PowerHalService.PowerState;
34import com.android.car.hal.VehicleHal;
35import com.android.internal.annotations.GuardedBy;
Yao Chendacd7242016-01-26 14:42:42 -080036import com.android.internal.annotations.VisibleForTesting;
keunyoung4b0212c2015-10-29 17:11:57 -070037
38import java.io.PrintWriter;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080039import java.util.LinkedList;
keunyoung4b0212c2015-10-29 17:11:57 -070040import java.util.Timer;
41import java.util.TimerTask;
42import java.util.concurrent.CopyOnWriteArrayList;
43
44public class CarPowerManagementService implements CarServiceBase,
45 PowerHalService.PowerEventListener {
46
47 /**
48 * Listener for other services to monitor power events.
49 */
50 public interface PowerServiceEventListener {
51 /**
52 * Shutdown is happening
53 */
54 void onShutdown();
55
56 /**
57 * Entering deep sleep.
58 */
59 void onSleepEntry();
60
61 /**
62 * Got out of deep sleep.
63 */
64 void onSleepExit();
65 }
66
67 /**
68 * Interface for components requiring processing time before shutting-down or
69 * entering sleep, and wake-up after shut-down.
70 */
71 public interface PowerEventProcessingHandler {
72 /**
73 * Called before shutdown or sleep entry to allow running some processing. This call
74 * should only queue such task in different thread and should return quickly.
75 * Blocking inside this call can trigger watchdog timer which can terminate the
76 * whole system.
77 * @param shuttingDown whether system is shutting down or not (= sleep entry).
78 * @return time necessary to run processing in ms. should return 0 if there is no
79 * processing necessary.
80 */
Keun-young Park303ea1d2016-01-21 11:26:16 -080081 long onPrepareShutdown(boolean shuttingDown);
82
83 /**
84 * Called when power state is changed to ON state. Display can be either on or off.
85 * @param displayOn
86 */
87 void onPowerOn(boolean displayOn);
keunyoung4b0212c2015-10-29 17:11:57 -070088
89 /**
90 * Returns wake up time after system is fully shutdown. Power controller will power on
91 * the system after this time. This power on is meant for regular maintenance kind of
92 * operation.
93 * @return 0 of wake up is not necessary.
94 */
95 int getWakeupTime();
96 }
97
Keun-young Parkfd3fbf72016-01-22 17:00:17 -080098 /** Interface to abstract all system interaction. Separated for testing. */
99 public interface SystemInteface {
100 void setDisplayState(boolean on);
101 void releaseAllWakeLocks();
102 void shutdown();
103 void enterDeepSleep(int wakeupTimeSec);
104 void switchToPartialWakeLock();
105 void switchToFullWakeLock();
106 void startDisplayStateMonitoring(CarPowerManagementService service);
107 void stopDisplayStateMonitoring();
108 boolean isSystemSupportingDeepSleep();
109 boolean isWakeupCausedByTimer();
110 }
111
keunyoung4b0212c2015-10-29 17:11:57 -0700112 private final Context mContext;
113 private final PowerHalService mHal;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800114 private final SystemInteface mSystemInterface;
keunyoung4b0212c2015-10-29 17:11:57 -0700115 private final HandlerThread mHandlerThread;
116 private final PowerHandler mHandler;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800117
keunyoung4b0212c2015-10-29 17:11:57 -0700118 private final CopyOnWriteArrayList<PowerServiceEventListener> mListeners =
119 new CopyOnWriteArrayList<>();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800120 private final CopyOnWriteArrayList<PowerEventProcessingHandlerWrapper>
121 mPowerEventProcessingHandlers = new CopyOnWriteArrayList<>();
122
123 @GuardedBy("this")
keunyoung4b0212c2015-10-29 17:11:57 -0700124 private PowerState mCurrentState;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800125 @GuardedBy("this")
keunyoung4b0212c2015-10-29 17:11:57 -0700126 private Timer mTimer;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800127 @GuardedBy("this")
128 private long mProcessingStartTime;
129 @GuardedBy("this")
130 private long mLastSleepEntryTime;
131 @GuardedBy("this")
132 private final LinkedList<PowerState> mPendingPowerStates = new LinkedList<>();
keunyoung4b0212c2015-10-29 17:11:57 -0700133
134 private final int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
135 private final int SHUTDOWN_EXTEND_MAX_MS = 5000;
136
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800137 /**
138 * Constructor for full functionality.
139 */
140 public CarPowerManagementService(@NonNull Context context) {
141 this(context, VehicleHal.getInstance().getPowerHal(),
142 new SystemIntefaceImpl(context));
keunyoung4b0212c2015-10-29 17:11:57 -0700143 }
144
Yao Chendacd7242016-01-26 14:42:42 -0800145 /**
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800146 * Constructor for full functionality. Can inject external interfaces
147 */
148 public CarPowerManagementService(@NonNull Context context, @NonNull PowerHalService powerHal,
149 @NonNull SystemInteface systemInterface) {
150 mContext = context;
151 mHal = powerHal;
152 mSystemInterface = systemInterface;
153 mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
154 mHandlerThread.start();
155 mHandler = new PowerHandler(mHandlerThread.getLooper());
156 }
157
158 /**
159 * Create a dummy instance for unit testing purpose only. Instance constructed in this way
160 * is not safe as members expected to be non-null are null.
Yao Chendacd7242016-01-26 14:42:42 -0800161 */
162 @VisibleForTesting
163 protected CarPowerManagementService() {
164 mContext = null;
165 mHal = null;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800166 mSystemInterface = null;
Yao Chendacd7242016-01-26 14:42:42 -0800167 mHandlerThread = null;
Keun-young Parkca013ae2016-02-10 16:22:25 -0800168 mHandler = new PowerHandler(Looper.getMainLooper());
Yao Chendacd7242016-01-26 14:42:42 -0800169 }
170
keunyoung4b0212c2015-10-29 17:11:57 -0700171 @Override
172 public void init() {
173 mHal.setListener(this);
174 if (mHal.isPowerStateSupported()) {
175 mHal.sendBootComplete();
176 PowerState currentState = mHal.getCurrentPowerState();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800177 onApPowerStateChange(currentState);
keunyoung4b0212c2015-10-29 17:11:57 -0700178 } else {
179 Log.w(CarLog.TAG_POWER, "Vehicle hal does not support power state yet.");
Keun-young Parkfad57922016-04-21 11:00:45 -0700180 onApPowerStateChange(new PowerState(PowerHalService.STATE_ON_FULL, 0));
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800181 mSystemInterface.switchToFullWakeLock();
keunyoung4b0212c2015-10-29 17:11:57 -0700182 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800183 mSystemInterface.startDisplayStateMonitoring(this);
keunyoung4b0212c2015-10-29 17:11:57 -0700184 }
185
186 @Override
187 public void release() {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800188 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700189 releaseTimerLocked();
190 mCurrentState = null;
191 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800192 mSystemInterface.stopDisplayStateMonitoring();
keunyoung4b0212c2015-10-29 17:11:57 -0700193 mHandler.cancelAll();
194 mListeners.clear();
Keun-young Park303ea1d2016-01-21 11:26:16 -0800195 mPowerEventProcessingHandlers.clear();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800196 mSystemInterface.releaseAllWakeLocks();
keunyoung4b0212c2015-10-29 17:11:57 -0700197 }
198
199 /**
200 * Register listener to monitor power event. There is no unregister counter-part and the list
201 * will be cleared when the service is released.
202 * @param listener
203 */
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800204 public synchronized void registerPowerEventListener(PowerServiceEventListener listener) {
keunyoung4b0212c2015-10-29 17:11:57 -0700205 mListeners.add(listener);
206 }
207
208 /**
209 * Register PowerEventPreprocessingHandler to run pre-processing before shutdown or
210 * sleep entry. There is no unregister counter-part and the list
211 * will be cleared when the service is released.
212 * @param handler
213 */
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800214 public synchronized void registerPowerEventProcessingHandler(
215 PowerEventProcessingHandler handler) {
216 mPowerEventProcessingHandlers.add(new PowerEventProcessingHandlerWrapper(handler));
217 // onPowerOn will not be called if power on notification is already done inside the
218 // handler thread. So request it once again here. Wrapper will have its own
219 // gatekeeping to prevent calling onPowerOn twice.
220 mHandler.handlePowerOn();
keunyoung4b0212c2015-10-29 17:11:57 -0700221 }
222
223 /**
224 * Notifies earlier completion of power event processing. PowerEventProcessingHandler quotes
225 * time necessary from onPrePowerEvent() call, but actual processing can finish earlier than
226 * that, and this call can be called in such case to trigger shutdown without waiting further.
227 *
228 * @param handler PowerEventProcessingHandler that was already registered with
229 * {@link #registerPowerEventListener(PowerServiceEventListener)} call. If it was not
230 * registered before, this call will be ignored.
231 */
232 public void notifyPowerEventProcessingCompletion(PowerEventProcessingHandler handler) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800233 long processingTime = 0;
234 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
235 if (wrapper.handler == handler) {
236 wrapper.markProcessingDone();
237 } else if (!wrapper.isProcessingDone()) {
238 processingTime = Math.max(processingTime, wrapper.getProcessingTime());
239 }
240 }
241 long now = SystemClock.elapsedRealtime();
242 long startTime;
243 boolean shouldShutdown = true;
244 synchronized (this) {
245 startTime = mProcessingStartTime;
246 if (mCurrentState == null) {
247 return;
248 }
249 if (mCurrentState.state != PowerHalService.STATE_SHUTDOWN_PREPARE) {
250 return;
251 }
252 if (mCurrentState.canEnterDeepSleep()) {
253 shouldShutdown = false;
254 if (mLastSleepEntryTime > mProcessingStartTime && mLastSleepEntryTime < now) {
255 // already slept
256 return;
257 }
258 }
259 }
260 if ((startTime + processingTime) <= now) {
261 Log.i(CarLog.TAG_POWER, "Processing all done");
262 mHandler.handleProcessingComplete(shouldShutdown);
263 }
keunyoung4b0212c2015-10-29 17:11:57 -0700264 }
265
266 @Override
267 public void dump(PrintWriter writer) {
268 writer.println("*PowerManagementService*");
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800269 writer.print("mCurrentState:" + mCurrentState);
270 writer.print(",mProcessingStartTime:" + mProcessingStartTime);
271 writer.println(",mLastSleepEntryTime:" + mLastSleepEntryTime);
272 writer.println("**PowerEventProcessingHandlers");
273 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
274 writer.println(wrapper.toString());
275 }
keunyoung4b0212c2015-10-29 17:11:57 -0700276 }
277
278 @Override
279 public void onApPowerStateChange(PowerState state) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800280 synchronized (this) {
281 mPendingPowerStates.addFirst(state);
282 }
283 mHandler.handlePowerStateChange();
keunyoung4b0212c2015-10-29 17:11:57 -0700284 }
285
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800286 private void doHandlePowerStateChange() {
287 PowerState state = null;
288 synchronized (this) {
289 state = mPendingPowerStates.peekFirst();
290 mPendingPowerStates.clear();
291 if (state == null) {
292 return;
293 }
294 if (!needPowerStateChange(state)) {
295 return;
296 }
297 // now real power change happens. Whatever was queued before should be all cancelled.
298 releaseTimerLocked();
299 mHandler.cancelProcessingComplete();
keunyoung4b0212c2015-10-29 17:11:57 -0700300 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800301
keunyoung4b0212c2015-10-29 17:11:57 -0700302 Log.i(CarLog.TAG_POWER, "Power state change:" + state);
303 switch (state.state) {
304 case PowerHalService.STATE_ON_DISP_OFF:
305 handleDisplayOff(state);
Keun-young Park303ea1d2016-01-21 11:26:16 -0800306 notifyPowerOn(false);
keunyoung4b0212c2015-10-29 17:11:57 -0700307 break;
308 case PowerHalService.STATE_ON_FULL:
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800309 handleFullOn(state);
310 notifyPowerOn(true);
keunyoung4b0212c2015-10-29 17:11:57 -0700311 break;
312 case PowerHalService.STATE_SHUTDOWN_PREPARE:
313 handleShutdownPrepare(state);
314 break;
315 }
316 }
317
318 private void handleDisplayOff(PowerState newState) {
319 setCurrentState(newState);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800320 mSystemInterface.setDisplayState(false);
keunyoung4b0212c2015-10-29 17:11:57 -0700321 }
322
323 private void handleFullOn(PowerState newState) {
324 setCurrentState(newState);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800325 mSystemInterface.setDisplayState(true);
keunyoung4b0212c2015-10-29 17:11:57 -0700326 }
327
Yao Chendacd7242016-01-26 14:42:42 -0800328 @VisibleForTesting
329 protected void notifyPowerOn(boolean displayOn) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800330 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
331 wrapper.callOnPowerOn(displayOn);
Keun-young Park303ea1d2016-01-21 11:26:16 -0800332 }
333 }
334
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800335 @VisibleForTesting
336 protected long notifyPrepareShutdown(boolean shuttingDown) {
337 long processingTimeMs = 0;
338 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
339 long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
340 if (handlerProcessingTime > processingTimeMs) {
341 processingTimeMs = handlerProcessingTime;
342 }
343 }
344 return processingTimeMs;
345 }
346
keunyoung4b0212c2015-10-29 17:11:57 -0700347 private void handleShutdownPrepare(PowerState newState) {
348 setCurrentState(newState);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800349 mSystemInterface.setDisplayState(false);;
keunyoung4b0212c2015-10-29 17:11:57 -0700350 boolean shouldShutdown = true;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800351 if (mHal.isDeepSleepAllowed() && mSystemInterface.isSystemSupportingDeepSleep() &&
keunyoung4b0212c2015-10-29 17:11:57 -0700352 newState.canEnterDeepSleep()) {
353 Log.i(CarLog.TAG_POWER, "starting sleep");
354 shouldShutdown = false;
355 doHandlePreprocessing(shouldShutdown);
356 return;
357 } else if (newState.canPostponeShutdown()) {
358 Log.i(CarLog.TAG_POWER, "starting shutdown with processing");
359 doHandlePreprocessing(shouldShutdown);
360 } else {
361 Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800362 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700363 releaseTimerLocked();
364 }
365 doHandleShutdown();
366 }
367 }
368
369 private void releaseTimerLocked() {
370 if (mTimer != null) {
371 mTimer.cancel();
372 }
373 mTimer = null;
374 }
375
376 private void doHandlePreprocessing(boolean shuttingDown) {
377 long processingTimeMs = 0;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800378 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
379 long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
380 if (handlerProcessingTime > 0) {
381 wrapper.setProcessingTimeAndResetProcessingDone(handlerProcessingTime);
382 }
keunyoung4b0212c2015-10-29 17:11:57 -0700383 if (handlerProcessingTime > processingTimeMs) {
384 processingTimeMs = handlerProcessingTime;
385 }
386 }
387 if (processingTimeMs > 0) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800388 int pollingCount = (int)(processingTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
keunyoung4b0212c2015-10-29 17:11:57 -0700389 Log.i(CarLog.TAG_POWER, "processing before shutdown expected for :" + processingTimeMs +
390 " ms, adding polling:" + pollingCount);
391 synchronized (this) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800392 mProcessingStartTime = SystemClock.elapsedRealtime();
keunyoung4b0212c2015-10-29 17:11:57 -0700393 releaseTimerLocked();
394 mTimer = new Timer();
395 mTimer.scheduleAtFixedRate(new ShutdownProcessingTimerTask(shuttingDown,
396 pollingCount),
397 0 /*delay*/,
398 SHUTDOWN_POLLING_INTERVAL_MS);
399 }
400 } else {
401 mHandler.handleProcessingComplete(shuttingDown);
402 }
403 }
404
405 private void doHandleDeepSleep() {
Keun-young Park9d4cf622016-08-04 13:24:28 -0700406 // keep holding partial wakelock to prevent entering sleep before enterDeepSleep call
407 // enterDeepSleep should force sleep entry even if wake lock is kept.
408 mSystemInterface.switchToPartialWakeLock();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800409 mHandler.cancelProcessingComplete();
keunyoung4b0212c2015-10-29 17:11:57 -0700410 for (PowerServiceEventListener listener : mListeners) {
411 listener.onSleepEntry();
412 }
413 int wakeupTimeSec = getWakeupTime();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800414 mHal.sendSleepEntry();
415 synchronized (this) {
416 mLastSleepEntryTime = SystemClock.elapsedRealtime();
417 }
418 if (!shouldDoFakeShutdown()) { // if it is mocked, do not enter sleep.
419 mSystemInterface.enterDeepSleep(wakeupTimeSec);
420 }
keunyoung4b0212c2015-10-29 17:11:57 -0700421 mHal.sendSleepExit();
422 for (PowerServiceEventListener listener : mListeners) {
423 listener.onSleepExit();
424 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800425 if (mSystemInterface.isWakeupCausedByTimer()) {
keunyoung4b0212c2015-10-29 17:11:57 -0700426 doHandlePreprocessing(false /*shuttingDown*/);
427 } else {
428 PowerState currentState = mHal.getCurrentPowerState();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800429 if (needPowerStateChange(currentState)) {
430 onApPowerStateChange(currentState);
keunyoung4b0212c2015-10-29 17:11:57 -0700431 } else { // power controller woke-up but no power state change. Just shutdown.
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800432 Log.w(CarLog.TAG_POWER, "external sleep wake up, but no power state change:" +
keunyoung4b0212c2015-10-29 17:11:57 -0700433 currentState);
434 doHandleShutdown();
435 }
436 }
437 }
438
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800439 private void doHandleNotifyPowerOn() {
440 boolean displayOn = false;
441 synchronized (this) {
Keun-young Parkd73afae2016-04-08 20:03:32 -0700442 if (mCurrentState != null && mCurrentState.state == PowerHalService.STATE_ON_FULL) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800443 displayOn = true;
444 }
445 }
446 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
447 // wrapper will not send it forward if it is already called.
448 wrapper.callOnPowerOn(displayOn);
449 }
450 }
451
452 private boolean needPowerStateChange(PowerState newState) {
453 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700454 if (mCurrentState != null && mCurrentState.equals(newState)) {
455 return false;
456 }
457 return true;
458 }
459 }
460
keunyoung4b0212c2015-10-29 17:11:57 -0700461 private void doHandleShutdown() {
462 // now shutdown
463 for (PowerServiceEventListener listener : mListeners) {
464 listener.onShutdown();
465 }
466 int wakeupTimeSec = 0;
467 if (mHal.isTimedWakeupAllowed()) {
468 wakeupTimeSec = getWakeupTime();
469 }
470 mHal.sendShutdownStart(wakeupTimeSec);
471 if (!shouldDoFakeShutdown()) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800472 mSystemInterface.shutdown();
keunyoung4b0212c2015-10-29 17:11:57 -0700473 }
474 }
475
476 private int getWakeupTime() {
477 int wakeupTimeSec = 0;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800478 for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
479 int t = wrapper.handler.getWakeupTime();
keunyoung4b0212c2015-10-29 17:11:57 -0700480 if (t > wakeupTimeSec) {
481 wakeupTimeSec = t;
482 }
483 }
484 return wakeupTimeSec;
485 }
486
487 private void doHandleProcessingComplete(boolean shutdownWhenCompleted) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800488 synchronized (this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700489 releaseTimerLocked();
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800490 if (!shutdownWhenCompleted && mLastSleepEntryTime > mProcessingStartTime) {
491 // entered sleep after processing start. So this could be duplicate request.
492 Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
493 return;
494 }
keunyoung4b0212c2015-10-29 17:11:57 -0700495 }
496 if (shutdownWhenCompleted) {
497 doHandleShutdown();
498 } else {
499 doHandleDeepSleep();
500 }
501 }
502
keunyoung4b0212c2015-10-29 17:11:57 -0700503 private synchronized void setCurrentState(PowerState state) {
504 mCurrentState = state;
505 }
506
507 @Override
508 public void onDisplayBrightnessChange(int brightness) {
509 // TODO
510 }
511
512 private void doHandleDisplayBrightnessChange(int brightness) {
513 //TODO
514 }
515
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800516 private void doHandleMainDisplayStateChange(boolean on) {
keunyoung4b0212c2015-10-29 17:11:57 -0700517 //TODO
518 }
519
keunyoung4b0212c2015-10-29 17:11:57 -0700520 private boolean shouldDoFakeShutdown() {
521 ICarImpl carImpl = ICarImpl.getInstance(mContext);
522 if (!carImpl.isInMocking()) {
523 return false;
524 }
Keun-young Parke54ac272016-02-16 19:02:18 -0800525 CarTestService testService = (CarTestService) carImpl.getCarService(Car.TEST_SERVICE);
keunyoung4b0212c2015-10-29 17:11:57 -0700526 return !testService.shouldDoRealShutdownInMocking();
527 }
528
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800529 public void handleMainDisplayChanged(boolean on) {
530 mHandler.handleMainDisplayStateChange(on);
531 }
532
533 public Handler getHandler() {
534 return mHandler;
535 }
536
keunyoung4b0212c2015-10-29 17:11:57 -0700537 private class PowerHandler extends Handler {
538
539 private final int MSG_POWER_STATE_CHANGE = 0;
540 private final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1;
541 private final int MSG_MAIN_DISPLAY_STATE_CHANGE = 2;
542 private final int MSG_PROCESSING_COMPLETE = 3;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800543 private final int MSG_NOTIFY_POWER_ON = 4;
keunyoung4b0212c2015-10-29 17:11:57 -0700544
545 // Do not handle this immediately but with some delay as there can be a race between
546 // display off due to rear view camera and delivery to here.
547 private final long MAIN_DISPLAY_EVENT_DELAY_MS = 500;
548
549 private PowerHandler(Looper looper) {
550 super(looper);
551 }
552
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800553 private void handlePowerStateChange() {
554 Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);
keunyoung4b0212c2015-10-29 17:11:57 -0700555 sendMessage(msg);
556 }
557
558 private void handleDisplayBrightnessChange(int brightness) {
559 Message msg = obtainMessage(MSG_DISPLAY_BRIGHTNESS_CHANGE, brightness, 0);
560 sendMessage(msg);
561 }
562
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800563 private void handleMainDisplayStateChange(boolean on) {
keunyoung4b0212c2015-10-29 17:11:57 -0700564 removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800565 Message msg = obtainMessage(MSG_MAIN_DISPLAY_STATE_CHANGE, Boolean.valueOf(on));
keunyoung4b0212c2015-10-29 17:11:57 -0700566 sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS);
567 }
568
569 private void handleProcessingComplete(boolean shutdownWhenCompleted) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800570 removeMessages(MSG_PROCESSING_COMPLETE);
keunyoung4b0212c2015-10-29 17:11:57 -0700571 Message msg = obtainMessage(MSG_PROCESSING_COMPLETE, shutdownWhenCompleted ? 1 : 0, 0);
572 sendMessage(msg);
573 }
574
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800575 private void handlePowerOn() {
576 Message msg = obtainMessage(MSG_NOTIFY_POWER_ON);
577 sendMessage(msg);
578 }
579
580 private void cancelProcessingComplete() {
581 removeMessages(MSG_PROCESSING_COMPLETE);
582 }
583
keunyoung4b0212c2015-10-29 17:11:57 -0700584 private void cancelAll() {
585 removeMessages(MSG_POWER_STATE_CHANGE);
586 removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE);
587 removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
588 removeMessages(MSG_PROCESSING_COMPLETE);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800589 removeMessages(MSG_NOTIFY_POWER_ON);
keunyoung4b0212c2015-10-29 17:11:57 -0700590 }
591
592 @Override
593 public void handleMessage(Message msg) {
594 switch (msg.what) {
595 case MSG_POWER_STATE_CHANGE:
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800596 doHandlePowerStateChange();
keunyoung4b0212c2015-10-29 17:11:57 -0700597 break;
598 case MSG_DISPLAY_BRIGHTNESS_CHANGE:
599 doHandleDisplayBrightnessChange(msg.arg1);
600 break;
601 case MSG_MAIN_DISPLAY_STATE_CHANGE:
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800602 doHandleMainDisplayStateChange((Boolean) msg.obj);
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800603 break;
keunyoung4b0212c2015-10-29 17:11:57 -0700604 case MSG_PROCESSING_COMPLETE:
605 doHandleProcessingComplete(msg.arg1 == 1);
606 break;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800607 case MSG_NOTIFY_POWER_ON:
608 doHandleNotifyPowerOn();
609 break;
keunyoung4b0212c2015-10-29 17:11:57 -0700610 }
611 }
612 }
613
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800614 private static class SystemIntefaceImpl implements SystemInteface {
keunyoung4b0212c2015-10-29 17:11:57 -0700615
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800616 private final PowerManager mPowerManager;
617 private final DisplayManager mDisplayManager;
618 private final WakeLock mFullWakeLock;
619 private final WakeLock mPartialWakeLock;
620 private final DisplayStateListener mDisplayListener;
621 private CarPowerManagementService mService;
622 private boolean mDisplayStateSet;
623
624 private SystemIntefaceImpl(Context context) {
625 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
626 mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
627 mFullWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, CarLog.TAG_POWER);
628 mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
629 CarLog.TAG_POWER);
630 mDisplayListener = new DisplayStateListener();
keunyoung4b0212c2015-10-29 17:11:57 -0700631 }
632
633 @Override
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800634 public void startDisplayStateMonitoring(CarPowerManagementService service) {
635 synchronized (this) {
636 mService = service;
637 mDisplayStateSet = isMainDisplayOn();
638 }
639 mDisplayManager.registerDisplayListener(mDisplayListener, service.getHandler());
640 }
641
642 @Override
643 public void stopDisplayStateMonitoring() {
644 mDisplayManager.unregisterDisplayListener(mDisplayListener);
645 }
646
647 @Override
648 public void setDisplayState(boolean on) {
649 synchronized (this) {
650 mDisplayStateSet = on;
651 }
652 if (on) {
653 switchToFullWakeLock();
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800654 Log.i(CarLog.TAG_POWER, "on display");
655 mPowerManager.wakeUp(SystemClock.uptimeMillis());
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800656 } else {
657 switchToPartialWakeLock();
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800658 Log.i(CarLog.TAG_POWER, "off display");
659 mPowerManager.goToSleep(SystemClock.uptimeMillis());
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800660 }
661 }
662
663 private boolean isMainDisplayOn() {
664 Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
Keun-young Park1f4d6a72016-02-11 09:49:47 -0800665 return disp.getState() == Display.STATE_ON;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800666 }
667
668 @Override
669 public void shutdown() {
670 mPowerManager.shutdown(false /* no confirm*/, null, true /* true */);
671 }
672
673 @Override
674 public void enterDeepSleep(int wakeupTimeSec) {
Keun-young Park9d4cf622016-08-04 13:24:28 -0700675 //TODO set wake up time
676 mPowerManager.goToSleep(SystemClock.uptimeMillis(),
677 PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN,
678 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800679 }
680
681 @Override
682 public boolean isSystemSupportingDeepSleep() {
683 //TODO should return by checking some kernel suspend control sysfs
684 return false;
685 }
686
687 @Override
688 public void switchToPartialWakeLock() {
689 if (!mPartialWakeLock.isHeld()) {
690 mPartialWakeLock.acquire();
691 }
692 if (mFullWakeLock.isHeld()) {
693 mFullWakeLock.release();
keunyoung4b0212c2015-10-29 17:11:57 -0700694 }
695 }
696
697 @Override
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800698 public void switchToFullWakeLock() {
699 if (!mFullWakeLock.isHeld()) {
700 mFullWakeLock.acquire();
701 }
702 if (mPartialWakeLock.isHeld()) {
703 mPartialWakeLock.release();
704 }
705 }
706
707 @Override
708 public void releaseAllWakeLocks() {
709 if (mPartialWakeLock.isHeld()) {
710 mPartialWakeLock.release();
711 }
712 if (mFullWakeLock.isHeld()) {
713 mFullWakeLock.release();
714 }
715 }
716
717 @Override
718 public boolean isWakeupCausedByTimer() {
719 //TODO check wake up reason and do necessary operation information should come from
720 // kernel. it can be either power on or wake up for maintenance
721 // power on will involve GPIO trigger from power controller
722 // its own wakeup will involve timer expiration.
723 return false;
724 }
725
726 private void handleMainDisplayChanged() {
727 boolean isOn = isMainDisplayOn();
728 CarPowerManagementService service;
729 synchronized (this) {
730 if (mDisplayStateSet == isOn) { // same as what is set
731 return;
732 }
733 service = mService;
734 }
735 service.handleMainDisplayChanged(isOn);
736 }
737
738 private class DisplayStateListener implements DisplayManager.DisplayListener {
739
740 @Override
741 public void onDisplayAdded(int displayId) {
742 //ignore
743 }
744
745 @Override
746 public void onDisplayChanged(int displayId) {
747 if (displayId == Display.DEFAULT_DISPLAY) {
748 handleMainDisplayChanged();
749 }
750 }
751
752 @Override
753 public void onDisplayRemoved(int displayId) {
754 //ignore
755 }
keunyoung4b0212c2015-10-29 17:11:57 -0700756 }
757 }
758
759 private class ShutdownProcessingTimerTask extends TimerTask {
760 private final boolean mShutdownWhenCompleted;
761 private final int mExpirationCount;
762 private int mCurrentCount;
763
764 private ShutdownProcessingTimerTask(boolean shutdownWhenCompleted, int expirationCount) {
765 mShutdownWhenCompleted = shutdownWhenCompleted;
766 mExpirationCount = expirationCount;
767 mCurrentCount = 0;
768 }
769
770 @Override
771 public void run() {
772 mCurrentCount++;
773 if (mCurrentCount > mExpirationCount) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800774 synchronized (CarPowerManagementService.this) {
keunyoung4b0212c2015-10-29 17:11:57 -0700775 releaseTimerLocked();
776 }
777 mHandler.handleProcessingComplete(mShutdownWhenCompleted);
778 } else {
779 mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);
780 }
781 }
782 }
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800783
784 private static class PowerEventProcessingHandlerWrapper {
785 public final PowerEventProcessingHandler handler;
786 private long mProcessingTime = 0;
787 private boolean mProcessingDone = true;
788 private boolean mPowerOnSent = false;
Keun-young Parkd73afae2016-04-08 20:03:32 -0700789 private int mLastDisplayState = -1;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800790
791 public PowerEventProcessingHandlerWrapper(PowerEventProcessingHandler handler) {
792 this.handler = handler;
793 }
794
795 public synchronized void setProcessingTimeAndResetProcessingDone(long processingTime) {
796 mProcessingTime = processingTime;
797 mProcessingDone = false;
798 }
799
800 public synchronized long getProcessingTime() {
801 return mProcessingTime;
802 }
803
804 public synchronized void markProcessingDone() {
805 mProcessingDone = true;
806 }
807
808 public synchronized boolean isProcessingDone() {
809 return mProcessingDone;
810 }
811
812 public void callOnPowerOn(boolean displayOn) {
Keun-young Parkd73afae2016-04-08 20:03:32 -0700813 int newDisplayState = displayOn ? 1 : 0;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800814 boolean shouldCall = false;
815 synchronized (this) {
Keun-young Parkd73afae2016-04-08 20:03:32 -0700816 if (!mPowerOnSent || (mLastDisplayState != newDisplayState)) {
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800817 shouldCall = true;
818 mPowerOnSent = true;
Keun-young Parkd73afae2016-04-08 20:03:32 -0700819 mLastDisplayState = newDisplayState;
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800820 }
821 }
822 if (shouldCall) {
823 handler.onPowerOn(displayOn);
824 }
825 }
826
Keun-young Parkfd3fbf72016-01-22 17:00:17 -0800827 @Override
828 public String toString() {
829 return "PowerEventProcessingHandlerWrapper [handler=" + handler + ", mProcessingTime="
830 + mProcessingTime + ", mProcessingDone=" + mProcessingDone + "]";
831 }
832 }
keunyoung4b0212c2015-10-29 17:11:57 -0700833}