From 190d026167401ed593924dd12ab6e134e2d08e94 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Sun, 14 Sep 2014 15:39:13 -0400 Subject: Doze: Aggressively dial down notification-related pulses. When dozing and buzz-worthy notifications arrive, don't follow the LED logic with periodic pulses. Instead, follow a simple decay schedule after the initial arrival, pulsing only at 10s, 30s, 60s, and 120s. The schedule is reset when a new notification arrives, but only for the first three times, until either the pickup sensor is triggered or the device exits doze. Also: - Make the notification trigger configurable. - Centralize existing sysprop configuration into DozeParameters. - Decouple vibration from debugging, make separately configurable. - Remove "delayed" pulse concept, fold into new schedule. Bug:17496795 Change-Id: I64fc1c862bcfa1c288a4fd91c9d17e3bff245add --- packages/SystemUI/res/values/config.xml | 14 +-- .../src/com/android/systemui/doze/DozeService.java | 116 ++++++++++++++------- .../systemui/statusbar/phone/DozeParameters.java | 92 ++++++++++------ .../systemui/statusbar/phone/PhoneStatusBar.java | 11 +- .../systemui/statusbar/phone/ScrimController.java | 12 +-- 5 files changed, 161 insertions(+), 84 deletions(-) (limited to 'packages') diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index cc77aaa..dd81e69 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -188,9 +188,14 @@ false - - 60000:10000,300000:30000,1800000:60000,0 + + true + + + 1s,10s,30s,60s,120s + + + 3 1000 @@ -201,9 +206,6 @@ 1000 - - 1000 - 222 diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 0aa7f352..dae15e6 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -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) { - 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); - } + 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 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(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index e14ef12..bcf42de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -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; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index e6db2c8..d86f030 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 57e5755..3ff11d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -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; } -- cgit v1.1