Doze: Refactor v1
Pulls appart UI, state, power, display and trigger management
into individual components controlled by a state machine.
Also adds tests.
Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
Change-Id: I6ee7e79d58a5a3ebeb975775af44ad1e5aaccf93
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 2bb1d6a..94cbdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -16,468 +16,46 @@
package com.android.systemui.doze;
-import android.app.AlarmManager;
-import android.app.UiModeManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
import android.service.dreams.DreamService;
import android.util.Log;
-import android.view.Display;
-
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-public class DozeService extends DreamService implements DozeSensors.Callback {
+public class DozeService extends DreamService implements DozeMachine.Service {
private static final String TAG = "DozeService";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ACTION_BASE = "com.android.systemui.doze";
- private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
-
- /**
- * If true, reregisters all trigger sensors when the screen turns off.
- */
- private static final boolean REREGISTER_ALL_SENSORS_ON_SCREEN_OFF = true;
-
- private final String mTag = String.format(TAG + ".%08x", hashCode());
- private final Context mContext = this;
- private final DozeParameters mDozeParameters = new DozeParameters(mContext);
- private final Handler mHandler = new Handler();
-
- private DozeHost mHost;
- private DozeSensors mDozeSensors;
- private SensorManager mSensorManager;
- private PowerManager mPowerManager;
- private PowerManager.WakeLock mWakeLock;
- private UiModeManager mUiModeManager;
- private boolean mDreaming;
- private boolean mPulsing;
- private boolean mBroadcastReceiverRegistered;
- private boolean mDisplayStateSupported;
- private boolean mPowerSaveActive;
- private boolean mCarMode;
- private long mNotificationPulseTime;
-
- private AmbientDisplayConfiguration mConfig;
- private AlarmManager mAlarmManager;
+ private DozeMachine mDozeMachine;
public DozeService() {
- if (DEBUG) Log.d(mTag, "new DozeService()");
setDebug(DEBUG);
}
@Override
- protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dumpOnHandler(fd, pw, args);
- pw.print(" mDreaming: "); pw.println(mDreaming);
- pw.print(" mPulsing: "); pw.println(mPulsing);
- pw.print(" mWakeLock: held="); pw.println(mWakeLock.isHeld());
- pw.print(" mHost: "); pw.println(mHost);
- pw.print(" mBroadcastReceiverRegistered: "); pw.println(mBroadcastReceiverRegistered);
- pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
- pw.print(" mPowerSaveActive: "); pw.println(mPowerSaveActive);
- pw.print(" mCarMode: "); pw.println(mCarMode);
- pw.print(" mNotificationPulseTime: "); pw.println(
- DozeLog.FORMAT.format(new Date(mNotificationPulseTime
- - SystemClock.elapsedRealtime() + System.currentTimeMillis())));
- mDozeParameters.dump(pw);
- }
-
- @Override
public void onCreate() {
- if (DEBUG) Log.d(mTag, "onCreate");
super.onCreate();
- if (getApplication() instanceof SystemUIApplication) {
- final SystemUIApplication app = (SystemUIApplication) getApplication();
- mHost = app.getComponent(DozeHost.class);
- }
- if (mHost == null) Log.w(TAG, "No doze service host found.");
-
setWindowless(true);
- mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
- mAlarmManager = (AlarmManager) mContext.getSystemService(AlarmManager.class);
- mConfig = new AmbientDisplayConfiguration(mContext);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mWakeLock.setReferenceCounted(true);
- mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
- mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
- turnDisplayOff();
- mDozeSensors = new DozeSensors(mContext, mSensorManager, mDozeParameters,
- mConfig, mWakeLock, this);
- }
-
- @Override
- public void onAttachedToWindow() {
- if (DEBUG) Log.d(mTag, "onAttachedToWindow");
- super.onAttachedToWindow();
+ mDozeMachine = DozeFactory.assembleMachine(this);
}
@Override
public void onDreamingStarted() {
super.onDreamingStarted();
-
- if (mHost == null) {
- finish();
- return;
- }
-
- mPowerSaveActive = mHost.isPowerSaveActive();
- mCarMode = mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
- if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze() + " mPowerSaveActive="
- + mPowerSaveActive + " mCarMode=" + mCarMode);
- if (mPowerSaveActive) {
- finishToSavePower();
- return;
- }
- if (mCarMode) {
- finishForCarMode();
- return;
- }
-
- mDreaming = true;
- listenForPulseSignals(true);
-
- // Ask the host to get things ready to start dozing.
- // Once ready, we call startDozing() at which point the CPU may suspend
- // and we will need to acquire a wakelock to do work.
- mHost.startDozing(mWakeLock.wrap(() -> {
- if (mDreaming) {
- startDozing();
-
- // From this point until onDreamingStopped we will need to hold a
- // wakelock whenever we are doing work. Note that we never call
- // stopDozing because can we just keep dozing until the bitter end.
- }
- }));
-
- if (mDozeParameters.getAlwaysOn()) {
- mTimeTick.onAlarm();
- }
+ mDozeMachine.requestState(DozeMachine.State.INITIALIZED);
+ startDozing();
}
@Override
public void onDreamingStopped() {
- if (DEBUG) Log.d(mTag, "onDreamingStopped isDozing=" + isDozing());
super.onDreamingStopped();
-
- if (mHost == null) {
- return;
- }
-
- mDreaming = false;
- listenForPulseSignals(false);
-
- // Tell the host that it's over.
- mHost.stopDozing();
- mAlarmManager.cancel(mTimeTick);
- }
-
- private void requestPulse(final int reason) {
- requestPulse(reason, false /* performedProxCheck */);
- }
-
- private void requestPulse(final int reason, boolean performedProxCheck) {
- Assert.isMainThread();
- if (mHost != null && mDreaming && !mPulsing) {
- // Let the host know we want to pulse. Wait for it to be ready, then
- // turn the screen on. When finished, turn the screen off again.
- // Here we need a wakelock to stay awake until the pulse is finished.
- mWakeLock.acquire();
- mPulsing = true;
- if (!mDozeParameters.getProxCheckBeforePulse()) {
- // skip proximity check
- continuePulsing(reason);
- return;
- }
- final long start = SystemClock.uptimeMillis();
- if (performedProxCheck) {
- // the caller already performed a successful proximity check; we'll only do one to
- // capture statistics, continue pulsing immediately.
- continuePulsing(reason);
- }
- // perform a proximity check
- new ProximityCheck() {
- @Override
- public void onProximityResult(int result) {
- final boolean isNear = result == RESULT_NEAR;
- final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(mContext, isNear, end - start, reason);
- if (performedProxCheck) {
- // we already continued
- return;
- }
- // avoid pulsing in pockets
- if (isNear) {
- mPulsing = false;
- mWakeLock.release();
- return;
- }
-
- // not in-pocket, continue pulsing
- continuePulsing(reason);
- }
- }.check();
- }
- }
-
- private void continuePulsing(int reason) {
- if (mHost.isPulsingBlocked()) {
- mPulsing = false;
- mWakeLock.release();
- return;
- }
- mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
- @Override
- public void onPulseStarted() {
- if (mPulsing && mDreaming) {
- turnDisplayOn();
- }
- }
-
- @Override
- public void onPulseFinished() {
- if (mPulsing && mDreaming) {
- mPulsing = false;
- if (REREGISTER_ALL_SENSORS_ON_SCREEN_OFF) {
- mDozeSensors.reregisterAllSensors();
- }
- turnDisplayOff();
- }
- mWakeLock.release(); // needs to be unconditional to balance acquire
- }
- }, reason);
- }
-
- private void turnDisplayOff() {
- if (DEBUG) Log.d(mTag, "Display off");
- if (mDozeParameters.getAlwaysOn()) {
- turnDisplayOn();
- } else {
- setDozeScreenState(Display.STATE_OFF);
- }
- }
-
- private void turnDisplayOn() {
- if (DEBUG) Log.d(mTag, "Display on");
- setDozeScreenState(mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON);
- }
-
- private void finishToSavePower() {
- Log.w(mTag, "Exiting ambient mode due to low power battery saver");
- finish();
- }
-
- private void finishForCarMode() {
- Log.w(mTag, "Exiting ambient mode, not allowed in car mode");
- finish();
- }
-
- private void listenForPulseSignals(boolean listen) {
- if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
- mDozeSensors.setListening(listen);
- listenForBroadcasts(listen);
- listenForNotifications(listen);
- }
-
- private void listenForBroadcasts(boolean listen) {
- if (listen) {
- final IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
- mBroadcastReceiverRegistered = true;
- } else {
- if (mBroadcastReceiverRegistered) {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
- mBroadcastReceiverRegistered = false;
- }
- }
-
- private void listenForNotifications(boolean listen) {
- if (listen) {
- mHost.addCallback(mHostCallback);
- } else {
- mHost.removeCallback(mHostCallback);
- }
- }
-
- private void requestNotificationPulse() {
- if (DEBUG) Log.d(mTag, "requestNotificationPulse");
- if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return;
- mNotificationPulseTime = SystemClock.elapsedRealtime();
- requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mDozeMachine.requestState(DozeMachine.State.FINISH);
}
@Override
- public void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck) {
- requestPulse(pulseReason, sensorPerformedProxCheck);
-
- if (pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP) {
- final long timeSinceNotification =
- SystemClock.elapsedRealtime() - mNotificationPulseTime;
- final boolean withinVibrationThreshold =
- timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
- DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
- }
-
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (PULSE_ACTION.equals(intent.getAction())) {
- if (DEBUG) Log.d(mTag, "Received pulse intent");
- requestPulse(DozeLog.PULSE_REASON_INTENT);
- }
- if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
- mCarMode = true;
- if (mCarMode && mDreaming) {
- finishForCarMode();
- }
- }
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mDozeSensors.onUserSwitched();
- }
- }
- };
-
- private AlarmManager.OnAlarmListener mTimeTick = new AlarmManager.OnAlarmListener() {
- @Override
- public void onAlarm() {
- mHost.dozeTimeTick();
-
- // Keep wakelock until a frame has been pushed.
- mHandler.post(mWakeLock.wrap(()->{}));
-
- Calendar calendar = GregorianCalendar.getInstance();
- calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.MILLISECOND, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.add(Calendar.MINUTE, 1);
-
- long delta = calendar.getTimeInMillis() - System.currentTimeMillis();
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler);
- }
- };
-
- private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
- @Override
- public void onNewNotifications() {
- if (DEBUG) Log.d(mTag, "onNewNotifications (noop)");
- // noop for now
- }
-
- @Override
- public void onBuzzBeepBlinked() {
- if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
- requestNotificationPulse();
- }
-
- @Override
- public void onNotificationLight(boolean on) {
- if (DEBUG) Log.d(mTag, "onNotificationLight (noop) on=" + on);
- // noop for now
- }
-
- @Override
- public void onPowerSaveChanged(boolean active) {
- mPowerSaveActive = active;
- if (mPowerSaveActive && mDreaming) {
- finishToSavePower();
- }
- }
- };
-
- private abstract class ProximityCheck implements SensorEventListener, Runnable {
- private static final int TIMEOUT_DELAY_MS = 500;
-
- protected static final int RESULT_UNKNOWN = 0;
- protected static final int RESULT_NEAR = 1;
- protected static final int RESULT_FAR = 2;
-
- private final String mTag = DozeService.this.mTag + ".ProximityCheck";
-
- private boolean mRegistered;
- private boolean mFinished;
- private float mMaxRange;
-
- abstract public void onProximityResult(int result);
-
- public void check() {
- if (mFinished || mRegistered) return;
- final Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- if (sensor == null) {
- if (DEBUG) Log.d(mTag, "No sensor found");
- finishWithResult(RESULT_UNKNOWN);
- return;
- }
- mDozeSensors.setDisableSensorsInterferingWithProximity(true);
-
- mMaxRange = sensor.getMaximumRange();
- mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
- mHandler);
- mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
- mRegistered = true;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.values.length == 0) {
- if (DEBUG) Log.d(mTag, "Event has no values!");
- finishWithResult(RESULT_UNKNOWN);
- } else {
- if (DEBUG) Log.d(mTag, "Event: value=" + event.values[0] + " max=" + mMaxRange);
- final boolean isNear = event.values[0] < mMaxRange;
- finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
- }
- }
-
- @Override
- public void run() {
- if (DEBUG) Log.d(mTag, "No event received before timeout");
- finishWithResult(RESULT_UNKNOWN);
- }
-
- private void finishWithResult(int result) {
- if (mFinished) return;
- if (mRegistered) {
- mHandler.removeCallbacks(this);
- mSensorManager.unregisterListener(this);
- mDozeSensors.setDisableSensorsInterferingWithProximity(false);
- mRegistered = false;
- }
- onProximityResult(result);
- mFinished = true;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // noop
- }
+ protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mDozeMachine.dump(pw);
}
}