am 2c52c443: am 0d334365: Merge "Doze: Avoid pulsing in pockets." into lmp-dev

* commit '2c52c443dcca748279e386160143ae388b19bb66':
  Doze: Avoid pulsing in pockets.
This commit is contained in:
John Spurlock
2014-10-07 17:43:03 +00:00
committed by Android Git Automerger
2 changed files with 154 additions and 34 deletions

View File

@@ -48,6 +48,8 @@ public class DozeLog {
private static SummaryStats sScreenOnPulsingStats; private static SummaryStats sScreenOnPulsingStats;
private static SummaryStats sScreenOnNotPulsingStats; private static SummaryStats sScreenOnNotPulsingStats;
private static SummaryStats sEmergencyCallStats; private static SummaryStats sEmergencyCallStats;
private static SummaryStats sProxNearStats;
private static SummaryStats sProxFarStats;
public static void tracePickupPulse(boolean withinVibrationThreshold) { public static void tracePickupPulse(boolean withinVibrationThreshold) {
if (!ENABLED) return; if (!ENABLED) return;
@@ -88,6 +90,8 @@ public class DozeLog {
sScreenOnPulsingStats = new SummaryStats(); sScreenOnPulsingStats = new SummaryStats();
sScreenOnNotPulsingStats = new SummaryStats(); sScreenOnNotPulsingStats = new SummaryStats();
sEmergencyCallStats = new SummaryStats(); sEmergencyCallStats = new SummaryStats();
sProxNearStats = new SummaryStats();
sProxFarStats = new SummaryStats();
log("init"); log("init");
KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback); KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
} }
@@ -133,6 +137,12 @@ public class DozeLog {
} }
} }
public static void traceProximityResult(boolean near, long millis) {
if (!ENABLED) return;
log("proximityResult near=" + near + " millis=" + millis);
(near ? sProxNearStats : sProxFarStats).append();
}
public static void dump(PrintWriter pw) { public static void dump(PrintWriter pw) {
synchronized (DozeLog.class) { synchronized (DozeLog.class) {
if (sMessages == null) return; if (sMessages == null) return;
@@ -154,6 +164,8 @@ public class DozeLog {
sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)"); sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)"); sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
sEmergencyCallStats.dump(pw, "Emergency call"); sEmergencyCallStats.dump(pw, "Emergency call");
sProxNearStats.dump(pw, "Proximity (near)");
sProxFarStats.dump(pw, "Proximity (far)");
} }
} }

View File

@@ -25,11 +25,15 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.hardware.TriggerEvent; import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener; import android.hardware.TriggerEventListener;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock;
import android.os.Vibrator; import android.os.Vibrator;
import android.service.dreams.DreamService; import android.service.dreams.DreamService;
import android.util.Log; import android.util.Log;
@@ -55,6 +59,7 @@ public class DozeService extends DreamService {
private final String mTag = String.format(TAG + ".%08x", hashCode()); private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this; private final Context mContext = this;
private final DozeParameters mDozeParameters = new DozeParameters(mContext); private final DozeParameters mDozeParameters = new DozeParameters(mContext);
private final Handler mHandler = new Handler();
private DozeHost mHost; private DozeHost mHost;
private SensorManager mSensors; private SensorManager mSensors;
@@ -197,33 +202,49 @@ public class DozeService extends DreamService {
// Here we need a wakelock to stay awake until the pulse is finished. // Here we need a wakelock to stay awake until the pulse is finished.
mWakeLock.acquire(); mWakeLock.acquire();
mPulsing = true; mPulsing = true;
mHost.pulseWhileDozing(new DozeHost.PulseCallback() { final long start = SystemClock.uptimeMillis();
new ProximityCheck() {
@Override @Override
public void onPulseStarted() { public void onProximityResult(int result) {
if (mPulsing && mDreaming) { // avoid pulsing in pockets
turnDisplayOn(); final boolean isNear = result == RESULT_NEAR;
} DozeLog.traceProximityResult(isNear, SystemClock.uptimeMillis() - start);
} if (isNear) {
@Override
public void onPulseFinished() {
if (mPulsing && mDreaming) {
mPulsing = false; mPulsing = false;
turnDisplayOff(); mWakeLock.release();
return;
} }
mWakeLock.release(); // needs to be unconditional to balance acquire
// not in-pocket, continue pulsing
mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
@Override
public void onPulseStarted() {
if (mPulsing && mDreaming) {
turnDisplayOn();
}
}
@Override
public void onPulseFinished() {
if (mPulsing && mDreaming) {
mPulsing = false;
turnDisplayOff();
}
mWakeLock.release(); // needs to be unconditional to balance acquire
}
});
} }
}); }.check();
} }
} }
private void turnDisplayOff() { private void turnDisplayOff() {
if (DEBUG) Log.d(TAG, "Display off"); if (DEBUG) Log.d(mTag, "Display off");
setDozeScreenState(Display.STATE_OFF); setDozeScreenState(Display.STATE_OFF);
} }
private void turnDisplayOn() { private void turnDisplayOn() {
if (DEBUG) Log.d(TAG, "Display on"); if (DEBUG) Log.d(mTag, "Display on");
setDozeScreenState(mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON); setDozeScreenState(mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON);
} }
@@ -270,24 +291,24 @@ public class DozeService extends DreamService {
} }
private void resetNotificationResets() { private void resetNotificationResets() {
if (DEBUG) Log.d(TAG, "resetNotificationResets"); if (DEBUG) Log.d(mTag, "resetNotificationResets");
mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets(); mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
} }
private void updateNotificationPulse() { private void updateNotificationPulse() {
if (DEBUG) Log.d(TAG, "updateNotificationPulse"); if (DEBUG) Log.d(mTag, "updateNotificationPulse");
if (!mDozeParameters.getPulseOnNotifications()) return; if (!mDozeParameters.getPulseOnNotifications()) return;
if (mScheduleResetsRemaining <= 0) { if (mScheduleResetsRemaining <= 0) {
if (DEBUG) Log.d(TAG, "No more schedule resets remaining"); if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
return; return;
} }
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) { if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
if (DEBUG) Log.d(TAG, "Recently updated, not resetting schedule"); if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
return; return;
} }
mScheduleResetsRemaining--; mScheduleResetsRemaining--;
if (DEBUG) Log.d(TAG, "mScheduleResetsRemaining = " + mScheduleResetsRemaining); if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
mNotificationPulseTime = now; mNotificationPulseTime = now;
rescheduleNotificationPulse(true /*predicate*/); rescheduleNotificationPulse(true /*predicate*/);
} }
@@ -302,31 +323,31 @@ public class DozeService extends DreamService {
} }
private void rescheduleNotificationPulse(boolean predicate) { private void rescheduleNotificationPulse(boolean predicate) {
if (DEBUG) Log.d(TAG, "rescheduleNotificationPulse predicate=" + predicate); if (DEBUG) Log.d(mTag, "rescheduleNotificationPulse predicate=" + predicate);
final PendingIntent notificationPulseIntent = notificationPulseIntent(0); final PendingIntent notificationPulseIntent = notificationPulseIntent(0);
mAlarmManager.cancel(notificationPulseIntent); mAlarmManager.cancel(notificationPulseIntent);
if (!predicate) { if (!predicate) {
if (DEBUG) Log.d(TAG, " don't reschedule: predicate is false"); if (DEBUG) Log.d(mTag, " don't reschedule: predicate is false");
return; return;
} }
final PulseSchedule schedule = mDozeParameters.getPulseSchedule(); final PulseSchedule schedule = mDozeParameters.getPulseSchedule();
if (schedule == null) { if (schedule == null) {
if (DEBUG) Log.d(TAG, " don't reschedule: schedule is null"); if (DEBUG) Log.d(mTag, " don't reschedule: schedule is null");
return; return;
} }
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final long time = schedule.getNextTime(now, mNotificationPulseTime); final long time = schedule.getNextTime(now, mNotificationPulseTime);
if (time <= 0) { if (time <= 0) {
if (DEBUG) Log.d(TAG, " don't reschedule: time is " + time); if (DEBUG) Log.d(mTag, " don't reschedule: time is " + time);
return; return;
} }
final long delta = time - now; final long delta = time - now;
if (delta <= 0) { if (delta <= 0) {
if (DEBUG) Log.d(TAG, " don't reschedule: delta is " + delta); if (DEBUG) Log.d(mTag, " don't reschedule: delta is " + delta);
return; return;
} }
final long instance = time - mNotificationPulseTime; final long instance = time - mNotificationPulseTime;
if (DEBUG) Log.d(TAG, "Scheduling pulse " + instance + " in " + delta + "ms for " if (DEBUG) Log.d(mTag, "Scheduling pulse " + instance + " in " + delta + "ms for "
+ new Date(time)); + new Date(time));
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance)); mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance));
} }
@@ -404,7 +425,9 @@ public class DozeService extends DreamService {
private final boolean mConfigured; private final boolean mConfigured;
private final boolean mDebugVibrate; private final boolean mDebugVibrate;
private boolean mEnabled; private boolean mRequested;
private boolean mRegistered;
private boolean mDisabled;
public TriggerSensor(int type, boolean configured, boolean debugVibrate) { public TriggerSensor(int type, boolean configured, boolean debugVibrate) {
mSensor = mSensors.getDefaultSensor(type); mSensor = mSensors.getDefaultSensor(type);
@@ -413,19 +436,34 @@ public class DozeService extends DreamService {
} }
public void setListening(boolean listen) { public void setListening(boolean listen) {
if (mRequested == listen) return;
mRequested = listen;
updateListener();
}
public void setDisabled(boolean disabled) {
if (mDisabled == disabled) return;
mDisabled = disabled;
updateListener();
}
private void updateListener() {
if (!mConfigured || mSensor == null) return; if (!mConfigured || mSensor == null) return;
if (listen) { if (mRequested && !mDisabled) {
mEnabled = mSensors.requestTriggerSensor(this, mSensor); mRegistered = mSensors.requestTriggerSensor(this, mSensor);
} else if (mEnabled) { } else if (mRegistered) {
mSensors.cancelTriggerSensor(this, mSensor); mSensors.cancelTriggerSensor(this, mSensor);
mEnabled = false; mRegistered = false;
} }
} }
@Override @Override
public String toString() { public String toString() {
return new StringBuilder("{mEnabled=").append(mEnabled).append(", mConfigured=") return new StringBuilder("{mRegistered=").append(mRegistered)
.append(mConfigured).append(", mDebugVibrate=").append(mDebugVibrate) .append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
.append(", mDebugVibrate=").append(mDebugVibrate)
.append(", mSensor=").append(mSensor).append("}").toString(); .append(", mSensor=").append(mSensor).append("}").toString();
} }
@@ -449,7 +487,8 @@ public class DozeService extends DreamService {
// reset the notification pulse schedule, but only if we think we were not triggered // reset the notification pulse schedule, but only if we think we were not triggered
// by a notification-related vibration // by a notification-related vibration
final long timeSinceNotification = System.currentTimeMillis() - mNotificationPulseTime; final long timeSinceNotification = System.currentTimeMillis()
- mNotificationPulseTime;
final boolean withinVibrationThreshold = final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold(); timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
if (withinVibrationThreshold) { if (withinVibrationThreshold) {
@@ -465,4 +504,73 @@ public class DozeService extends DreamService {
} }
} }
} }
private abstract class ProximityCheck implements SensorEventListener, Runnable {
private static final int TIMEOUT_DELAY_MS = 500;
protected static final int RESULT_UNKNOWN = 0;
protected static final int RESULT_NEAR = 1;
protected static final int RESULT_FAR = 2;
private final String mTag = DozeService.this.mTag + ".ProximityCheck";
private boolean mRegistered;
private boolean mFinished;
private float mMaxRange;
abstract public void onProximityResult(int result);
public void check() {
if (mFinished || mRegistered) return;
final Sensor sensor = mSensors.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (sensor == null) {
if (DEBUG) Log.d(mTag, "No sensor found");
finishWithResult(RESULT_UNKNOWN);
return;
}
// the pickup sensor interferes with the prox event, disable it until we have a result
mPickupSensor.setDisabled(true);
mMaxRange = sensor.getMaximumRange();
mSensors.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0, mHandler);
mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
mRegistered = true;
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.values.length == 0) {
if (DEBUG) Log.d(mTag, "Event has no values!");
finishWithResult(RESULT_UNKNOWN);
} else {
if (DEBUG) Log.d(mTag, "Event: value=" + event.values[0] + " max=" + mMaxRange);
final boolean isNear = event.values[0] < mMaxRange;
finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
}
}
@Override
public void run() {
if (DEBUG) Log.d(mTag, "No event received before timeout");
finishWithResult(RESULT_UNKNOWN);
}
private void finishWithResult(int result) {
if (mFinished) return;
if (mRegistered) {
mHandler.removeCallbacks(this);
mSensors.unregisterListener(this);
// we're done - reenable the pickup sensor
mPickupSensor.setDisabled(false);
mRegistered = false;
}
onProximityResult(result);
mFinished = true;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// noop
}
}
} }