1 /*
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  */
16 package com.android.car;
17 
18 import android.os.Handler;
19 import android.os.HandlerThread;
20 import android.os.Looper;
21 import android.os.Message;
22 import android.os.SystemClock;
23 import android.util.Log;
24 
25 import com.android.car.hal.PowerHalService;
26 import com.android.car.hal.PowerHalService.PowerState;
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.annotations.VisibleForTesting;
29 
30 import java.io.PrintWriter;
31 import java.util.LinkedList;
32 import java.util.Timer;
33 import java.util.TimerTask;
34 import java.util.concurrent.CopyOnWriteArrayList;
35 
36 public class CarPowerManagementService implements CarServiceBase,
37     PowerHalService.PowerEventListener {
38 
39     /**
40      * Listener for other services to monitor power events.
41      */
42     public interface PowerServiceEventListener {
43         /**
44          * Shutdown is happening
45          */
onShutdown()46         void onShutdown();
47 
48         /**
49          * Entering deep sleep.
50          */
onSleepEntry()51         void onSleepEntry();
52 
53         /**
54          * Got out of deep sleep.
55          */
onSleepExit()56         void onSleepExit();
57     }
58 
59     /**
60      * Interface for components requiring processing time before shutting-down or
61      * entering sleep, and wake-up after shut-down.
62      */
63     public interface PowerEventProcessingHandler {
64         /**
65          * Called before shutdown or sleep entry to allow running some processing. This call
66          * should only queue such task in different thread and should return quickly.
67          * Blocking inside this call can trigger watchdog timer which can terminate the
68          * whole system.
69          * @param shuttingDown whether system is shutting down or not (= sleep entry).
70          * @return time necessary to run processing in ms. should return 0 if there is no
71          *         processing necessary.
72          */
onPrepareShutdown(boolean shuttingDown)73         long onPrepareShutdown(boolean shuttingDown);
74 
75         /**
76          * Called when power state is changed to ON state. Display can be either on or off.
77          * @param displayOn
78          */
onPowerOn(boolean displayOn)79         void onPowerOn(boolean displayOn);
80 
81         /**
82          * Returns wake up time after system is fully shutdown. Power controller will power on
83          * the system after this time. This power on is meant for regular maintenance kind of
84          * operation.
85          * @return 0 of wake up is not necessary.
86          */
getWakeupTime()87         int getWakeupTime();
88     }
89 
90     private final PowerHalService mHal;
91     private final SystemInterface mSystemInterface;
92 
93     private final CopyOnWriteArrayList<PowerServiceEventListener> mListeners =
94             new CopyOnWriteArrayList<>();
95     private final CopyOnWriteArrayList<PowerEventProcessingHandlerWrapper>
96             mPowerEventProcessingHandlers = new CopyOnWriteArrayList<>();
97 
98     @GuardedBy("this")
99     private PowerState mCurrentState;
100     @GuardedBy("this")
101     private Timer mTimer;
102     @GuardedBy("this")
103     private long mProcessingStartTime;
104     @GuardedBy("this")
105     private long mLastSleepEntryTime;
106     @GuardedBy("this")
107     private final LinkedList<PowerState> mPendingPowerStates = new LinkedList<>();
108     @GuardedBy("this")
109     private HandlerThread mHandlerThread;
110     @GuardedBy("this")
111     private PowerHandler mHandler;
112 
113     private final static int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
114     private final static int SHUTDOWN_EXTEND_MAX_MS = 5000;
115 
CarPowerManagementService(PowerHalService powerHal, SystemInterface systemInterface)116     public CarPowerManagementService(PowerHalService powerHal, SystemInterface systemInterface) {
117         mHal = powerHal;
118         mSystemInterface = systemInterface;
119     }
120 
121     /**
122      * Create a dummy instance for unit testing purpose only. Instance constructed in this way
123      * is not safe as members expected to be non-null are null.
124      */
125     @VisibleForTesting
CarPowerManagementService()126     protected CarPowerManagementService() {
127         mHal = null;
128         mSystemInterface = null;
129         mHandlerThread = null;
130         mHandler = new PowerHandler(Looper.getMainLooper());
131     }
132 
133     @Override
init()134     public void init() {
135         synchronized (this) {
136             mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
137             mHandlerThread.start();
138             mHandler = new PowerHandler(mHandlerThread.getLooper());
139         }
140 
141         mHal.setListener(this);
142         if (mHal.isPowerStateSupported()) {
143             mHal.sendBootComplete();
144             PowerState currentState = mHal.getCurrentPowerState();
145             if (currentState != null) {
146                 onApPowerStateChange(currentState);
147             } else {
148                 Log.w(CarLog.TAG_POWER, "Unable to get get current power state during "
149                         + "initialization");
150             }
151         } else {
152             Log.w(CarLog.TAG_POWER, "Vehicle hal does not support power state yet.");
153             onApPowerStateChange(new PowerState(PowerHalService.STATE_ON_FULL, 0));
154             mSystemInterface.switchToFullWakeLock();
155         }
156         mSystemInterface.startDisplayStateMonitoring(this);
157     }
158 
159     @Override
release()160     public void release() {
161         HandlerThread handlerThread;
162         synchronized (this) {
163             releaseTimerLocked();
164             mCurrentState = null;
165             mHandler.cancelAll();
166             handlerThread = mHandlerThread;
167         }
168         handlerThread.quitSafely();
169         try {
170             handlerThread.join(1000);
171         } catch (InterruptedException e) {
172             Log.e(CarLog.TAG_POWER, "Timeout while joining for handler thread to join.");
173         }
174         mSystemInterface.stopDisplayStateMonitoring();
175         mListeners.clear();
176         mPowerEventProcessingHandlers.clear();
177         mSystemInterface.releaseAllWakeLocks();
178     }
179 
180     /**
181      * Register listener to monitor power event. There is no unregister counter-part and the list
182      * will be cleared when the service is released.
183      * @param listener
184      */
registerPowerEventListener(PowerServiceEventListener listener)185     public synchronized void registerPowerEventListener(PowerServiceEventListener listener) {
186         mListeners.add(listener);
187     }
188 
189     /**
190      * Register PowerEventPreprocessingHandler to run pre-processing before shutdown or
191      * sleep entry. There is no unregister counter-part and the list
192      * will be cleared when the service is released.
193      * @param handler
194      */
registerPowerEventProcessingHandler( PowerEventProcessingHandler handler)195     public synchronized void registerPowerEventProcessingHandler(
196             PowerEventProcessingHandler handler) {
197         mPowerEventProcessingHandlers.add(new PowerEventProcessingHandlerWrapper(handler));
198         // onPowerOn will not be called if power on notification is already done inside the
199         // handler thread. So request it once again here. Wrapper will have its own
200         // gatekeeping to prevent calling onPowerOn twice.
201         mHandler.handlePowerOn();
202     }
203 
204     /**
205      * Notifies earlier completion of power event processing. PowerEventProcessingHandler quotes
206      * time necessary from onPrePowerEvent() call, but actual processing can finish earlier than
207      * that, and this call can be called in such case to trigger shutdown without waiting further.
208      *
209      * @param handler PowerEventProcessingHandler that was already registered with
210      *        {@link #registerPowerEventListener(PowerServiceEventListener)} call. If it was not
211      *        registered before, this call will be ignored.
212      */
notifyPowerEventProcessingCompletion(PowerEventProcessingHandler handler)213     public void notifyPowerEventProcessingCompletion(PowerEventProcessingHandler handler) {
214         long processingTime = 0;
215         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
216             if (wrapper.handler == handler) {
217                 wrapper.markProcessingDone();
218             } else if (!wrapper.isProcessingDone()) {
219                 processingTime = Math.max(processingTime, wrapper.getProcessingTime());
220             }
221         }
222         long now = SystemClock.elapsedRealtime();
223         long startTime;
224         boolean shouldShutdown = true;
225         PowerHandler powerHandler;
226         synchronized (this) {
227             startTime = mProcessingStartTime;
228             if (mCurrentState == null) {
229                 return;
230             }
231             if (mCurrentState.mState != PowerHalService.STATE_SHUTDOWN_PREPARE) {
232                 return;
233             }
234             if (mCurrentState.canEnterDeepSleep()) {
235                 shouldShutdown = false;
236                 if (mLastSleepEntryTime > mProcessingStartTime && mLastSleepEntryTime < now) {
237                     // already slept
238                     return;
239                 }
240             }
241             powerHandler = mHandler;
242         }
243         if ((startTime + processingTime) <= now) {
244             Log.i(CarLog.TAG_POWER, "Processing all done");
245             powerHandler.handleProcessingComplete(shouldShutdown);
246         }
247     }
248 
249     @Override
dump(PrintWriter writer)250     public void dump(PrintWriter writer) {
251         writer.println("*PowerManagementService*");
252         writer.print("mCurrentState:" + mCurrentState);
253         writer.print(",mProcessingStartTime:" + mProcessingStartTime);
254         writer.println(",mLastSleepEntryTime:" + mLastSleepEntryTime);
255         writer.println("**PowerEventProcessingHandlers");
256         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
257             writer.println(wrapper.toString());
258         }
259     }
260 
261     @Override
onApPowerStateChange(PowerState state)262     public void onApPowerStateChange(PowerState state) {
263         PowerHandler handler;
264         synchronized (this) {
265             mPendingPowerStates.addFirst(state);
266             handler = mHandler;
267         }
268         handler.handlePowerStateChange();
269     }
270 
doHandlePowerStateChange()271     private void doHandlePowerStateChange() {
272         PowerState state = null;
273         PowerHandler handler;
274         synchronized (this) {
275             state = mPendingPowerStates.peekFirst();
276             mPendingPowerStates.clear();
277             if (state == null) {
278                 return;
279             }
280             if (!needPowerStateChange(state)) {
281                 return;
282             }
283             // now real power change happens. Whatever was queued before should be all cancelled.
284             releaseTimerLocked();
285             handler = mHandler;
286         }
287         handler.cancelProcessingComplete();
288 
289         Log.i(CarLog.TAG_POWER, "Power state change:" + state);
290         switch (state.mState) {
291             case PowerHalService.STATE_ON_DISP_OFF:
292                 handleDisplayOff(state);
293                 notifyPowerOn(false);
294                 break;
295             case PowerHalService.STATE_ON_FULL:
296                 handleFullOn(state);
297                 notifyPowerOn(true);
298                 break;
299             case PowerHalService.STATE_SHUTDOWN_PREPARE:
300                 handleShutdownPrepare(state);
301                 break;
302         }
303     }
304 
handleDisplayOff(PowerState newState)305     private void handleDisplayOff(PowerState newState) {
306         setCurrentState(newState);
307         mSystemInterface.setDisplayState(false);
308     }
309 
handleFullOn(PowerState newState)310     private void handleFullOn(PowerState newState) {
311         setCurrentState(newState);
312         mSystemInterface.setDisplayState(true);
313     }
314 
315     @VisibleForTesting
notifyPowerOn(boolean displayOn)316     protected void notifyPowerOn(boolean displayOn) {
317         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
318             wrapper.callOnPowerOn(displayOn);
319         }
320     }
321 
322     @VisibleForTesting
notifyPrepareShutdown(boolean shuttingDown)323     protected long notifyPrepareShutdown(boolean shuttingDown) {
324         long processingTimeMs = 0;
325         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
326             long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
327             if (handlerProcessingTime > processingTimeMs) {
328                 processingTimeMs = handlerProcessingTime;
329             }
330         }
331         return processingTimeMs;
332     }
333 
handleShutdownPrepare(PowerState newState)334     private void handleShutdownPrepare(PowerState newState) {
335         setCurrentState(newState);
336         mSystemInterface.setDisplayState(false);;
337         boolean shouldShutdown = true;
338         if (mHal.isDeepSleepAllowed() && mSystemInterface.isSystemSupportingDeepSleep() &&
339                 newState.canEnterDeepSleep()) {
340             Log.i(CarLog.TAG_POWER, "starting sleep");
341             shouldShutdown = false;
342             doHandlePreprocessing(shouldShutdown);
343             return;
344         } else if (newState.canPostponeShutdown()) {
345             Log.i(CarLog.TAG_POWER, "starting shutdown with processing");
346             doHandlePreprocessing(shouldShutdown);
347         } else {
348             Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
349             synchronized (this) {
350                 releaseTimerLocked();
351             }
352             doHandleShutdown();
353         }
354     }
355 
releaseTimerLocked()356     private void releaseTimerLocked() {
357         if (mTimer != null) {
358             mTimer.cancel();
359         }
360         mTimer = null;
361     }
362 
doHandlePreprocessing(boolean shuttingDown)363     private void doHandlePreprocessing(boolean shuttingDown) {
364         long processingTimeMs = 0;
365         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
366             long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
367             if (handlerProcessingTime > 0) {
368                 wrapper.setProcessingTimeAndResetProcessingDone(handlerProcessingTime);
369             }
370             if (handlerProcessingTime > processingTimeMs) {
371                 processingTimeMs = handlerProcessingTime;
372             }
373         }
374         if (processingTimeMs > 0) {
375             int pollingCount = (int)(processingTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
376             Log.i(CarLog.TAG_POWER, "processing before shutdown expected for :" + processingTimeMs +
377                     " ms, adding polling:" + pollingCount);
378             synchronized (this) {
379                 mProcessingStartTime = SystemClock.elapsedRealtime();
380                 releaseTimerLocked();
381                 mTimer = new Timer();
382                 mTimer.scheduleAtFixedRate(new ShutdownProcessingTimerTask(shuttingDown,
383                         pollingCount),
384                         0 /*delay*/,
385                         SHUTDOWN_POLLING_INTERVAL_MS);
386             }
387         } else {
388             PowerHandler handler;
389             synchronized (this) {
390                 handler = mHandler;
391             }
392             handler.handleProcessingComplete(shuttingDown);
393         }
394     }
395 
doHandleDeepSleep()396     private void doHandleDeepSleep() {
397         // keep holding partial wakelock to prevent entering sleep before enterDeepSleep call
398         // enterDeepSleep should force sleep entry even if wake lock is kept.
399         mSystemInterface.switchToPartialWakeLock();
400         PowerHandler handler;
401         synchronized (this) {
402             handler = mHandler;
403         }
404         handler.cancelProcessingComplete();
405         for (PowerServiceEventListener listener : mListeners) {
406             listener.onSleepEntry();
407         }
408         int wakeupTimeSec = getWakeupTime();
409         mHal.sendSleepEntry();
410         synchronized (this) {
411             mLastSleepEntryTime = SystemClock.elapsedRealtime();
412         }
413         mSystemInterface.enterDeepSleep(wakeupTimeSec);
414         mHal.sendSleepExit();
415         for (PowerServiceEventListener listener : mListeners) {
416             listener.onSleepExit();
417         }
418         if (mSystemInterface.isWakeupCausedByTimer()) {
419             doHandlePreprocessing(false /*shuttingDown*/);
420         } else {
421             PowerState currentState = mHal.getCurrentPowerState();
422             if (currentState != null && needPowerStateChange(currentState)) {
423                 onApPowerStateChange(currentState);
424             } else { // power controller woke-up but no power state change. Just shutdown.
425                 Log.w(CarLog.TAG_POWER, "external sleep wake up, but no power state change:" +
426                         currentState);
427                 doHandleShutdown();
428             }
429         }
430     }
431 
doHandleNotifyPowerOn()432     private void doHandleNotifyPowerOn() {
433         boolean displayOn = false;
434         synchronized (this) {
435             if (mCurrentState != null && mCurrentState.mState == PowerHalService.STATE_ON_FULL) {
436                 displayOn = true;
437             }
438         }
439         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
440             // wrapper will not send it forward if it is already called.
441             wrapper.callOnPowerOn(displayOn);
442         }
443     }
444 
needPowerStateChange(PowerState newState)445     private boolean needPowerStateChange(PowerState newState) {
446         synchronized (this) {
447             if (mCurrentState != null && mCurrentState.equals(newState)) {
448                 return false;
449             }
450             return true;
451         }
452     }
453 
doHandleShutdown()454     private void doHandleShutdown() {
455         // now shutdown
456         for (PowerServiceEventListener listener : mListeners) {
457             listener.onShutdown();
458         }
459         int wakeupTimeSec = 0;
460         if (mHal.isTimedWakeupAllowed()) {
461             wakeupTimeSec = getWakeupTime();
462         }
463         mHal.sendShutdownStart(wakeupTimeSec);
464         mSystemInterface.shutdown();
465     }
466 
getWakeupTime()467     private int getWakeupTime() {
468         int wakeupTimeSec = 0;
469         for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
470             int t = wrapper.handler.getWakeupTime();
471             if (t > wakeupTimeSec) {
472                 wakeupTimeSec = t;
473             }
474         }
475         return wakeupTimeSec;
476     }
477 
doHandleProcessingComplete(boolean shutdownWhenCompleted)478     private void doHandleProcessingComplete(boolean shutdownWhenCompleted) {
479         synchronized (this) {
480             releaseTimerLocked();
481             if (!shutdownWhenCompleted && mLastSleepEntryTime > mProcessingStartTime) {
482                 // entered sleep after processing start. So this could be duplicate request.
483                 Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
484                 return;
485             }
486         }
487         if (shutdownWhenCompleted) {
488             doHandleShutdown();
489         } else {
490             doHandleDeepSleep();
491         }
492     }
493 
setCurrentState(PowerState state)494     private synchronized void setCurrentState(PowerState state) {
495         mCurrentState = state;
496     }
497 
498     @Override
onDisplayBrightnessChange(int brightness)499     public void onDisplayBrightnessChange(int brightness) {
500         // TODO bug: 32065231
501     }
502 
doHandleDisplayBrightnessChange(int brightness)503     private void doHandleDisplayBrightnessChange(int brightness) {
504         //TODO bug: 32065231
505     }
506 
doHandleMainDisplayStateChange(boolean on)507     private void doHandleMainDisplayStateChange(boolean on) {
508         //TODO bug: 32065231
509     }
510 
handleMainDisplayChanged(boolean on)511     public void handleMainDisplayChanged(boolean on) {
512         PowerHandler handler;
513         synchronized (this) {
514             handler = mHandler;
515         }
516         handler.handleMainDisplayStateChange(on);
517     }
518 
getHandler()519     public synchronized Handler getHandler() {
520         return mHandler;
521     }
522 
523     private class PowerHandler extends Handler {
524 
525         private final int MSG_POWER_STATE_CHANGE = 0;
526         private final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1;
527         private final int MSG_MAIN_DISPLAY_STATE_CHANGE = 2;
528         private final int MSG_PROCESSING_COMPLETE = 3;
529         private final int MSG_NOTIFY_POWER_ON = 4;
530 
531         // Do not handle this immediately but with some delay as there can be a race between
532         // display off due to rear view camera and delivery to here.
533         private final long MAIN_DISPLAY_EVENT_DELAY_MS = 500;
534 
PowerHandler(Looper looper)535         private PowerHandler(Looper looper) {
536             super(looper);
537         }
538 
handlePowerStateChange()539         private void handlePowerStateChange() {
540             Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);
541             sendMessage(msg);
542         }
543 
handleDisplayBrightnessChange(int brightness)544         private void handleDisplayBrightnessChange(int brightness) {
545             Message msg = obtainMessage(MSG_DISPLAY_BRIGHTNESS_CHANGE, brightness, 0);
546             sendMessage(msg);
547         }
548 
handleMainDisplayStateChange(boolean on)549         private void handleMainDisplayStateChange(boolean on) {
550             removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
551             Message msg = obtainMessage(MSG_MAIN_DISPLAY_STATE_CHANGE, Boolean.valueOf(on));
552             sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS);
553         }
554 
handleProcessingComplete(boolean shutdownWhenCompleted)555         private void handleProcessingComplete(boolean shutdownWhenCompleted) {
556             removeMessages(MSG_PROCESSING_COMPLETE);
557             Message msg = obtainMessage(MSG_PROCESSING_COMPLETE, shutdownWhenCompleted ? 1 : 0, 0);
558             sendMessage(msg);
559         }
560 
handlePowerOn()561         private void handlePowerOn() {
562             Message msg = obtainMessage(MSG_NOTIFY_POWER_ON);
563             sendMessage(msg);
564         }
565 
cancelProcessingComplete()566         private void cancelProcessingComplete() {
567             removeMessages(MSG_PROCESSING_COMPLETE);
568         }
569 
cancelAll()570         private void cancelAll() {
571             removeMessages(MSG_POWER_STATE_CHANGE);
572             removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE);
573             removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
574             removeMessages(MSG_PROCESSING_COMPLETE);
575             removeMessages(MSG_NOTIFY_POWER_ON);
576         }
577 
578         @Override
handleMessage(Message msg)579         public void handleMessage(Message msg) {
580             switch (msg.what) {
581                 case MSG_POWER_STATE_CHANGE:
582                     doHandlePowerStateChange();
583                     break;
584                 case MSG_DISPLAY_BRIGHTNESS_CHANGE:
585                     doHandleDisplayBrightnessChange(msg.arg1);
586                     break;
587                 case MSG_MAIN_DISPLAY_STATE_CHANGE:
588                     doHandleMainDisplayStateChange((Boolean) msg.obj);
589                     break;
590                 case MSG_PROCESSING_COMPLETE:
591                     doHandleProcessingComplete(msg.arg1 == 1);
592                     break;
593                 case MSG_NOTIFY_POWER_ON:
594                     doHandleNotifyPowerOn();
595                     break;
596             }
597         }
598     }
599 
600     private class ShutdownProcessingTimerTask extends TimerTask {
601         private final boolean mShutdownWhenCompleted;
602         private final int mExpirationCount;
603         private int mCurrentCount;
604 
ShutdownProcessingTimerTask(boolean shutdownWhenCompleted, int expirationCount)605         private ShutdownProcessingTimerTask(boolean shutdownWhenCompleted, int expirationCount) {
606             mShutdownWhenCompleted = shutdownWhenCompleted;
607             mExpirationCount = expirationCount;
608             mCurrentCount = 0;
609         }
610 
611         @Override
run()612         public void run() {
613             mCurrentCount++;
614             if (mCurrentCount > mExpirationCount) {
615                 PowerHandler handler;
616                 synchronized (CarPowerManagementService.this) {
617                     releaseTimerLocked();
618                     handler = mHandler;
619                 }
620                 handler.handleProcessingComplete(mShutdownWhenCompleted);
621             } else {
622                 mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);
623             }
624         }
625     }
626 
627     private static class PowerEventProcessingHandlerWrapper {
628         public final PowerEventProcessingHandler handler;
629         private long mProcessingTime = 0;
630         private boolean mProcessingDone = true;
631         private boolean mPowerOnSent = false;
632         private int mLastDisplayState = -1;
633 
PowerEventProcessingHandlerWrapper(PowerEventProcessingHandler handler)634         public PowerEventProcessingHandlerWrapper(PowerEventProcessingHandler handler) {
635             this.handler = handler;
636         }
637 
setProcessingTimeAndResetProcessingDone(long processingTime)638         public synchronized void setProcessingTimeAndResetProcessingDone(long processingTime) {
639             mProcessingTime = processingTime;
640             mProcessingDone = false;
641         }
642 
getProcessingTime()643         public synchronized long getProcessingTime() {
644             return mProcessingTime;
645         }
646 
markProcessingDone()647         public synchronized void markProcessingDone() {
648             mProcessingDone = true;
649         }
650 
isProcessingDone()651         public synchronized boolean isProcessingDone() {
652             return mProcessingDone;
653         }
654 
callOnPowerOn(boolean displayOn)655         public void callOnPowerOn(boolean displayOn) {
656             int newDisplayState = displayOn ? 1 : 0;
657             boolean shouldCall = false;
658             synchronized (this) {
659                 if (!mPowerOnSent || (mLastDisplayState != newDisplayState)) {
660                     shouldCall = true;
661                     mPowerOnSent = true;
662                     mLastDisplayState = newDisplayState;
663                 }
664             }
665             if (shouldCall) {
666                 handler.onPowerOn(displayOn);
667             }
668         }
669 
670         @Override
toString()671         public String toString() {
672             return "PowerEventProcessingHandlerWrapper [handler=" + handler + ", mProcessingTime="
673                     + mProcessingTime + ", mProcessingDone=" + mProcessingDone + "]";
674         }
675     }
676 }
677