From 6023ccbea1568d8b22db82cac9bf7a03367d0a61 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Wed, 28 Jun 2017 16:22:02 +0200 Subject: [PATCH] AOD: Add hysteresis to pausing the display Delays pausing the display by several seconds to avoid flickering when the prox is only covered transiently. Also adds cooldown to the proximity sensor, such that when it toggles too often it gets disabled for a while. Bug: 62292293 Test: cover prox for less than 10s, observe display stays on. Cover for more, observe display turns off. Change-Id: Ifa407b84760fc299fbbcfa92d9e942e0093c4b73 --- .../android/systemui/doze/DozeFactory.java | 11 ++-- .../android/systemui/doze/DozeMachine.java | 9 +++- .../com/android/systemui/doze/DozePauser.java | 53 +++++++++++++++++++ .../android/systemui/doze/DozeSensors.java | 49 +++++++++++++++-- .../android/systemui/doze/DozeTriggers.java | 20 ++++--- .../src/com/android/systemui/doze/DozeUi.java | 1 + .../systemui/doze/DozeTriggersTest.java | 5 +- 7 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/doze/DozePauser.java diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 1cc10c22082ad..c072772552544 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -52,8 +52,9 @@ public class DozeFactory { config, wakeLock); machine.setParts(new DozeMachine.Part[]{ - createDozeTriggers(context, sensorManager, host, config, params, handler, wakeLock, - machine), + new DozePauser(handler, machine, alarmManager), + createDozeTriggers(context, sensorManager, host, alarmManager, config, params, + handler, wakeLock, machine), createDozeUi(context, host, wakeLock, machine, handler, alarmManager), }); @@ -61,10 +62,10 @@ public class DozeFactory { } private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager, - DozeHost host, AmbientDisplayConfiguration config, DozeParameters params, - Handler handler, WakeLock wakeLock, DozeMachine machine) { + DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, + DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine) { boolean allowPulseTriggers = true; - return new DozeTriggers(context, machine, host, config, params, + return new DozeTriggers(context, machine, host, alarmManager, config, params, sensorManager, handler, wakeLock, allowPulseTriggers); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 5526e6ba3f454..348dd97ab96a3 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -60,13 +60,16 @@ public class DozeMachine { /** Doze is done. DozeService is finished. */ FINISH, /** AOD, but the display is temporarily off. */ - DOZE_AOD_PAUSED; + DOZE_AOD_PAUSED, + /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */ + DOZE_AOD_PAUSING; boolean canPulse() { switch (this) { case DOZE: case DOZE_AOD: case DOZE_AOD_PAUSED: + case DOZE_AOD_PAUSING: return true; default: return false; @@ -93,6 +96,7 @@ public class DozeMachine { case DOZE_PULSING: return Display.STATE_ON; case DOZE_AOD: + case DOZE_AOD_PAUSING: return Display.STATE_DOZE_SUSPEND; default: return Display.STATE_UNKNOWN; @@ -284,7 +288,8 @@ public class DozeMachine { if (mState == State.FINISH) { return State.FINISH; } - if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD || mState == State.DOZE) + if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING + || mState == State.DOZE_AOD || mState == State.DOZE) && requestedState == State.DOZE_PULSE_DONE) { Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); return mState; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java new file mode 100644 index 0000000000000..a33b454c6430e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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.AlarmManager; +import android.os.Handler; + +import com.android.systemui.util.AlarmTimeout; + +/** + * Moves the doze machine from the pausing to the paused state after a timeout. + */ +public class DozePauser implements DozeMachine.Part { + public static final String TAG = DozePauser.class.getSimpleName(); + private static final long TIMEOUT = 10 * 1000; + private final AlarmTimeout mPauseTimeout; + private final DozeMachine mMachine; + + public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager) { + mMachine = machine; + mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler); + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + switch (newState) { + case DOZE_AOD_PAUSING: + mPauseTimeout.schedule(TIMEOUT, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + break; + default: + mPauseTimeout.cancel(); + break; + } + } + + private void onTimeout() { + mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 23da716706d3c..df840ea20383a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import android.annotation.AnyThread; import android.app.ActivityManager; +import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -29,6 +30,7 @@ import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; import android.net.Uri; import android.os.Handler; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -38,6 +40,7 @@ import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; @@ -51,6 +54,7 @@ public class DozeSensors { private static final String TAG = "DozeSensors"; private final Context mContext; + private final AlarmManager mAlarmManager; private final SensorManager mSensorManager; private final TriggerSensor[] mSensors; private final ContentResolver mResolver; @@ -65,10 +69,12 @@ public class DozeSensors { private final ProxSensor mProxSensor; - public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters, + public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager, + DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, Consumer proxCallback) { mContext = context; + mAlarmManager = alarmManager; mSensorManager = sensorManager; mDozeParameters = dozeParameters; mConfig = config; @@ -140,7 +146,7 @@ public class DozeSensors { } public void setProxListening(boolean listen) { - mProxSensor.setRegistered(listen); + mProxSensor.setRequested(listen); } private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @@ -168,11 +174,23 @@ public class DozeSensors { private class ProxSensor implements SensorEventListener { + static final long COOLDOWN_TRIGGER = 2 * 1000; + static final long COOLDOWN_PERIOD = 5 * 1000; + + boolean mRequested; boolean mRegistered; Boolean mCurrentlyFar; + long mLastNear; + final AlarmTimeout mCooldownTimer; - void setRegistered(boolean register) { - if (mRegistered == register) { + + public ProxSensor() { + mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered, + "prox_cooldown", mHandler); + } + + void setRequested(boolean requested) { + if (mRequested == requested) { // Send an update even if we don't re-register. mHandler.post(() -> { if (mCurrentlyFar != null) { @@ -181,6 +199,18 @@ public class DozeSensors { }); return; } + mRequested = requested; + updateRegistered(); + } + + private void updateRegistered() { + setRegistered(mRequested && !mCooldownTimer.isScheduled()); + } + + private void setRegistered(boolean register) { + if (mRegistered == register) { + return; + } if (register) { mRegistered = mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY), @@ -196,6 +226,17 @@ public class DozeSensors { public void onSensorChanged(SensorEvent event) { mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange(); mProxCallback.accept(mCurrentlyFar); + + long now = SystemClock.elapsedRealtime(); + if (!mCurrentlyFar) { + mLastNear = now; + } else if (mCurrentlyFar && now - mLastNear < COOLDOWN_TRIGGER) { + // If the last near was very recent, we might be using more power for prox + // wakeups than we're saving from turning of the screen. Instead, turn it off + // for a while. + mCooldownTimer.schedule(COOLDOWN_PERIOD, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + updateRegistered(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 8d1d6e0ce460d..610eaffec34b8 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import android.app.AlarmManager; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -70,7 +71,7 @@ public class DozeTriggers implements DozeMachine.Part { public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, - AmbientDisplayConfiguration config, + AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, SensorManager sensorManager, Handler handler, WakeLock wakeLock, boolean allowPulseTriggers) { mContext = context; @@ -82,8 +83,8 @@ public class DozeTriggers implements DozeMachine.Part { mHandler = handler; mWakeLock = wakeLock; mAllowPulseTriggers = allowPulseTriggers; - mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config, - wakeLock, this::onSensor, this::onProximityFar); + mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, + config, wakeLock, this::onSensor, this::onProximityFar); mUiModeManager = mContext.getSystemService(UiModeManager.class); } @@ -152,18 +153,22 @@ public class DozeTriggers implements DozeMachine.Part { private void onProximityFar(boolean far) { final boolean near = !far; - DozeMachine.State state = mMachine.getState(); + final DozeMachine.State state = mMachine.getState(); + final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); + final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); + final boolean aod = (state == DozeMachine.State.DOZE_AOD); + if (near && state == DozeMachine.State.DOZE_PULSING) { if (DEBUG) Log.i(TAG, "Prox NEAR, ending pulse"); DozeLog.tracePulseCanceledByProx(mContext); mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE); } - if (far && state == DozeMachine.State.DOZE_AOD_PAUSED) { + if (far && (paused || pausing)) { if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD"); mMachine.requestState(DozeMachine.State.DOZE_AOD); - } else if (near && state == DozeMachine.State.DOZE_AOD) { + } else if (near && aod) { if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD"); - mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED); + mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING); } } @@ -192,6 +197,7 @@ public class DozeTriggers implements DozeMachine.Part { } break; case DOZE_AOD_PAUSED: + case DOZE_AOD_PAUSING: mDozeSensors.setProxListening(true); mDozeSensors.setListening(false); break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 97731cf24774f..1dc37cdbca7ad 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -74,6 +74,7 @@ public class DozeUi implements DozeMachine.Part { public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD: + case DOZE_AOD_PAUSING: scheduleTimeTick(); break; case DOZE: diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 8641faca5d6e2..a8ea1c0770da8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AlarmManager; import android.app.Instrumentation; import android.os.Handler; import android.os.Looper; @@ -56,6 +57,7 @@ public class DozeTriggersTest extends SysuiTestCase { private Handler mHandler; private WakeLock mWakeLock; private Instrumentation mInstrumentation; + private AlarmManager mAlarmManager; @BeforeClass public static void setupSuite() { @@ -67,6 +69,7 @@ public class DozeTriggersTest extends SysuiTestCase { public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); mMachine = mock(DozeMachine.class); + mAlarmManager = mock(AlarmManager.class); mHost = new DozeHostFake(); mConfig = DozeConfigurationUtil.createMockConfig(); mParameters = DozeConfigurationUtil.createMockParameters(); @@ -75,7 +78,7 @@ public class DozeTriggersTest extends SysuiTestCase { mWakeLock = new WakeLockFake(); mInstrumentation.runOnMainSync(() -> { - mTriggers = new DozeTriggers(mContext, mMachine, mHost, + mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters, mSensors, mHandler, mWakeLock, true); }); }