Merge "Doze: Refactor v1"
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
new file mode 100644
index 0000000..4cfc811
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import android.app.Application;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.PowerManager;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.statusbar.phone.DozeParameters;
+
+public class DozeFactory {
+
+ /** Creates a DozeMachine with its parts for {@code dozeService}. */
+ public static DozeMachine assembleMachine(DozeService dozeService) {
+ Context context = dozeService;
+ SensorManager sensorManager = context.getSystemService(SensorManager.class);
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+
+ DozeHost host = getHost(dozeService);
+ AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
+ DozeParameters params = new DozeParameters(context);
+ Handler handler = new Handler();
+ DozeFactory.WakeLock wakeLock = new DozeFactory.WakeLock(powerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, "Doze"));
+
+ DozeMachine machine = new DozeMachine(dozeService, params, wakeLock);
+ machine.setParts(new DozeMachine.Part[]{
+ new DozeTriggers(context, machine, host, config, params,
+ sensorManager, handler, wakeLock),
+ new DozeUi(context, machine, wakeLock, host),
+ });
+
+ return machine;
+ }
+
+ private static DozeHost getHost(DozeService service) {
+ Application appCandidate = service.getApplication();
+ final SystemUIApplication app = (SystemUIApplication) appCandidate;
+ return app.getComponent(DozeHost.class);
+ }
+
+ /** A wrapper around {@link PowerManager.WakeLock} for testability. */
+ public static class WakeLock {
+ private final PowerManager.WakeLock mInner;
+
+ public WakeLock(PowerManager.WakeLock inner) {
+ mInner = inner;
+ }
+
+ /** @see PowerManager.WakeLock#acquire() */
+ public void acquire() {
+ mInner.acquire();
+ }
+
+ /** @see PowerManager.WakeLock#release() */
+ public void release() {
+ mInner.release();
+ }
+
+ /** @see PowerManager.WakeLock#wrap(Runnable) */
+ public Runnable wrap(Runnable runnable) {
+ return mInner.wrap(runnable);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 02a98b0..fb940b5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -24,7 +24,7 @@
public interface DozeHost {
void addCallback(@NonNull Callback callback);
void removeCallback(@NonNull Callback callback);
- void startDozing(@NonNull Runnable ready);
+ void startDozing();
void pulseWhileDozing(@NonNull PulseCallback callback, int reason);
void stopDozing();
void dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
new file mode 100644
index 0000000..c633aa1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import android.annotation.MainThread;
+import android.util.Log;
+import android.view.Display;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.Assert;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Orchestrates all things doze.
+ *
+ * DozeMachine implements a state machine that orchestrates how the UI and triggers work and
+ * interfaces with the power and screen states.
+ *
+ * During state transitions and in certain states, DozeMachine holds a wake lock.
+ */
+public class DozeMachine {
+
+ static final String TAG = "DozeMachine";
+ static final boolean DEBUG = DozeService.DEBUG;
+
+ enum State {
+ /** Default state. Transition to INITIALIZED to get Doze going. */
+ UNINITIALIZED,
+ /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
+ INITIALIZED,
+ /** Regular doze. Device is asleep and listening for pulse triggers. */
+ DOZE,
+ /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
+ DOZE_AOD,
+ /** Pulse has been requested. Device is awake and preparing UI */
+ DOZE_REQUEST_PULSE,
+ /** Pulse is showing. Device is awake and showing UI. */
+ DOZE_PULSING,
+ /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
+ DOZE_PULSE_DONE,
+ /** Doze is done. DozeService is finished. */
+ FINISH,
+ }
+
+ private final Service mDozeService;
+ private final DozeFactory.WakeLock mWakeLock;
+ private final DozeParameters mParams;
+ private Part[] mParts;
+
+ private final ArrayList<State> mQueuedRequests = new ArrayList<>();
+ private State mState = State.UNINITIALIZED;
+ private boolean mWakeLockHeldForCurrentState = false;
+
+ public DozeMachine(Service service, DozeParameters params, DozeFactory.WakeLock wakeLock) {
+ mDozeService = service;
+ mParams = params;
+ mWakeLock = wakeLock;
+ }
+
+ /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
+ public void setParts(Part[] parts) {
+ Preconditions.checkState(mParts == null);
+ mParts = parts;
+ }
+
+ /**
+ * Requests transitioning to {@code requestedState}.
+ *
+ * This can be called during a state transition, in which case it will be queued until all
+ * queued state transitions are done.
+ *
+ * A wake lock is held while the transition is happening.
+ *
+ * Note that {@link #transitionPolicy} can modify what state will be transitioned to.
+ */
+ @MainThread
+ public void requestState(State requestedState) {
+ Assert.isMainThread();
+ if (DEBUG) {
+ Log.i(TAG, "request: current=" + mState + " req=" + requestedState,
+ new Throwable("here"));
+ }
+
+ boolean runNow = !isExecutingTransition();
+ mQueuedRequests.add(requestedState);
+ if (runNow) {
+ mWakeLock.acquire();
+ for (int i = 0; i < mQueuedRequests.size(); i++) {
+ // Transitions in Parts can call back into requestState, which will
+ // cause mQueuedRequests to grow.
+ transitionTo(mQueuedRequests.get(i));
+ }
+ mQueuedRequests.clear();
+ mWakeLock.release();
+ }
+ }
+
+ /**
+ * @return the current state.
+ *
+ * This must not be called during a transition.
+ */
+ @MainThread
+ public State getState() {
+ Assert.isMainThread();
+ Preconditions.checkState(!isExecutingTransition());
+ return mState;
+ }
+
+ private boolean isExecutingTransition() {
+ return !mQueuedRequests.isEmpty();
+ }
+
+ private void transitionTo(State requestedState) {
+ State newState = transitionPolicy(requestedState);
+
+ if (DEBUG) {
+ Log.i(TAG, "transition: old=" + mState + " req=" + requestedState + " new=" + newState);
+ }
+
+ if (newState == mState) {
+ return;
+ }
+
+ validateTransition(newState);
+
+ State oldState = mState;
+ mState = newState;
+
+ performTransitionOnComponents(oldState, newState);
+ updateScreenState(newState);
+ updateWakeLockState(newState);
+
+ resolveIntermediateState(newState);
+ }
+
+ private void performTransitionOnComponents(State oldState, State newState) {
+ for (Part p : mParts) {
+ p.transitionTo(oldState, newState);
+ }
+
+ switch (newState) {
+ case FINISH:
+ mDozeService.finish();
+ break;
+ default:
+ }
+ }
+
+ private void validateTransition(State newState) {
+ switch (mState) {
+ case FINISH:
+ Preconditions.checkState(newState == State.FINISH);
+ break;
+ case UNINITIALIZED:
+ Preconditions.checkState(newState == State.INITIALIZED);
+ break;
+ }
+ switch (newState) {
+ case UNINITIALIZED:
+ throw new IllegalArgumentException("can't go to UNINITIALIZED");
+ case INITIALIZED:
+ Preconditions.checkState(mState == State.UNINITIALIZED);
+ break;
+ case DOZE_PULSING:
+ Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
+ break;
+ case DOZE_PULSE_DONE:
+ Preconditions.checkState(mState == State.DOZE_PULSING);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private int screenPolicy(State newState) {
+ switch (newState) {
+ case UNINITIALIZED:
+ case INITIALIZED:
+ case DOZE:
+ return Display.STATE_OFF;
+ case DOZE_PULSING:
+ case DOZE_AOD:
+ return Display.STATE_DOZE; // TODO: use STATE_ON if appropriate.
+ default:
+ return Display.STATE_UNKNOWN;
+ }
+ }
+
+ private boolean wakeLockPolicy(State newState) {
+ switch (newState) {
+ case DOZE_REQUEST_PULSE:
+ case DOZE_PULSING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private State transitionPolicy(State requestedState) {
+ if (mState == State.FINISH) {
+ return State.FINISH;
+ }
+ return requestedState;
+ }
+
+ private void updateWakeLockState(State newState) {
+ boolean newPolicy = wakeLockPolicy(newState);
+ if (mWakeLockHeldForCurrentState && !newPolicy) {
+ mWakeLock.release();
+ } else if (!mWakeLockHeldForCurrentState && newPolicy) {
+ mWakeLock.acquire();
+ }
+ }
+
+ private void updateScreenState(State newState) {
+ int state = screenPolicy(newState);
+ if (state != Display.STATE_UNKNOWN) {
+ mDozeService.setDozeScreenState(state);
+ }
+ }
+
+ private void resolveIntermediateState(State state) {
+ switch (state) {
+ case INITIALIZED:
+ case DOZE_PULSE_DONE:
+ transitionTo(mParams.getAlwaysOn()
+ ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /** Dumps the current state */
+ public void dump(PrintWriter pw) {
+ pw.print(" state="); pw.println(mState);
+ pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
+ pw.println("Parts:");
+ for (Part p : mParts) {
+ p.dump(pw);
+ }
+ }
+
+ /** A part of the DozeMachine that needs to be notified about state changes. */
+ public interface Part {
+ /**
+ * Transition from {@code oldState} to {@code newState}.
+ *
+ * This method is guaranteed to only be called while a wake lock is held.
+ */
+ void transitionTo(State oldState, State newState);
+
+ /** Dump current state. For debugging only. */
+ default void dump(PrintWriter pw) {}
+ }
+
+ /** A wrapper interface for {@link android.service.dreams.DreamService} */
+ public interface Service {
+ /** Finish dreaming. */
+ void finish();
+
+ /** Request a display state. See {@link android.view.Display#STATE_DOZE}. */
+ void setDozeScreenState(int state);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 81dfafc..bb4ea2d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -11,16 +11,11 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.doze;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
-import com.android.systemui.statusbar.phone.DozeParameters;
-
import android.annotation.AnyThread;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -32,12 +27,17 @@
import android.hardware.TriggerEventListener;
import android.net.Uri;
import android.os.Handler;
-import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
+import com.android.systemui.statusbar.phone.DozeParameters;
+
+import java.io.PrintWriter;
import java.util.List;
public class DozeSensors {
@@ -53,14 +53,14 @@
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
- private final PowerManager.WakeLock mWakeLock;
+ private final DozeFactory.WakeLock mWakeLock;
private final Callback mCallback;
private final Handler mHandler = new Handler();
public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
- AmbientDisplayConfiguration config, PowerManager.WakeLock wakeLock, Callback callback) {
+ AmbientDisplayConfiguration config, DozeFactory.WakeLock wakeLock, Callback callback) {
mContext = context;
mSensorManager = sensorManager;
mDozeParameters = dozeParameters;
@@ -144,6 +144,13 @@
mPickupSensor.setDisabled(disable);
}
+ /** Dump current state */
+ public void dump(PrintWriter pw) {
+ for (TriggerSensor s : mSensors) {
+ pw.print("Sensor: "); pw.println(s.toString());
+ }
+ }
+
private class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
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);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
new file mode 100644
index 0000000..9df8113
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+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.SystemClock;
+import android.os.UserHandle;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.Assert;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles triggers for ambient state changes.
+ */
+public class DozeTriggers implements DozeMachine.Part {
+
+ private static final String TAG = "DozeTriggers";
+
+ /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
+ private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
+
+ private final Context mContext;
+ private final DozeMachine mMachine;
+ private final DozeSensors mDozeSensors;
+ private final DozeHost mDozeHost;
+ private final AmbientDisplayConfiguration mConfig;
+ private final DozeParameters mDozeParameters;
+ private final SensorManager mSensorManager;
+ private final Handler mHandler;
+ private final DozeFactory.WakeLock mWakeLock;
+ private final UiModeManager mUiModeManager;
+ private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
+
+ private long mNotificationPulseTime;
+ private boolean mPulsePending;
+
+
+ public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
+ AmbientDisplayConfiguration config,
+ DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
+ DozeFactory.WakeLock wakeLock) {
+ mContext = context;
+ mMachine = machine;
+ mDozeHost = dozeHost;
+ mConfig = config;
+ mDozeParameters = dozeParameters;
+ mSensorManager = sensorManager;
+ mHandler = handler;
+ mWakeLock = wakeLock;
+ mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config,
+ wakeLock, this::onSensor);
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ }
+
+ private void onNotification() {
+ if (DozeMachine.DEBUG) Log.d(TAG, "requestNotificationPulse");
+ mNotificationPulseTime = SystemClock.elapsedRealtime();
+ if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return;
+ requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
+ }
+
+ private void onWhisper() {
+ requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
+ }
+
+ private void onSensor(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 void onCarMode() {
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+
+ private void onPowerSave() {
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case INITIALIZED:
+ mBroadcastReceiver.register(mContext);
+ mDozeHost.addCallback(mHostCallback);
+ checkTriggersAtInit();
+ break;
+ case DOZE:
+ case DOZE_AOD:
+ mDozeSensors.setListening(true);
+ if (oldState != DozeMachine.State.INITIALIZED) {
+ mDozeSensors.reregisterAllSensors();
+ }
+ break;
+ case FINISH:
+ mBroadcastReceiver.unregister(mContext);
+ mDozeHost.removeCallback(mHostCallback);
+ mDozeSensors.setListening(false);
+ break;
+ default:
+ }
+ }
+
+ private void checkTriggersAtInit() {
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+ onCarMode();
+ }
+ if (mDozeHost.isPowerSaveActive()) {
+ onPowerSave();
+ }
+ }
+
+ private void requestPulse(final int reason, boolean performedProxCheck) {
+ Assert.isMainThread();
+ if (mPulsePending || !canPulse()) {
+ return;
+ }
+
+ mPulsePending = true;
+ if (!mDozeParameters.getProxCheckBeforePulse() || performedProxCheck) {
+ // skip proximity check
+ continuePulseRequest(reason);
+ return;
+ }
+
+ final long start = SystemClock.uptimeMillis();
+ new ProximityCheck() {
+ @Override
+ public void onProximityResult(int result) {
+ final long end = SystemClock.uptimeMillis();
+ DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
+ end - start, reason);
+ if (performedProxCheck) {
+ // we already continued
+ return;
+ }
+ // avoid pulsing in pockets
+ if (result == RESULT_NEAR) {
+ return;
+ }
+
+ // not in-pocket, continue pulsing
+ continuePulseRequest(reason);
+ }
+ }.check();
+ }
+
+ private boolean canPulse() {
+ return mMachine.getState() == DozeMachine.State.DOZE
+ || mMachine.getState() == DozeMachine.State.DOZE_AOD;
+ }
+
+ private void continuePulseRequest(int reason) {
+ mPulsePending = false;
+ if (mDozeHost.isPulsingBlocked() || !canPulse()) {
+ return;
+ }
+ mMachine.requestState(DozeMachine.State.DOZE_REQUEST_PULSE);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(" notificationPulseTime=");
+ pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
+
+ pw.print(" pulsePending="); pw.println(mPulsePending);
+ pw.println("DozeSensors:");
+ mDozeSensors.dump(pw);
+ }
+
+ 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 boolean mRegistered;
+ private boolean mFinished;
+ private float mMaxRange;
+
+ protected abstract void onProximityResult(int result);
+
+ public void check() {
+ Preconditions.checkState(!mFinished && !mRegistered);
+ final Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (sensor == null) {
+ if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: 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);
+ mWakeLock.acquire();
+ mRegistered = true;
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.values.length == 0) {
+ if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
+ finishWithResult(RESULT_UNKNOWN);
+ } else {
+ if (DozeMachine.DEBUG) {
+ Log.d(TAG, "ProxCheck: 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 (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
+ finishWithResult(RESULT_UNKNOWN);
+ }
+
+ private void finishWithResult(int result) {
+ if (mFinished) return;
+ boolean wasRegistered = mRegistered;
+ if (mRegistered) {
+ mHandler.removeCallbacks(this);
+ mSensorManager.unregisterListener(this);
+ mDozeSensors.setDisableSensorsInterferingWithProximity(false);
+ mRegistered = false;
+ }
+ onProximityResult(result);
+ if (wasRegistered) {
+ mWakeLock.release();
+ }
+ mFinished = true;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // noop
+ }
+ }
+
+ private class TriggerReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PULSE_ACTION.equals(intent.getAction())) {
+ if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
+ requestPulse(DozeLog.PULSE_REASON_INTENT, false /* performedProxCheck */);
+ }
+ if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+ onCarMode();
+ }
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ mDozeSensors.onUserSwitched();
+ }
+ }
+
+ public void register(Context context) {
+ IntentFilter filter = new IntentFilter(PULSE_ACTION);
+ filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ context.registerReceiver(this, filter);
+ }
+
+ public void unregister(Context context) {
+ context.unregisterReceiver(this);
+ }
+ }
+
+ private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
+ @Override
+ public void onNewNotifications() {
+ }
+
+ @Override
+ public void onBuzzBeepBlinked() {
+ onNotification();
+ }
+
+ @Override
+ public void onNotificationLight(boolean on) {
+
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean active) {
+ if (active) {
+ onPowerSave();
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
new file mode 100644
index 0000000..95e49ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import android.content.Context;
+
+/**
+ * The policy controlling doze.
+ */
+public class DozeUi implements DozeMachine.Part {
+
+ private final Context mContext;
+ private final DozeHost mHost;
+ private DozeFactory.WakeLock mWakeLock;
+ private DozeMachine mMachine;
+
+ public DozeUi(Context context, DozeMachine machine, DozeFactory.WakeLock wakeLock,
+ DozeHost host) {
+ mContext = context;
+ mMachine = machine;
+ mWakeLock = wakeLock;
+ mHost = host;
+ }
+
+ private void pulseWhileDozing(int reason) {
+ mHost.pulseWhileDozing(
+ new DozeHost.PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ mMachine.requestState(DozeMachine.State.DOZE_PULSING);
+ }
+
+ @Override
+ public void onPulseFinished() {
+ mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
+ }
+ }, reason);
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case DOZE_REQUEST_PULSE:
+ pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
+ break;
+ case INITIALIZED:
+ mHost.startDozing();
+ break;
+ case FINISH:
+ mHost.stopDozing();
+ break;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 669a512..c33d91a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -5059,7 +5059,6 @@
private static final long PROCESSING_TIME = 500;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- private final H mHandler = new H();
// Keeps the last reported state by fireNotificationLight.
private boolean mNotificationLightOn;
@@ -5105,18 +5104,39 @@
}
@Override
- public void startDozing(@NonNull Runnable ready) {
- mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget();
+ public void startDozing() {
+ if (!mDozingRequested) {
+ mDozingRequested = true;
+ DozeLog.traceDozing(mContext, mDozing);
+ updateDozing();
+ }
}
@Override
public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, reason, 0, callback).sendToTarget();
+ mDozeScrimController.pulse(new PulseCallback() {
+
+ @Override
+ public void onPulseStarted() {
+ callback.onPulseStarted();
+ mStackScroller.setPulsing(true);
+ }
+
+ @Override
+ public void onPulseFinished() {
+ callback.onPulseFinished();
+ mStackScroller.setPulsing(false);
+ }
+ }, reason);
}
@Override
public void stopDozing() {
- mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget();
+ if (mDozingRequested) {
+ mDozingRequested = false;
+ DozeLog.traceDozing(mContext, mDozing);
+ updateDozing();
+ }
}
@Override
@@ -5140,59 +5160,5 @@
return mNotificationLightOn;
}
- private void handleStartDozing(@NonNull Runnable ready) {
- if (!mDozingRequested) {
- mDozingRequested = true;
- DozeLog.traceDozing(mContext, mDozing);
- updateDozing();
- }
- ready.run();
- }
-
- private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- mDozeScrimController.pulse(new PulseCallback() {
-
- @Override
- public void onPulseStarted() {
- callback.onPulseStarted();
- mStackScroller.setPulsing(true);
- }
-
- @Override
- public void onPulseFinished() {
- callback.onPulseFinished();
- mStackScroller.setPulsing(false);
- }
- }, reason);
- }
-
- private void handleStopDozing() {
- if (mDozingRequested) {
- mDozingRequested = false;
- DozeLog.traceDozing(mContext, mDozing);
- updateDozing();
- }
- }
-
- private final class H extends Handler {
- private static final int MSG_START_DOZING = 1;
- private static final int MSG_PULSE_WHILE_DOZING = 2;
- private static final int MSG_STOP_DOZING = 3;
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START_DOZING:
- handleStartDozing((Runnable) msg.obj);
- break;
- case MSG_PULSE_WHILE_DOZING:
- handlePulseWhileDozing((PulseCallback) msg.obj, msg.arg1);
- break;
- case MSG_STOP_DOZING:
- handleStopDozing();
- break;
- }
- }
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
new file mode 100644
index 0000000..ba7c923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
+import static com.android.systemui.doze.DozeMachine.State.FINISH;
+import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
+import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+
+import com.android.systemui.statusbar.phone.DozeParameters;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DozeMachineTest {
+
+ DozeMachine mMachine;
+
+ private DozeServiceFake mServiceFake;
+ private WakeLockFake mWakeLockFake;
+ private DozeParameters mParamsMock;
+ private DozeMachine.Part mPartMock;
+
+ @Before
+ public void setUp() {
+ mServiceFake = new DozeServiceFake();
+ mWakeLockFake = new WakeLockFake();
+ mParamsMock = mock(DozeParameters.class);
+ mPartMock = mock(DozeMachine.Part.class);
+
+ mMachine = new DozeMachine(mServiceFake, mParamsMock, mWakeLockFake);
+
+ mMachine.setParts(new DozeMachine.Part[]{mPartMock});
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInitialize_initializesParts() {
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(UNINITIALIZED, INITIALIZED);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInitialize_goesToDoze() {
+ when(mParamsMock.getAlwaysOn()).thenReturn(false);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInitialize_goesToAod() {
+ when(mParamsMock.getAlwaysOn()).thenReturn(true);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE_AOD);
+ assertEquals(DOZE_AOD, mMachine.getState());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testPulseDone_goesToDoze() {
+ when(mParamsMock.getAlwaysOn()).thenReturn(false);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestState(DOZE_PULSING);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testPulseDone_goesToAoD() {
+ when(mParamsMock.getAlwaysOn()).thenReturn(true);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestState(DOZE_PULSING);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE_AOD);
+ assertEquals(DOZE_AOD, mMachine.getState());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testFinished_staysFinished() {
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(FINISH);
+ reset(mPartMock);
+
+ mMachine.requestState(DOZE);
+
+ verify(mPartMock, never()).transitionTo(any(), any());
+ assertEquals(FINISH, mMachine.getState());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testFinish_finishesService() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(FINISH);
+
+ assertTrue(mServiceFake.finished);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWakeLock_heldInTransition() {
+ doAnswer((inv) -> {
+ assertTrue(mWakeLockFake.isHeld());
+ return null;
+ }).when(mPartMock).transitionTo(any(), any());
+
+ mMachine.requestState(INITIALIZED);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWakeLock_heldInPulseStates() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+ assertTrue(mWakeLockFake.isHeld());
+
+ mMachine.requestState(DOZE_PULSING);
+ assertTrue(mWakeLockFake.isHeld());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWakeLock_notHeldInDozeStates() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE);
+ assertFalse(mWakeLockFake.isHeld());
+
+ mMachine.requestState(DOZE_AOD);
+ assertFalse(mWakeLockFake.isHeld());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testScreen_offInDoze() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE);
+
+ assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testScreen_onInAod() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE_AOD);
+
+ assertEquals(Display.STATE_DOZE, mServiceFake.screenState);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testScreen_onInPulse() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestState(DOZE_PULSING);
+
+ assertEquals(Display.STATE_DOZE, mServiceFake.screenState);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testScreen_offInRequestPulseWithoutAoD() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE);
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+
+ assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testScreen_onInRequestPulseWithoutAoD() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE_AOD);
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+
+ assertEquals(Display.STATE_DOZE, mServiceFake.screenState);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testTransitions_canRequestTransitions() {
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE);
+ doAnswer(inv -> {
+ mMachine.requestState(DOZE_PULSING);
+ return null;
+ }).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE));
+
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+
+ assertEquals(DOZE_PULSING, mMachine.getState());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java
new file mode 100644
index 0000000..d12fc2c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import android.view.Display;
+
+public class DozeServiceFake implements DozeMachine.Service {
+
+ public boolean finished;
+ public int screenState;
+
+ public DozeServiceFake() {
+ reset();
+ }
+
+ @Override
+ public void finish() {
+ finished = true;
+ }
+
+ @Override
+ public void setDozeScreenState(int state) {
+ screenState = state;
+ }
+
+ public void reset() {
+ finished = false;
+ screenState = Display.STATE_UNKNOWN;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/WakeLockFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/WakeLockFake.java
new file mode 100644
index 0000000..7c04fe2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/WakeLockFake.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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.systemui.doze;
+
+import com.android.internal.util.Preconditions;
+
+public class WakeLockFake extends DozeFactory.WakeLock {
+
+ private int mAcquired = 0;
+
+ public WakeLockFake() {
+ super(null);
+ }
+
+ @Override
+ public void acquire() {
+ mAcquired++;
+ }
+
+ @Override
+ public void release() {
+ Preconditions.checkState(mAcquired > 0);
+ mAcquired--;
+ }
+
+ @Override
+ public Runnable wrap(Runnable runnable) {
+ acquire();
+ return () -> {
+ try {
+ runnable.run();
+ } finally {
+ release();
+ }
+ };
+ }
+
+ public boolean isHeld() {
+ return mAcquired > 0;
+ }
+}