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

Commit 5ff1d4f8 authored by John Spurlock's avatar John Spurlock Committed by Android (Google) Code Review
Browse files

Merge "Doze: Aggressively dial down notification-related pulses." into lmp-dev

parents 895e08ad 190d0261
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -194,9 +194,14 @@
    <!-- Doze: should the pickup sensor be used as a pulse signal? -->
    <bool name="doze_pulse_on_pick_up">false</bool>

    <!-- Doze: period of time between pulses (start of pulse) when following the notification light
         Format: <under_ms>:<period_ms>,...,<default_ms> -->
    <string name="doze_pulse_period_function">60000:10000,300000:30000,1800000:60000,0</string>
    <!-- Doze: should notifications be used as a pulse signal? -->
    <bool name="doze_pulse_on_notifications">true</bool>

    <!-- Doze: when to pulse after a buzzworthy notification arrives -->
    <string name="doze_pulse_schedule">1s,10s,30s,60s,120s</string>

    <!-- Doze: maximum number of times the notification pulse schedule can be reset -->
    <integer name="doze_pulse_schedule_resets">3</integer>

    <!-- Doze: pulse parameter - how long does it take to fade in? -->
    <integer name="doze_pulse_duration_in">1000</integer>
@@ -207,9 +212,6 @@
    <!-- Doze: pulse parameter - how long does it take to fade out? -->
    <integer name="doze_pulse_duration_out">1000</integer>

    <!-- Doze: pulse parameter - how long to wait before pulsing (if not starting immediately) -->
    <integer name="doze_pulse_delay">1000</integer>

    <!-- Doze: alpha to apply to small icons when dozing -->
    <integer name="doze_small_icon_alpha">222</integer><!-- 87% of 0xff -->

+79 −37
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
@@ -30,15 +29,14 @@ import android.hardware.TriggerEventListener;
import android.media.AudioAttributes;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Vibrator;
import android.service.dreams.DreamService;
import android.util.Log;
import android.view.Display;

import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.DozeParameters.PulseSchedule;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,6 +70,7 @@ public class DozeService extends DreamService {
    private PendingIntent mNotificationPulseIntent;
    private boolean mPowerSaveActive;
    private long mNotificationPulseTime;
    private int mScheduleResetsRemaining;

    public DozeService() {
        if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -90,6 +89,7 @@ public class DozeService extends DreamService {
        pw.print("  mNotificationLightOn: "); pw.println(mNotificationLightOn);
        pw.print("  mPowerSaveActive: "); pw.println(mPowerSaveActive);
        pw.print("  mNotificationPulseTime: "); pw.println(mNotificationPulseTime);
        pw.print("  mScheduleResetsRemaining: "); pw.println(mScheduleResetsRemaining);
        mDozeParameters.dump(pw);
    }

@@ -107,16 +107,14 @@ public class DozeService extends DreamService {
        setWindowless(true);

        mSensors = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        mSigMotionSensor = new TriggerSensor(Sensor.TYPE_SIGNIFICANT_MOTION, "doze.pulse.sigmotion",
                R.bool.doze_pulse_on_significant_motion);
        mPickupSensor = new TriggerSensor(Sensor.TYPE_PICK_UP_GESTURE, "doze.pulse.pickup",
                R.bool.doze_pulse_on_pick_up);
        mSigMotionSensor = new TriggerSensor(Sensor.TYPE_SIGNIFICANT_MOTION,
                mDozeParameters.getPulseOnSigMotion(), mDozeParameters.getVibrateOnSigMotion());
        mPickupSensor = new TriggerSensor(Sensor.TYPE_PICK_UP_GESTURE,
                mDozeParameters.getPulseOnPickup(), mDozeParameters.getVibrateOnPickup());
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        final Resources res = mContext.getResources();
        mDisplayStateSupported = SystemProperties.getBoolean("doze.display.supported",
                res.getBoolean(R.bool.doze_display_state_supported));
        mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
        mNotificationPulseIntent = PendingIntent.getBroadcast(mContext, 0,
                new Intent(NOTIFICATION_PULSE_ACTION).setPackage(getPackageName()),
                PendingIntent.FLAG_UPDATE_CURRENT);
@@ -142,6 +140,7 @@ public class DozeService extends DreamService {
        }
        mDreaming = true;
        listenForPulseSignals(true);
        rescheduleNotificationPulse(false /*predicate*/);  // cancel any pending pulse alarms
        requestDoze();
    }

@@ -174,15 +173,21 @@ public class DozeService extends DreamService {
        mHandler.removeCallbacks(mDisplayOff);
    }

    @Override
    public void startDozing() {
        if (DEBUG) Log.d(mTag, "startDozing");
        super.startDozing();
    }

    private void requestDoze() {
        if (mHost != null) {
            mHost.requestDoze(this);
        }
    }

    private void requestPulse(boolean delayed) {
    private void requestPulse() {
        if (mHost != null) {
            mHost.requestPulse(delayed, this);
            mHost.requestPulse(this);
        }
    }

@@ -222,24 +227,61 @@ public class DozeService extends DreamService {
    private void listenForNotifications(boolean listen) {
        if (mHost == null) return;
        if (listen) {
            resetNotificationResets();
            mHost.addCallback(mHostCallback);
        } else {
            mHost.removeCallback(mHostCallback);
        }
    }

    private void rescheduleNotificationPulse() {
    private void resetNotificationResets() {
        if (DEBUG) Log.d(TAG, "resetNotificationResets");
        mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
    }

    private void updateNotificationPulse() {
        if (DEBUG) Log.d(TAG, "updateNotificationPulse");
        if (!mDozeParameters.getPulseOnNotifications()) return;
        if (mScheduleResetsRemaining <= 0) {
            if (DEBUG) Log.d(TAG, "No more schedule resets remaining");
            return;
        }
        final long now = System.currentTimeMillis();
        if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
            if (DEBUG) Log.d(TAG, "Recently updated, not resetting schedule");
            return;
        }
        mScheduleResetsRemaining--;
        if (DEBUG) Log.d(TAG, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
        mNotificationPulseTime = now;
        rescheduleNotificationPulse(true /*predicate*/);
    }

    private void rescheduleNotificationPulse(boolean predicate) {
        if (DEBUG) Log.d(TAG, "rescheduleNotificationPulse predicate=" + predicate);
        mAlarmManager.cancel(mNotificationPulseIntent);
        if (mNotificationLightOn) {
        if (!predicate) {
            if (DEBUG) Log.d(TAG, "  don't reschedule: predicate is false");
            return;
        }
        final PulseSchedule schedule = mDozeParameters.getPulseSchedule();
        if (schedule == null) {
            if (DEBUG) Log.d(TAG, "  don't reschedule: schedule is null");
            return;
        }
        final long now = System.currentTimeMillis();
            final long age = now - mNotificationPulseTime;
            final long period = mDozeParameters.getPulsePeriod(age);
            final long time = now + period;
            if (period > 0) {
                if (DEBUG) Log.d(TAG, "Scheduling pulse in " + period + " for " + new Date(time));
                mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, mNotificationPulseIntent);
        final long time = schedule.getNextTime(now, mNotificationPulseTime);
        if (time <= 0) {
            if (DEBUG) Log.d(TAG, "  don't reschedule: time is " + time);
            return;
        }
        final long delta = time - now;
        if (delta <= 0) {
            if (DEBUG) Log.d(TAG, "  don't reschedule: delta is " + delta);
            return;
        }
        if (DEBUG) Log.d(TAG, "Scheduling pulse in " + delta + "ms for " + new Date(time));
        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, mNotificationPulseIntent);
    }

    private static String triggerEventToString(TriggerEvent event) {
@@ -268,12 +310,12 @@ public class DozeService extends DreamService {
        public void onReceive(Context context, Intent intent) {
            if (PULSE_ACTION.equals(intent.getAction())) {
                if (DEBUG) Log.d(mTag, "Received pulse intent");
                requestPulse(false /*delayed*/);
                requestPulse();
            }
            if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) {
                if (DEBUG) Log.d(mTag, "Received notification pulse intent");
                requestPulse(true /*delayed*/);
                rescheduleNotificationPulse();
                requestPulse();
                rescheduleNotificationPulse(mNotificationLightOn);
            }
        }
    };
@@ -288,8 +330,7 @@ public class DozeService extends DreamService {
        @Override
        public void onBuzzBeepBlinked() {
            if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
            mNotificationPulseTime = System.currentTimeMillis();
            requestPulse(true /*delayed*/);
            updateNotificationPulse();
        }

        @Override
@@ -298,10 +339,8 @@ public class DozeService extends DreamService {
            if (mNotificationLightOn == on) return;
            mNotificationLightOn = on;
            if (mNotificationLightOn) {
                mNotificationPulseTime = System.currentTimeMillis();
                requestPulse(true /*delayed*/);
                updateNotificationPulse();
            }
            rescheduleNotificationPulse();
        }

        @Override
@@ -317,7 +356,7 @@ public class DozeService extends DreamService {
        void addCallback(Callback callback);
        void removeCallback(Callback callback);
        void requestDoze(DozeService dozeService);
        void requestPulse(boolean delayed, DozeService dozeService);
        void requestPulse(DozeService dozeService);
        void dozingStopped(DozeService dozeService);
        boolean isPowerSaveActive();

@@ -332,13 +371,14 @@ public class DozeService extends DreamService {
    private class TriggerSensor extends TriggerEventListener {
        private final Sensor mSensor;
        private final boolean mConfigured;
        private final boolean mDebugVibrate;

        private boolean mEnabled;

        public TriggerSensor(int type, String sysPropConfig, int resConfig) {
        public TriggerSensor(int type, boolean configured, boolean debugVibrate) {
            mSensor = mSensors.getDefaultSensor(type);
            mConfigured = SystemProperties.getBoolean(sysPropConfig,
                    mContext.getResources().getBoolean(resConfig));
            mConfigured = configured;
            mDebugVibrate = debugVibrate;
        }

        public void setListening(boolean listen) {
@@ -354,13 +394,14 @@ public class DozeService extends DreamService {
        @Override
        public String toString() {
            return new StringBuilder("{mEnabled=").append(mEnabled).append(", mConfigured=")
                    .append(mConfigured).append(", mSensor=").append(mSensor).append("}").toString();
                    .append(mConfigured).append(", mDebugVibrate=").append(mDebugVibrate)
                    .append(", mSensor=").append(mSensor).append("}").toString();
        }

        @Override
        public void onTrigger(TriggerEvent event) {
            if (DEBUG) Log.d(mTag, "onTrigger: " + triggerEventToString(event));
            if (DEBUG) {
            if (mDebugVibrate) {
                final Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
                if (v != null) {
                    v.vibrate(1000, new AudioAttributes.Builder()
@@ -368,8 +409,9 @@ public class DozeService extends DreamService {
                            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build());
                }
            }
            requestPulse(false /*delayed*/);
            requestPulse();
            setListening(true);  // reregister, this sensor only fires once
            resetNotificationResets();
        }
    }
}
+63 −29
Original line number Diff line number Diff line
@@ -25,17 +25,19 @@ import android.util.MathUtils;
import com.android.systemui.R;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DozeParameters {
    private static final String TAG = "DozeParameters";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private static final int MAX_DURATION = 10 * 1000;

    private final Context mContext;

    private StepFunction mPulsePeriodFunction;
    private static PulseSchedule sPulseSchedule;

    public DozeParameters(Context context) {
        mContext = context;
@@ -43,12 +45,22 @@ public class DozeParameters {

    public void dump(PrintWriter pw) {
        pw.println("  DozeParameters:");
        pw.print("    getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
        pw.print("    getPulseDuration(): "); pw.println(getPulseDuration());
        pw.print("    getPulseInDuration(): "); pw.println(getPulseInDuration());
        pw.print("    getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
        pw.print("    getPulseOutDuration(): "); pw.println(getPulseOutDuration());
        pw.print("    getPulseStartDelay(): "); pw.println(getPulseStartDelay());
        pw.print("    getPulsePeriodFunction(): "); pw.println(getPulsePeriodFunction());
        pw.print("    getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
        pw.print("    getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion());
        pw.print("    getPulseOnPickup(): "); pw.println(getPulseOnPickup());
        pw.print("    getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
        pw.print("    getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());
        pw.print("    getPulseSchedule(): "); pw.println(getPulseSchedule());
        pw.print("    getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
    }

    public boolean getDisplayStateSupported() {
        return getBoolean("doze.display.supported", R.bool.doze_display_state_supported);
    }

    public int getPulseDuration() {
@@ -67,20 +79,40 @@ public class DozeParameters {
        return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out);
    }

    public int getPulseStartDelay() {
        return getInt("doze.pulse.delay", R.integer.doze_pulse_delay);
    public boolean getPulseOnSigMotion() {
        return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion);
    }

    public boolean getVibrateOnSigMotion() {
        return SystemProperties.getBoolean("doze.vibrate.sigmotion", false);
    }

    public boolean getPulseOnPickup() {
        return getBoolean("doze.pulse.pickup", R.bool.doze_pulse_on_pick_up);
    }

    public boolean getVibrateOnPickup() {
        return SystemProperties.getBoolean("doze.vibrate.pickup", false);
    }

    public boolean getPulseOnNotifications() {
        return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
    }

    public long getPulsePeriod(long age) {
        final String spec = getPulsePeriodFunction();
        if (mPulsePeriodFunction == null || !mPulsePeriodFunction.mSpec.equals(spec)) {
            mPulsePeriodFunction = StepFunction.parse(spec);
    public PulseSchedule getPulseSchedule() {
        final String spec = getString("doze.pulse.schedule", R.string.doze_pulse_schedule);
        if (sPulseSchedule == null || !sPulseSchedule.mSpec.equals(spec)) {
            sPulseSchedule = PulseSchedule.parse(spec);
        }
        return mPulsePeriodFunction != null ? mPulsePeriodFunction.evaluate(age) : 0;
        return sPulseSchedule;
    }

    private String getPulsePeriodFunction() {
        return getString("doze.pulse.period.function", R.string.doze_pulse_period_function);
    public int getPulseScheduleResets() {
        return getInt("doze.pulse.schedule.resets", R.integer.doze_pulse_schedule_resets);
    }

    private boolean getBoolean(String propName, int resId) {
        return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
    }

    private int getInt(String propName, int resId) {
@@ -92,29 +124,25 @@ public class DozeParameters {
        return SystemProperties.get(propName, mContext.getString(resId));
    }

    private static class StepFunction {
        private static final Pattern PATTERN = Pattern.compile("(\\d+?)(:(\\d+?))?", 0);
    public static class PulseSchedule {
        private static final Pattern PATTERN = Pattern.compile("(\\d+?)s", 0);

        private String mSpec;
        private long[] mSteps;
        private long[] mValues;
        private long mDefault;
        private int[] mSchedule;

        public static StepFunction parse(String spec) {
        public static PulseSchedule parse(String spec) {
            if (TextUtils.isEmpty(spec)) return null;
            try {
                final StepFunction rt = new StepFunction();
                final PulseSchedule rt = new PulseSchedule();
                rt.mSpec = spec;
                final String[] tokens = spec.split(",");
                rt.mSteps = new long[tokens.length - 1];
                rt.mValues = new long[tokens.length - 1];
                for (int i = 0; i < tokens.length - 1; i++) {
                rt.mSchedule = new int[tokens.length];
                for (int i = 0; i < tokens.length; i++) {
                    final Matcher m = PATTERN.matcher(tokens[i]);
                    if (!m.matches()) throw new IllegalArgumentException("Bad token: " + tokens[i]);
                    rt.mSteps[i] = Long.parseLong(m.group(1));
                    rt.mValues[i] = Long.parseLong(m.group(3));
                    rt.mSchedule[i] = Integer.parseInt(m.group(1));
                }
                rt.mDefault = Long.parseLong(tokens[tokens.length - 1]);
                if (DEBUG) Log.d(TAG, "Parsed spec [" + spec + "] as: " + rt);
                return rt;
            } catch (RuntimeException e) {
                Log.w(TAG, "Error parsing spec: " + spec, e);
@@ -122,11 +150,17 @@ public class DozeParameters {
            }
        }

        public long evaluate(long x) {
            for (int i = 0; i < mSteps.length; i++) {
                if (x < mSteps[i]) return mValues[i];
        @Override
        public String toString() {
            return Arrays.toString(mSchedule);
        }

        public long getNextTime(long now, long notificationTime) {
            for (int i = 0; i < mSchedule.length; i++) {
                final long time = notificationTime + mSchedule[i] * 1000;
                if (time > now) return time;
            }
            return mDefault;
            return 0;
        }
    }
}
+5 −6
Original line number Diff line number Diff line
@@ -4083,11 +4083,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        }

        @Override
        public void requestPulse(boolean delayed, DozeService dozeService) {
        public void requestPulse(DozeService dozeService) {
            if (dozeService == null) return;
            dozeService.stayAwake(PROCESSING_TIME);
            mHandler.obtainMessage(H.REQUEST_PULSE, delayed ? 1 : 0, 0, dozeService)
                    .sendToTarget();
            mHandler.obtainMessage(H.REQUEST_PULSE, dozeService).sendToTarget();
        }

        @Override
@@ -4110,9 +4109,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
            mCurrentDozeService.startDozing();
        }

        private void handleRequestPulse(boolean delayed, DozeService dozeService) {
        private void handleRequestPulse(DozeService dozeService) {
            if (!dozeService.equals(mCurrentDozeService)) return;
            final long stayAwake = mScrimController.pulse(delayed);
            final long stayAwake = mScrimController.pulse();
            mCurrentDozeService.stayAwake(stayAwake);
        }

@@ -4136,7 +4135,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
                if (msg.what == REQUEST_DOZE) {
                    handleRequestDoze((DozeService) msg.obj);
                } else if (msg.what == REQUEST_PULSE) {
                    handleRequestPulse(msg.arg1 != 0, (DozeService) msg.obj);
                    handleRequestPulse((DozeService) msg.obj);
                } else if (msg.what == DOZING_STOPPED) {
                    handleDozingStopped((DozeService) msg.obj);
                }
+6 −6
Original line number Diff line number Diff line
@@ -129,20 +129,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
        if (!mDozing) {
            cancelPulsing();
            mAnimateChange = true;
        } else {
            mAnimateChange = false;
        }
        scheduleUpdate();
    }

    /** When dozing, fade screen contents in and out using the front scrim. */
    public long pulse(boolean delayed) {
    public long pulse() {
        if (!mDozing) return 0;
        final long now = System.currentTimeMillis();
        if (DEBUG) Log.d(TAG, "pulse delayed=" + delayed + " mPulseEndTime=" + mPulseEndTime
                + " now=" + now);
        if (DEBUG) Log.d(TAG, "pulse mPulseEndTime=" + mPulseEndTime + " now=" + now);
        if (mPulseEndTime != 0 && mPulseEndTime > now) return mPulseEndTime - now;
        final long delay = delayed ? mDozeParameters.getPulseStartDelay() : 0;
        mScrimInFront.postDelayed(mPulseIn, delay);
        mPulseEndTime = now + delay + mDozeParameters.getPulseDuration();
        mScrimInFront.post(mPulseIn);
        mPulseEndTime = now + mDozeParameters.getPulseDuration();
        return mPulseEndTime - now;
    }