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

Commit e1f32de9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Ambient: Pulse for every noisy notification exactly once" into nyc-dr1-dev

parents 066d94f6 97de2b26
Loading
Loading
Loading
Loading
+1 −7
Original line number Diff line number Diff line
@@ -218,12 +218,6 @@
    <!-- 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" translatable="false">10s,30s,60s</string>

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

    <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
    <integer name="doze_pickup_vibration_threshold">2000</integer>

@@ -253,7 +247,7 @@
    <integer name="doze_pulse_duration_in_pickup">130</integer>

    <!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
    <integer name="doze_pulse_duration_visible">3000</integer>
    <integer name="doze_pulse_duration_visible">6000</integer>

    <!-- Doze: pulse parameter - how long does it take to fade out? -->
    <integer name="doze_pulse_duration_out">600</integer>
+1 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ public class DozeLog {
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean ENABLED = true;
    private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
    static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");

    private static final int PULSE_REASONS = 4;

+13 −130
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.doze;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -43,7 +41,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
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;
@@ -55,19 +52,6 @@ public class DozeService extends DreamService {

    private static final String ACTION_BASE = "com.android.systemui.doze";
    private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
    private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
    private static final String EXTRA_INSTANCE = "instance";

    /**
     * Earliest time we pulse due to a notification light after the service started.
     *
     * <p>Incoming notification light events during the blackout period are
     * delayed to the earliest time defined by this constant.</p>
     *
     * <p>This delay avoids a pulse immediately after screen off, at which
     * point the notification light is re-enabled again by NoMan.</p>
     */
    private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000;

    private final String mTag = String.format(TAG + ".%08x", hashCode());
    private final Context mContext = this;
@@ -80,19 +64,14 @@ public class DozeService extends DreamService {
    private TriggerSensor mPickupSensor;
    private PowerManager mPowerManager;
    private PowerManager.WakeLock mWakeLock;
    private AlarmManager mAlarmManager;
    private UiModeManager mUiModeManager;
    private boolean mDreaming;
    private boolean mPulsing;
    private boolean mBroadcastReceiverRegistered;
    private boolean mDisplayStateSupported;
    private boolean mNotificationLightOn;
    private boolean mPowerSaveActive;
    private boolean mCarMode;
    private long mNotificationPulseTime;
    private long mLastScheduleResetTime;
    private long mEarliestPulseDueToLight;
    private int mScheduleResetsRemaining;

    public DozeService() {
        if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -110,11 +89,11 @@ public class DozeService extends DreamService {
        pw.print("  mSigMotionSensor: "); pw.println(mSigMotionSensor);
        pw.print("  mPickupSensor:"); pw.println(mPickupSensor);
        pw.print("  mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
        pw.print("  mNotificationLightOn: "); pw.println(mNotificationLightOn);
        pw.print("  mPowerSaveActive: "); pw.println(mPowerSaveActive);
        pw.print("  mCarMode: "); pw.println(mCarMode);
        pw.print("  mNotificationPulseTime: "); pw.println(mNotificationPulseTime);
        pw.print("  mScheduleResetsRemaining: "); pw.println(mScheduleResetsRemaining);
        pw.print("  mNotificationPulseTime: "); pw.println(
                DozeLog.FORMAT.format(new Date(mNotificationPulseTime
                        - SystemClock.elapsedRealtime() + System.currentTimeMillis())));
        mDozeParameters.dump(pw);
    }

@@ -141,7 +120,6 @@ public class DozeService extends DreamService {
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        mWakeLock.setReferenceCounted(true);
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
        mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
        turnDisplayOff();
@@ -176,8 +154,6 @@ public class DozeService extends DreamService {
        }

        mDreaming = true;
        rescheduleNotificationPulse(false /*predicate*/);  // cancel any pending pulse alarms
        mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS;
        listenForPulseSignals(true);

        // Ask the host to get things ready to start dozing.
@@ -316,7 +292,6 @@ public class DozeService extends DreamService {
    private void listenForBroadcasts(boolean listen) {
        if (listen) {
            final IntentFilter filter = new IntentFilter(PULSE_ACTION);
            filter.addAction(NOTIFICATION_PULSE_ACTION);
            filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
            mContext.registerReceiver(mBroadcastReceiver, filter);
            mBroadcastReceiverRegistered = true;
@@ -330,94 +305,18 @@ public class DozeService extends DreamService {

    private void listenForNotifications(boolean listen) {
        if (listen) {
            resetNotificationResets();
            mHost.addCallback(mHostCallback);

            // Continue to pulse for existing LEDs.
            mNotificationLightOn = mHost.isNotificationLightOn();
            if (mNotificationLightOn) {
                updateNotificationPulseDueToLight();
            }
        } else {
            mHost.removeCallback(mHostCallback);
        }
    }

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

    private void updateNotificationPulseDueToLight() {
        long timeMs = System.currentTimeMillis();
        timeMs = Math.max(timeMs, mEarliestPulseDueToLight);
        updateNotificationPulse(timeMs);
    }

    private void updateNotificationPulse(long notificationTimeMs) {
        if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs);
    private void requestNotificationPulse() {
        if (DEBUG) Log.d(mTag, "requestNotificationPulse");
        if (!mDozeParameters.getPulseOnNotifications()) return;
        if (mScheduleResetsRemaining <= 0) {
            if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
            return;
        }
        final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/);
        boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs;
        if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) {
            mScheduleResetsRemaining--;
            mLastScheduleResetTime = notificationTimeMs;
        } else if (!pulseImmediately){
            if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
            return;
        }
        if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
        mNotificationPulseTime = notificationTimeMs;
        if (pulseImmediately) {
            DozeLog.traceNotificationPulse(mContext, 0);
        mNotificationPulseTime = SystemClock.elapsedRealtime();
        requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
    }
        // schedule the rest of the pulses
        rescheduleNotificationPulse(true /*predicate*/);
    }

    private PendingIntent notificationPulseIntent(long instance) {
        return PendingIntent.getBroadcast(mContext, 0,
                new Intent(NOTIFICATION_PULSE_ACTION)
                        .setPackage(getPackageName())
                        .putExtra(EXTRA_INSTANCE, instance)
                        .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

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

    private static String triggerEventToString(TriggerEvent event) {
        if (event == null) return null;
@@ -439,13 +338,6 @@ public class DozeService extends DreamService {
                if (DEBUG) Log.d(mTag, "Received pulse intent");
                requestPulse(DozeLog.PULSE_REASON_INTENT);
            }
            if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) {
                final long instance = intent.getLongExtra(EXTRA_INSTANCE, -1);
                if (DEBUG) Log.d(mTag, "Received notification pulse intent instance=" + instance);
                DozeLog.traceNotificationPulse(mContext, instance);
                requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
                rescheduleNotificationPulse(mNotificationLightOn);
            }
            if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
                mCarMode = true;
                if (mCarMode && mDreaming) {
@@ -465,17 +357,13 @@ public class DozeService extends DreamService {
        @Override
        public void onBuzzBeepBlinked() {
            if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
            updateNotificationPulse(System.currentTimeMillis());
            requestNotificationPulse();
        }

        @Override
        public void onNotificationLight(boolean on) {
            if (DEBUG) Log.d(mTag, "onNotificationLight on=" + on);
            if (mNotificationLightOn == on) return;
            mNotificationLightOn = on;
            if (mNotificationLightOn) {
                updateNotificationPulseDueToLight();
            }
            if (DEBUG) Log.d(mTag, "onNotificationLight (noop) on=" + on);
            // noop for now
        }

        @Override
@@ -564,17 +452,12 @@ public class DozeService extends DreamService {
                requestPulse(mPulseReason, sensorPerformsProxCheck);
                updateListener();  // reregister, this sensor only fires once

                // reset the notification pulse schedule, but only if we think we were not triggered
                // by a notification-related vibration
                final long timeSinceNotification = System.currentTimeMillis()
                // record pickup gesture, also keep track of whether we might have been triggered
                // by recent vibration.
                final long timeSinceNotification = SystemClock.elapsedRealtime()
                        - mNotificationPulseTime;
                final boolean withinVibrationThreshold =
                        timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
                if (withinVibrationThreshold) {
                   if (DEBUG) Log.d(mTag, "Not resetting schedule, recent notification");
                } else {
                    resetNotificationResets();
                }
                if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
                    DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
                }
+0 −56
Original line number Diff line number Diff line
@@ -38,8 +38,6 @@ public class DozeParameters {

    private final Context mContext;

    private static PulseSchedule sPulseSchedule;

    private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;

    public DozeParameters(Context context) {
@@ -61,8 +59,6 @@ public class DozeParameters {
        pw.print("    getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
        pw.print("    getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
        pw.print("    getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());
        pw.print("    getPulseSchedule(): "); pw.println(getPulseSchedule());
        pw.print("    getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
        pw.print("    getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
        pw.print("    getPickupSubtypePerformsProxCheck(): ");pw.println(
                dumpPickupSubtypePerformsProxCheck());
@@ -126,18 +122,6 @@ public class DozeParameters {
        return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
    }

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

    public int getPulseScheduleResets() {
        return getInt("doze.pulse.schedule.resets", R.integer.doze_pulse_schedule_resets);
    }

    public int getPickupVibrationThreshold() {
        return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
    }
@@ -249,44 +233,4 @@ public class DozeParameters {
            return (mIsIn.get(value, mDefaultIsIn));
        }
    }

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

        private String mSpec;
        private int[] mSchedule;

        public static PulseSchedule parse(String spec) {
            if (TextUtils.isEmpty(spec)) return null;
            try {
                final PulseSchedule rt = new PulseSchedule();
                rt.mSpec = spec;
                final String[] tokens = spec.split(",");
                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.mSchedule[i] = Integer.parseInt(m.group(1));
                }
                if (DEBUG) Log.d(TAG, "Parsed spec [" + spec + "] as: " + rt);
                return rt;
            } catch (RuntimeException e) {
                Log.w(TAG, "Error parsing spec: " + spec, e);
                return null;
            }
        }

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