Merge "Doze: Refactor v1"

This commit is contained in:
TreeHugger Robot
2016-11-07 22:16:58 +00:00
committed by Android (Google) Code Review
11 changed files with 1169 additions and 499 deletions

View File

@@ -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);
}
}
}

View File

@@ -24,7 +24,7 @@ import android.annotation.NonNull;
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();

View File

@@ -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);
}
}

View File

@@ -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.TriggerEvent;
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 @@ public class DozeSensors {
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 @@ public class DozeSensors {
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;

View File

@@ -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);
}
}

View File

@@ -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();
}
}
};
}

View File

@@ -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;
}
}
}

View File

@@ -5059,7 +5059,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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;
}
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}