blob: 57ccf622c8921e6712e642705c1550426c2ff8b5 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car;
import android.car.Car;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.hardware.power.ICarPower;
import android.car.hardware.power.ICarPowerStateListener;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import com.android.car.hal.PowerHalService;
import com.android.car.hal.PowerHalService.PowerState;
import com.android.car.systeminterface.SystemInterface;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class CarPowerManagementService extends ICarPower.Stub implements CarServiceBase,
PowerHalService.PowerEventListener {
/**
* Listener for other services to monitor power events.
*/
public interface PowerServiceEventListener {
/**
* Shutdown is happening
*/
void onShutdown();
/**
* Entering deep sleep.
*/
void onSleepEntry();
/**
* Got out of deep sleep.
*/
void onSleepExit();
}
/**
* Interface for components requiring processing time before shutting-down or
* entering sleep, and wake-up after shut-down.
*/
public interface PowerEventProcessingHandler {
/**
* Called before shutdown or sleep entry to allow running some processing. This call
* should only queue such task in different thread and should return quickly.
* Blocking inside this call can trigger watchdog timer which can terminate the
* whole system.
* @param shuttingDown whether system is shutting down or not (= sleep entry).
* @return time necessary to run processing in ms. should return 0 if there is no
* processing necessary.
*/
long onPrepareShutdown(boolean shuttingDown);
/**
* Called when power state is changed to ON state. Display can be either on or off.
* @param displayOn
*/
void onPowerOn(boolean displayOn);
/**
* Returns wake up time after system is fully shutdown. Power controller will power on
* the system after this time. This power on is meant for regular maintenance kind of
* operation.
* @return 0 of wake up is not necessary.
*/
int getWakeupTime();
}
private final Context mContext;
private final PowerHalService mHal;
private final SystemInterface mSystemInterface;
private final CopyOnWriteArrayList<PowerServiceEventListener> mListeners =
new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<PowerEventProcessingHandlerWrapper>
mPowerEventProcessingHandlers = new CopyOnWriteArrayList<>();
private final PowerManagerCallbackList mPowerManagerListeners = new PowerManagerCallbackList();
private final Map<IBinder, Integer> mPowerManagerListenerTokens = new ConcurrentHashMap<>();
private int mTokenValue = 1;
@GuardedBy("this")
private PowerState mCurrentState;
@GuardedBy("this")
private Timer mTimer;
@GuardedBy("this")
private long mProcessingStartTime;
@GuardedBy("this")
private long mLastSleepEntryTime;
@GuardedBy("this")
private final LinkedList<PowerState> mPendingPowerStates = new LinkedList<>();
@GuardedBy("this")
private HandlerThread mHandlerThread;
@GuardedBy("this")
private PowerHandler mHandler;
private int mBootReason;
private boolean mShutdownOnNextSuspend = false;
// TODO: Make this OEM configurable.
private final static int APP_EXTEND_MAX_MS = 10000;
private final static int SHUTDOWN_POLLING_INTERVAL_MS = 2000;
private final static int SHUTDOWN_EXTEND_MAX_MS = 5000;
private class PowerManagerCallbackList extends RemoteCallbackList<ICarPowerStateListener> {
/**
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
@Override
public void onCallbackDied(ICarPowerStateListener listener) {
Log.i(CarLog.TAG_POWER, "binderDied " + listener.asBinder());
CarPowerManagementService.this.doUnregisterListener(listener);
}
}
public CarPowerManagementService(Context context, PowerHalService powerHal,
SystemInterface systemInterface) {
mContext = context;
mHal = powerHal;
mSystemInterface = systemInterface;
}
/**
* Create a dummy instance for unit testing purpose only. Instance constructed in this way
* is not safe as members expected to be non-null are null.
*/
@VisibleForTesting
protected CarPowerManagementService() {
mContext = null;
mHal = null;
mSystemInterface = null;
mHandlerThread = null;
mHandler = new PowerHandler(Looper.getMainLooper());
}
@Override
public void init() {
synchronized (this) {
mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
mHandlerThread.start();
mHandler = new PowerHandler(mHandlerThread.getLooper());
}
mHal.setListener(this);
if (mHal.isPowerStateSupported()) {
mHal.sendBootComplete();
PowerState currentState = mHal.getCurrentPowerState();
if (currentState != null) {
onApPowerStateChange(currentState);
} else {
Log.w(CarLog.TAG_POWER, "Unable to get get current power state during "
+ "initialization");
}
} else {
Log.w(CarLog.TAG_POWER, "Vehicle hal does not support power state yet.");
onApPowerStateChange(new PowerState(PowerHalService.STATE_ON_FULL, 0));
mSystemInterface.switchToFullWakeLock();
}
mSystemInterface.startDisplayStateMonitoring(this);
}
@Override
public void release() {
HandlerThread handlerThread;
synchronized (this) {
releaseTimerLocked();
mCurrentState = null;
mHandler.cancelAll();
handlerThread = mHandlerThread;
}
handlerThread.quitSafely();
try {
handlerThread.join(1000);
} catch (InterruptedException e) {
Log.e(CarLog.TAG_POWER, "Timeout while joining for handler thread to join.");
}
mSystemInterface.stopDisplayStateMonitoring();
mListeners.clear();
mPowerEventProcessingHandlers.clear();
mPowerManagerListeners.kill();
mPowerManagerListenerTokens.clear();
mSystemInterface.releaseAllWakeLocks();
}
/**
* Register listener to monitor power event. There is no unregister counter-part and the list
* will be cleared when the service is released.
* @param listener
*/
public synchronized void registerPowerEventListener(PowerServiceEventListener listener) {
mListeners.add(listener);
}
/**
* Register PowerEventPreprocessingHandler to run pre-processing before shutdown or
* sleep entry. There is no unregister counter-part and the list
* will be cleared when the service is released.
* @param handler
*/
public synchronized void registerPowerEventProcessingHandler(
PowerEventProcessingHandler handler) {
mPowerEventProcessingHandlers.add(new PowerEventProcessingHandlerWrapper(handler));
// onPowerOn will not be called if power on notification is already done inside the
// handler thread. So request it once again here. Wrapper will have its own
// gatekeeping to prevent calling onPowerOn twice.
mHandler.handlePowerOn();
}
/**
* Notifies earlier completion of power event processing. PowerEventProcessingHandler quotes
* time necessary from onPrePowerEvent() call, but actual processing can finish earlier than
* that, and this call can be called in such case to trigger shutdown without waiting further.
*
* @param handler PowerEventProcessingHandler that was already registered with
* {@link #registerPowerEventListener(PowerServiceEventListener)} call. If it was not
* registered before, this call will be ignored.
*/
public void notifyPowerEventProcessingCompletion(PowerEventProcessingHandler handler) {
long processingTime = 0;
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
if (wrapper.handler == handler) {
wrapper.markProcessingDone();
} else if (!wrapper.isProcessingDone()) {
processingTime = Math.max(processingTime, wrapper.getProcessingTime());
}
}
synchronized (mPowerManagerListenerTokens) {
if (!mPowerManagerListenerTokens.isEmpty()) {
processingTime += APP_EXTEND_MAX_MS;
}
}
long now = SystemClock.elapsedRealtime();
long startTime;
boolean shouldShutdown = true;
PowerHandler powerHandler;
synchronized (this) {
startTime = mProcessingStartTime;
if (mCurrentState == null) {
return;
}
if (mCurrentState.mState != PowerHalService.STATE_SHUTDOWN_PREPARE) {
return;
}
if (mCurrentState.canEnterDeepSleep() && !mShutdownOnNextSuspend) {
shouldShutdown = false;
if (mLastSleepEntryTime > mProcessingStartTime && mLastSleepEntryTime < now) {
// already slept
return;
}
}
powerHandler = mHandler;
}
if ((startTime + processingTime) <= now) {
Log.i(CarLog.TAG_POWER, "Processing all done");
powerHandler.handleProcessingComplete(shouldShutdown);
}
}
@Override
public void dump(PrintWriter writer) {
writer.println("*PowerManagementService*");
writer.print("mCurrentState:" + mCurrentState);
writer.print(",mProcessingStartTime:" + mProcessingStartTime);
writer.println(",mLastSleepEntryTime:" + mLastSleepEntryTime);
writer.println("**PowerEventProcessingHandlers");
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
writer.println(wrapper.toString());
}
}
@Override
public void onBootReasonReceived(int bootReason) {
mBootReason = bootReason;
}
@Override
public void onApPowerStateChange(PowerState state) {
PowerHandler handler;
synchronized (this) {
mPendingPowerStates.addFirst(state);
handler = mHandler;
}
handler.handlePowerStateChange();
}
private void doHandlePowerStateChange() {
PowerState state = null;
PowerHandler handler;
synchronized (this) {
state = mPendingPowerStates.peekFirst();
mPendingPowerStates.clear();
if (state == null) {
return;
}
if (!needPowerStateChange(state)) {
return;
}
// now real power change happens. Whatever was queued before should be all cancelled.
releaseTimerLocked();
handler = mHandler;
}
handler.cancelProcessingComplete();
Log.i(CarLog.TAG_POWER, "Power state change:" + state);
switch (state.mState) {
case PowerHalService.STATE_ON_DISP_OFF:
handleDisplayOff(state);
notifyPowerOn(false);
break;
case PowerHalService.STATE_ON_FULL:
handleFullOn(state);
notifyPowerOn(true);
break;
case PowerHalService.STATE_SHUTDOWN_PREPARE:
handleShutdownPrepare(state);
break;
}
}
private void handleDisplayOff(PowerState newState) {
setCurrentState(newState);
mSystemInterface.setDisplayState(false);
}
private void handleFullOn(PowerState newState) {
setCurrentState(newState);
mSystemInterface.setDisplayState(true);
}
@VisibleForTesting
protected void notifyPowerOn(boolean displayOn) {
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
wrapper.callOnPowerOn(displayOn);
}
}
@VisibleForTesting
protected long notifyPrepareShutdown(boolean shuttingDown) {
long processingTimeMs = 0;
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
if (handlerProcessingTime > processingTimeMs) {
processingTimeMs = handlerProcessingTime;
}
}
// Add time for powerManager events
processingTimeMs += sendPowerManagerEvent(shuttingDown);
return processingTimeMs;
}
private void handleShutdownPrepare(PowerState newState) {
setCurrentState(newState);
mSystemInterface.setDisplayState(false);;
boolean shouldShutdown = true;
if (mHal.isDeepSleepAllowed() && mSystemInterface.isSystemSupportingDeepSleep() &&
newState.canEnterDeepSleep() && !mShutdownOnNextSuspend) {
Log.i(CarLog.TAG_POWER, "starting sleep");
shouldShutdown = false;
doHandlePreprocessing(shouldShutdown);
return;
} else if (newState.canPostponeShutdown()) {
Log.i(CarLog.TAG_POWER, "starting shutdown with processing");
doHandlePreprocessing(shouldShutdown);
} else {
Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
synchronized (this) {
releaseTimerLocked();
}
doHandleShutdown();
}
}
@GuardedBy("this")
private void releaseTimerLocked() {
if (mTimer != null) {
mTimer.cancel();
}
mTimer = null;
}
private void doHandlePreprocessing(boolean shuttingDown) {
long processingTimeMs = 0;
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
long handlerProcessingTime = wrapper.handler.onPrepareShutdown(shuttingDown);
if (handlerProcessingTime > 0) {
wrapper.setProcessingTimeAndResetProcessingDone(handlerProcessingTime);
}
if (handlerProcessingTime > processingTimeMs) {
processingTimeMs = handlerProcessingTime;
}
}
// Add time for powerManager events
processingTimeMs += sendPowerManagerEvent(shuttingDown);
if (processingTimeMs > 0) {
int pollingCount = (int)(processingTimeMs / SHUTDOWN_POLLING_INTERVAL_MS) + 1;
Log.i(CarLog.TAG_POWER, "processing before shutdown expected for :" + processingTimeMs +
" ms, adding polling:" + pollingCount);
synchronized (this) {
mProcessingStartTime = SystemClock.elapsedRealtime();
releaseTimerLocked();
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new ShutdownProcessingTimerTask(shuttingDown,
pollingCount),
0 /*delay*/,
SHUTDOWN_POLLING_INTERVAL_MS);
}
} else {
PowerHandler handler;
synchronized (this) {
handler = mHandler;
}
handler.handleProcessingComplete(shuttingDown);
}
}
private long sendPowerManagerEvent(boolean shuttingDown) {
long processingTimeMs = 0;
int newState = shuttingDown ? CarPowerStateListener.SHUTDOWN_ENTER :
CarPowerStateListener.SUSPEND_ENTER;
synchronized (mPowerManagerListenerTokens) {
mPowerManagerListenerTokens.clear();
int i = mPowerManagerListeners.beginBroadcast();
while (i-- > 0) {
try {
ICarPowerStateListener listener = mPowerManagerListeners.getBroadcastItem(i);
listener.onStateChanged(newState, mTokenValue);
mPowerManagerListenerTokens.put(listener.asBinder(), mTokenValue);
mTokenValue++;
} catch (RemoteException e) {
// Its likely the connection snapped. Let binder death handle the situation.
Log.e(CarLog.TAG_POWER, "onStateChanged calling failed: " + e);
}
}
mPowerManagerListeners.finishBroadcast();
if (!mPowerManagerListenerTokens.isEmpty()) {
Log.i(CarLog.TAG_POWER, "mPowerMangerListenerTokens not empty, add APP_EXTEND_MAX_MS");
processingTimeMs += APP_EXTEND_MAX_MS;
}
}
return processingTimeMs;
}
private void doHandleDeepSleep() {
// keep holding partial wakelock to prevent entering sleep before enterDeepSleep call
// enterDeepSleep should force sleep entry even if wake lock is kept.
mSystemInterface.switchToPartialWakeLock();
PowerHandler handler;
synchronized (this) {
handler = mHandler;
}
handler.cancelProcessingComplete();
for (PowerServiceEventListener listener : mListeners) {
listener.onSleepEntry();
}
int wakeupTimeSec = getWakeupTime();
mHal.sendSleepEntry();
synchronized (this) {
mLastSleepEntryTime = SystemClock.elapsedRealtime();
}
if (mSystemInterface.enterDeepSleep(wakeupTimeSec) == false) {
// System did not suspend. Need to shutdown
// TODO: Shutdown gracefully
Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Need to shutdown");
}
mHal.sendSleepExit();
for (PowerServiceEventListener listener : mListeners) {
listener.onSleepExit();
}
// Notify applications
int i = mPowerManagerListeners.beginBroadcast();
while (i-- > 0) {
try {
ICarPowerStateListener listener = mPowerManagerListeners.getBroadcastItem(i);
listener.onStateChanged(CarPowerStateListener.SUSPEND_EXIT, 0);
} catch (RemoteException e) {
// Its likely the connection snapped. Let binder death handle the situation.
Log.e(CarLog.TAG_POWER, "onStateChanged calling failed: " + e);
}
}
mPowerManagerListeners.finishBroadcast();
if (mSystemInterface.isWakeupCausedByTimer()) {
doHandlePreprocessing(false /*shuttingDown*/);
} else {
PowerState currentState = mHal.getCurrentPowerState();
if (currentState != null && needPowerStateChange(currentState)) {
onApPowerStateChange(currentState);
} else { // power controller woke-up but no power state change. Just shutdown.
Log.w(CarLog.TAG_POWER, "external sleep wake up, but no power state change:" +
currentState);
doHandleShutdown();
}
}
}
private void doHandleNotifyPowerOn() {
boolean displayOn = false;
synchronized (this) {
if (mCurrentState != null && mCurrentState.mState == PowerHalService.STATE_ON_FULL) {
displayOn = true;
}
}
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
// wrapper will not send it forward if it is already called.
wrapper.callOnPowerOn(displayOn);
}
}
private boolean needPowerStateChange(PowerState newState) {
synchronized (this) {
if (mCurrentState != null && mCurrentState.equals(newState)) {
return false;
}
return true;
}
}
private void doHandleShutdown() {
// now shutdown
for (PowerServiceEventListener listener : mListeners) {
listener.onShutdown();
}
int wakeupTimeSec = 0;
if (mHal.isTimedWakeupAllowed()) {
wakeupTimeSec = getWakeupTime();
}
mHal.sendShutdownStart(wakeupTimeSec);
mSystemInterface.shutdown();
}
private int getWakeupTime() {
int wakeupTimeSec = 0;
for (PowerEventProcessingHandlerWrapper wrapper : mPowerEventProcessingHandlers) {
int t = wrapper.handler.getWakeupTime();
if (t > wakeupTimeSec) {
wakeupTimeSec = t;
}
}
return wakeupTimeSec;
}
private void doHandleProcessingComplete(boolean shutdownWhenCompleted) {
synchronized (this) {
releaseTimerLocked();
if (!shutdownWhenCompleted && mLastSleepEntryTime > mProcessingStartTime) {
// entered sleep after processing start. So this could be duplicate request.
Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
return;
}
}
if (shutdownWhenCompleted) {
doHandleShutdown();
} else {
doHandleDeepSleep();
}
}
private synchronized void setCurrentState(PowerState state) {
mCurrentState = state;
}
@Override
public void onDisplayBrightnessChange(int brightness) {
PowerHandler handler;
synchronized (this) {
handler = mHandler;
}
handler.handleDisplayBrightnessChange(brightness);
}
private void doHandleDisplayBrightnessChange(int brightness) {
mSystemInterface.setDisplayBrightness(brightness);
}
private void doHandleMainDisplayStateChange(boolean on) {
Log.w(CarLog.TAG_POWER, "Unimplemented: doHandleMainDisplayStateChange() - on = " + on);
}
public void handleMainDisplayChanged(boolean on) {
PowerHandler handler;
synchronized (this) {
handler = mHandler;
}
handler.handleMainDisplayStateChange(on);
}
/**
* Send display brightness to VHAL.
* @param brightness value 0-100%
*/
public void sendDisplayBrightness(int brightness) {
mHal.sendDisplayBrightness(brightness);
}
public synchronized Handler getHandler() {
return mHandler;
}
// Binder interface for CarPowerManager
@Override
public void registerListener(ICarPowerStateListener listener) {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
mPowerManagerListeners.register(listener);
}
@Override
public void unregisterListener(ICarPowerStateListener listener) {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
doUnregisterListener(listener);
}
private void doUnregisterListener(ICarPowerStateListener listener) {
boolean found = mPowerManagerListeners.unregister(listener);
if (found) {
// Remove outstanding token if there is one
IBinder binder = listener.asBinder();
synchronized (mPowerManagerListenerTokens) {
if (mPowerManagerListenerTokens.containsKey(binder)) {
int token = mPowerManagerListenerTokens.get(binder);
finishedLocked(binder, token);
}
}
}
}
@Override
public void requestShutdownOnNextSuspend() {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
mShutdownOnNextSuspend = true;
}
@Override
public int getBootReason() {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
// Return the most recent bootReason value
return mBootReason;
}
@Override
public void finished(ICarPowerStateListener listener, int token) {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
synchronized (mPowerManagerListenerTokens) {
finishedLocked(listener.asBinder(), token);
}
}
private void finishedLocked(IBinder binder, int token) {
int currentToken = mPowerManagerListenerTokens.get(binder);
if (currentToken == token) {
mPowerManagerListenerTokens.remove(binder);
if (mPowerManagerListenerTokens.isEmpty() &&
(mCurrentState.mState == PowerHalService.STATE_SHUTDOWN_PREPARE)) {
// All apps are ready to shutdown/suspend.
Log.i(CarLog.TAG_POWER, "Apps are finished, call notifyPowerEventProcessingCompletion");
notifyPowerEventProcessingCompletion(null);
}
}
}
private class PowerHandler extends Handler {
private final int MSG_POWER_STATE_CHANGE = 0;
private final int MSG_DISPLAY_BRIGHTNESS_CHANGE = 1;
private final int MSG_MAIN_DISPLAY_STATE_CHANGE = 2;
private final int MSG_PROCESSING_COMPLETE = 3;
private final int MSG_NOTIFY_POWER_ON = 4;
// Do not handle this immediately but with some delay as there can be a race between
// display off due to rear view camera and delivery to here.
private final long MAIN_DISPLAY_EVENT_DELAY_MS = 500;
private PowerHandler(Looper looper) {
super(looper);
}
private void handlePowerStateChange() {
Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);
sendMessage(msg);
}
private void handleDisplayBrightnessChange(int brightness) {
Message msg = obtainMessage(MSG_DISPLAY_BRIGHTNESS_CHANGE, brightness, 0);
sendMessage(msg);
}
private void handleMainDisplayStateChange(boolean on) {
removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
Message msg = obtainMessage(MSG_MAIN_DISPLAY_STATE_CHANGE, Boolean.valueOf(on));
sendMessageDelayed(msg, MAIN_DISPLAY_EVENT_DELAY_MS);
}
private void handleProcessingComplete(boolean shutdownWhenCompleted) {
removeMessages(MSG_PROCESSING_COMPLETE);
Message msg = obtainMessage(MSG_PROCESSING_COMPLETE, shutdownWhenCompleted ? 1 : 0, 0);
sendMessage(msg);
}
private void handlePowerOn() {
Message msg = obtainMessage(MSG_NOTIFY_POWER_ON);
sendMessage(msg);
}
private void cancelProcessingComplete() {
removeMessages(MSG_PROCESSING_COMPLETE);
}
private void cancelAll() {
removeMessages(MSG_POWER_STATE_CHANGE);
removeMessages(MSG_DISPLAY_BRIGHTNESS_CHANGE);
removeMessages(MSG_MAIN_DISPLAY_STATE_CHANGE);
removeMessages(MSG_PROCESSING_COMPLETE);
removeMessages(MSG_NOTIFY_POWER_ON);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_POWER_STATE_CHANGE:
doHandlePowerStateChange();
break;
case MSG_DISPLAY_BRIGHTNESS_CHANGE:
doHandleDisplayBrightnessChange(msg.arg1);
break;
case MSG_MAIN_DISPLAY_STATE_CHANGE:
doHandleMainDisplayStateChange((Boolean) msg.obj);
break;
case MSG_PROCESSING_COMPLETE:
doHandleProcessingComplete(msg.arg1 == 1);
break;
case MSG_NOTIFY_POWER_ON:
doHandleNotifyPowerOn();
break;
}
}
}
private class ShutdownProcessingTimerTask extends TimerTask {
private final boolean mShutdownWhenCompleted;
private final int mExpirationCount;
private int mCurrentCount;
private ShutdownProcessingTimerTask(boolean shutdownWhenCompleted, int expirationCount) {
mShutdownWhenCompleted = shutdownWhenCompleted;
mExpirationCount = expirationCount;
mCurrentCount = 0;
}
@Override
public void run() {
mCurrentCount++;
if (mCurrentCount > mExpirationCount) {
PowerHandler handler;
synchronized (CarPowerManagementService.this) {
releaseTimerLocked();
handler = mHandler;
}
handler.handleProcessingComplete(mShutdownWhenCompleted);
} else {
mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);
}
}
}
private static class PowerEventProcessingHandlerWrapper {
public final PowerEventProcessingHandler handler;
private long mProcessingTime = 0;
private boolean mProcessingDone = true;
private boolean mPowerOnSent = false;
private int mLastDisplayState = -1;
public PowerEventProcessingHandlerWrapper(PowerEventProcessingHandler handler) {
this.handler = handler;
}
public synchronized void setProcessingTimeAndResetProcessingDone(long processingTime) {
mProcessingTime = processingTime;
mProcessingDone = false;
}
public synchronized long getProcessingTime() {
return mProcessingTime;
}
public synchronized void markProcessingDone() {
mProcessingDone = true;
}
public synchronized boolean isProcessingDone() {
return mProcessingDone;
}
public void callOnPowerOn(boolean displayOn) {
int newDisplayState = displayOn ? 1 : 0;
boolean shouldCall = false;
synchronized (this) {
if (!mPowerOnSent || (mLastDisplayState != newDisplayState)) {
shouldCall = true;
mPowerOnSent = true;
mLastDisplayState = newDisplayState;
}
}
if (shouldCall) {
handler.onPowerOn(displayOn);
}
}
@Override
public String toString() {
return "PowerEventProcessingHandlerWrapper [handler=" + handler + ", mProcessingTime="
+ mProcessingTime + ", mProcessingDone=" + mProcessingDone + "]";
}
}
}