Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6023ccbe authored by Adrian Roos's avatar Adrian Roos
Browse files

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
parent ef5cf2b4
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -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);
    }

+7 −2
Original line number Diff line number Diff line
@@ -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;
+53 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+45 −4
Original line number Diff line number Diff line
@@ -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<Boolean> 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
+13 −7
Original line number Diff line number Diff line
@@ -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;
Loading