Merge "Doze: Refactor v1"
This commit is contained in:
committed by
Android (Google) Code Review
commit
ca67fa44ff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
283
packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
Normal file
283
packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
68
packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
Normal file
68
packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user