AmbientDisplay: Factor out DozeSensors

Change-Id: Ia604e2f1e2ca7af5e514ef434f99b3f85ef0b789
This commit is contained in:
Adrian Roos
2016-10-26 10:40:12 -07:00
parent d80812b882
commit ea8d6aebaf
2 changed files with 277 additions and 204 deletions

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2016 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 com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto;
import com.android.systemui.statusbar.phone.DozeParameters;
import android.annotation.AnyThread;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
public class DozeSensors {
private static final boolean DEBUG = DozeService.DEBUG;
private static final String TAG = "DozeSensors";
private final Context mContext;
private final SensorManager mSensorManager;
private final TriggerSensor[] mSensors;
private final ContentResolver mResolver;
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final PowerManager.WakeLock mWakeLock;
private final Callback mCallback;
private final Handler mHandler = new Handler();
public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
AmbientDisplayConfiguration config, PowerManager.WakeLock wakeLock, Callback callback) {
mContext = context;
mSensorManager = sensorManager;
mDozeParameters = dozeParameters;
mConfig = config;
mWakeLock = wakeLock;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
null /* setting */,
dozeParameters.getPulseOnSigMotion(),
DozeLog.PULSE_REASON_SENSOR_SIGMOTION),
mPickupSensor = new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PULSE_ON_PICK_UP,
config.pulseOnPickupAvailable(),
DozeLog.PULSE_REASON_SENSOR_PICKUP),
new TriggerSensor(
findSensorWithType(config.doubleTapSensorType()),
Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
true /* configured */,
DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
};
mCallback = callback;
}
private Sensor findSensorWithType(String type) {
if (TextUtils.isEmpty(type)) {
return null;
}
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor s : sensorList) {
if (type.equals(s.getStringType())) {
return s;
}
}
return null;
}
public void setListen(boolean listen) {
for (TriggerSensor s : mSensors) {
s.setListening(listen);
if (listen) {
s.registerSettingsObserver(mSettingsObserver);
}
}
if (!listen) {
mResolver.unregisterContentObserver(mSettingsObserver);
}
}
public void reregisterAllSensors() {
for (TriggerSensor s : mSensors) {
s.setListening(false);
}
for (TriggerSensor s : mSensors) {
s.setListening(true);
}
}
public void onUserSwitched() {
for (TriggerSensor s : mSensors) {
s.updateListener();
}
}
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (userId != ActivityManager.getCurrentUser()) {
return;
}
for (TriggerSensor s : mSensors) {
s.updateListener();
}
}
};
public void setDisableSensorsInterferingWithProximity(boolean disable) {
mPickupSensor.setDisabled(disable);
}
private class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
final int mPulseReason;
final String mSetting;
private boolean mRequested;
private boolean mRegistered;
private boolean mDisabled;
public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason) {
mSensor = sensor;
mSetting = setting;
mConfigured = configured;
mPulseReason = pulseReason;
}
public void setListening(boolean listen) {
if (mRequested == listen) return;
mRequested = listen;
updateListener();
}
public void setDisabled(boolean disabled) {
if (mDisabled == disabled) return;
mDisabled = disabled;
updateListener();
}
public void updateListener() {
if (!mConfigured || mSensor == null) return;
if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered);
} else if (mRegistered) {
final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor);
if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt);
mRegistered = false;
}
}
private boolean enabledBySetting() {
if (TextUtils.isEmpty(mSetting)) {
return true;
}
return Settings.Secure.getIntForUser(mResolver, mSetting, 1,
UserHandle.USER_CURRENT) != 0;
}
@Override
public String toString() {
return new StringBuilder("{mRegistered=").append(mRegistered)
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
.append(", mSensor=").append(mSensor).append("}").toString();
}
@Override
@AnyThread
public void onTrigger(TriggerEvent event) {
mWakeLock.acquire();
try {
if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
boolean sensorPerformsProxCheck = false;
if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
int subType = (int) event.values[0];
MetricsLogger.action(
mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE, subType);
sensorPerformsProxCheck =
mDozeParameters.getPickupSubtypePerformsProxCheck(subType);
}
mRegistered = false;
mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck);
updateListener(); // reregister, this sensor only fires once
} finally {
mWakeLock.release();
}
}
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
mResolver.registerContentObserver(
Settings.Secure.getUriFor(mSetting), false /* descendants */,
mSettingsObserver, UserHandle.USER_ALL);
}
}
private String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
.append(event.timestamp).append(',')
.append(event.sensor.getName());
if (event.values != null) {
for (int i = 0; i < event.values.length; i++) {
sb.append(',').append(event.values[i]);
}
}
return sb.append(']').toString();
}
}
public interface Callback {
void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck);
}
}

View File

@@ -16,47 +16,35 @@
package com.android.systemui.doze;
import android.app.ActivityManager;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import com.android.internal.hardware.AmbientDisplayConfiguration;
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 java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
public class DozeService extends DreamService {
public class DozeService extends DreamService implements DozeSensors.Callback {
private static final String TAG = "DozeService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String ACTION_BASE = "com.android.systemui.doze";
private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
@@ -72,9 +60,8 @@ public class DozeService extends DreamService {
private final Handler mHandler = new Handler();
private DozeHost mHost;
private DozeSensors mDozeSensors;
private SensorManager mSensorManager;
private TriggerSensor[] mSensors;
private TriggerSensor mPickupSensor;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
private UiModeManager mUiModeManager;
@@ -101,10 +88,6 @@ public class DozeService extends DreamService {
pw.print(" mWakeLock: held="); pw.println(mWakeLock.isHeld());
pw.print(" mHost: "); pw.println(mHost);
pw.print(" mBroadcastReceiverRegistered: "); pw.println(mBroadcastReceiverRegistered);
for (TriggerSensor s : mSensors) {
pw.print(" sensor: ");
pw.println(s);
}
pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
pw.print(" mPowerSaveActive: "); pw.println(mPowerSaveActive);
pw.print(" mCarMode: "); pw.println(mCarMode);
@@ -129,30 +112,14 @@ public class DozeService extends DreamService {
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
mConfig = new AmbientDisplayConfiguration(mContext);
mSensors = new TriggerSensor[] {
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
null /* setting */,
mDozeParameters.getPulseOnSigMotion(),
mDozeParameters.getVibrateOnSigMotion(),
DozeLog.PULSE_REASON_SENSOR_SIGMOTION),
mPickupSensor = new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PULSE_ON_PICK_UP,
mConfig.pulseOnPickupAvailable(), mDozeParameters.getVibrateOnPickup(),
DozeLog.PULSE_REASON_SENSOR_PICKUP),
new TriggerSensor(
findSensorWithType(mConfig.doubleTapSensorType()),
Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, true,
mDozeParameters.getVibrateOnPickup(),
DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
};
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
turnDisplayOff();
mDozeSensors = new DozeSensors(mContext, mSensorManager, mDozeParameters,
mConfig, mWakeLock, this);
}
@Override
@@ -282,7 +249,7 @@ public class DozeService extends DreamService {
if (mPulsing && mDreaming) {
mPulsing = false;
if (REREGISTER_ALL_SENSORS_ON_SCREEN_OFF) {
reregisterAllSensors();
mDozeSensors.reregisterAllSensors();
}
turnDisplayOff();
}
@@ -313,22 +280,11 @@ public class DozeService extends DreamService {
private void listenForPulseSignals(boolean listen) {
if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
for (TriggerSensor s : mSensors) {
s.setListening(listen);
}
mDozeSensors.setListen(listen);
listenForBroadcasts(listen);
listenForNotifications(listen);
}
private void reregisterAllSensors() {
for (TriggerSensor s : mSensors) {
s.setListening(false);
}
for (TriggerSensor s : mSensors) {
s.setListening(true);
}
}
private void listenForBroadcasts(boolean listen) {
if (listen) {
final IntentFilter filter = new IntentFilter(PULSE_ACTION);
@@ -336,18 +292,10 @@ public class DozeService extends DreamService {
filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mBroadcastReceiver, filter);
for (TriggerSensor s : mSensors) {
if (s.mConfigured && !TextUtils.isEmpty(s.mSetting)) {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(s.mSetting), false /* descendants */,
mSettingsObserver, UserHandle.USER_ALL);
}
}
mBroadcastReceiverRegistered = true;
} else {
if (mBroadcastReceiverRegistered) {
mContext.unregisterReceiver(mBroadcastReceiver);
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
mBroadcastReceiverRegistered = false;
}
@@ -368,17 +316,18 @@ public class DozeService extends DreamService {
requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
}
private static String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
.append(event.timestamp).append(',')
.append(event.sensor.getName());
if (event.values != null) {
for (int i = 0; i < event.values.length; i++) {
sb.append(',').append(event.values[i]);
}
@Override
public void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck) {
requestPulse(pulseReason, sensorPerformedProxCheck);
if (pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP) {
final long timeSinceNotification =
SystemClock.elapsedRealtime() - mNotificationPulseTime;
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
}
return sb.append(']').toString();
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -395,21 +344,7 @@ public class DozeService extends DreamService {
}
}
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
for (TriggerSensor s : mSensors) {
s.updateListener();
}
}
}
};
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (userId != ActivityManager.getCurrentUser()) {
return;
}
for (TriggerSensor s : mSensors) {
s.updateListener();
mDozeSensors.onUserSwitched();
}
}
};
@@ -442,122 +377,6 @@ public class DozeService extends DreamService {
}
};
private Sensor findSensorWithType(String type) {
if (TextUtils.isEmpty(type)) {
return null;
}
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor s : sensorList) {
if (type.equals(s.getStringType())) {
return s;
}
}
return null;
}
private class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
final boolean mDebugVibrate;
final int mPulseReason;
final String mSetting;
private boolean mRequested;
private boolean mRegistered;
private boolean mDisabled;
public TriggerSensor(Sensor sensor, String setting, boolean configured,
boolean debugVibrate, int pulseReason) {
mSensor = sensor;
mSetting = setting;
mConfigured = configured;
mDebugVibrate = debugVibrate;
mPulseReason = pulseReason;
}
public void setListening(boolean listen) {
if (mRequested == listen) return;
mRequested = listen;
updateListener();
}
public void setDisabled(boolean disabled) {
if (mDisabled == disabled) return;
mDisabled = disabled;
updateListener();
}
public void updateListener() {
if (!mConfigured || mSensor == null) return;
if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
if (DEBUG) Log.d(mTag, "requestTriggerSensor " + mRegistered);
} else if (mRegistered) {
final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor);
if (DEBUG) Log.d(mTag, "cancelTriggerSensor " + rt);
mRegistered = false;
}
}
private boolean enabledBySetting() {
if (TextUtils.isEmpty(mSetting)) {
return true;
}
return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSetting, 1,
UserHandle.USER_CURRENT) != 0;
}
@Override
public String toString() {
return new StringBuilder("{mRegistered=").append(mRegistered)
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
.append(", mDebugVibrate=").append(mDebugVibrate)
.append(", mSensor=").append(mSensor).append("}").toString();
}
@Override
public void onTrigger(TriggerEvent event) {
mWakeLock.acquire();
try {
if (DEBUG) Log.d(mTag, "onTrigger: " + triggerEventToString(event));
boolean sensorPerformsProxCheck = false;
if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
int subType = (int) event.values[0];
MetricsLogger.action(mContext, MetricsEvent.ACTION_AMBIENT_GESTURE, subType);
sensorPerformsProxCheck = mDozeParameters.getPickupSubtypePerformsProxCheck(
subType);
}
if (mDebugVibrate) {
final Vibrator v = (Vibrator) mContext.getSystemService(
Context.VIBRATOR_SERVICE);
if (v != null) {
v.vibrate(1000, new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build());
}
}
mRegistered = false;
requestPulse(mPulseReason, sensorPerformsProxCheck);
updateListener(); // reregister, this sensor only fires once
// 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 (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
}
} finally {
mWakeLock.release();
}
}
}
private abstract class ProximityCheck implements SensorEventListener, Runnable {
private static final int TIMEOUT_DELAY_MS = 500;
@@ -581,8 +400,7 @@ public class DozeService extends DreamService {
finishWithResult(RESULT_UNKNOWN);
return;
}
// the pickup sensor interferes with the prox event, disable it until we have a result
mPickupSensor.setDisabled(true);
mDozeSensors.setDisableSensorsInterferingWithProximity(true);
mMaxRange = sensor.getMaximumRange();
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
@@ -614,8 +432,7 @@ public class DozeService extends DreamService {
if (mRegistered) {
mHandler.removeCallbacks(this);
mSensorManager.unregisterListener(this);
// we're done - reenable the pickup sensor
mPickupSensor.setDisabled(false);
mDozeSensors.setDisableSensorsInterferingWithProximity(false);
mRegistered = false;
}
onProximityResult(result);