blob: 0d3503e5765cc38584b8c2fd90c837959c21eb16 [file] [log] [blame]
/*
* 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 static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
import android.annotation.AnyThread;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.display.AmbientDisplayConfiguration;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.view.Display;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.internal.R;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Tracks and registers/unregisters sensors while the device is dozing based on the config
* provided by {@link AmbientDisplayConfiguration} and parameters provided by {@link DozeParameters}
*
* Sensors registration depends on:
* - sensor existence/availability
* - user configuration (some can be toggled on/off via settings)
* - use of the proximity sensor (sometimes prox cannot be registered in certain display states)
* - touch state
* - device posture
*
* Sensors will trigger the provided Callback's {@link Callback#onSensorPulse} method.
* These sensors include:
* - pickup gesture
* - single and double tap gestures
* - udfps long-press gesture
* - reach and presence gestures
* - quick pickup gesture (low-threshold pickup gesture)
*
* This class also registers a ProximitySensor that reports near/far events and will
* trigger callbacks on the provided {@link mProxCallback}.
*/
public class DozeSensors {
private static final String TAG = "DozeSensors";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private final AsyncSensorManager mSensorManager;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final DozeLog mDozeLog;
private final SecureSettings mSecureSettings;
private final DevicePostureController mDevicePostureController;
private final AuthController mAuthController;
private final UserTracker mUserTracker;
private final boolean mScreenOffUdfpsEnabled;
// Sensors
@VisibleForTesting
protected TriggerSensor[] mTriggerSensors;
private final ProximitySensor mProximitySensor;
// Sensor callbacks
private final Callback mSensorCallback; // receives callbacks on registered sensor events
private final Consumer<Boolean> mProxCallback; // receives callbacks on near/far updates
private final Handler mHandler = new Handler();
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
private boolean mListeningTouchScreenSensors;
private boolean mListeningProxSensors;
private boolean mListeningAodOnlySensors;
private boolean mUdfpsEnrolled;
@DevicePostureController.DevicePostureInt
private int mDevicePosture;
// whether to only register sensors that use prox when the display state is dozing or off
private boolean mSelectivelyRegisterProxSensors;
@VisibleForTesting
public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "User performs pickup gesture that activates the ambient display")
ACTION_AMBIENT_GESTURE_PICKUP(459);
private final int mId;
DozeSensorsUiEvent(int id) {
mId = id;
}
@Override
public int getId() {
return mId;
}
}
DozeSensors(
Resources resources,
AsyncSensorManager sensorManager,
DozeParameters dozeParameters,
AmbientDisplayConfiguration config,
WakeLock wakeLock,
Callback sensorCallback,
Consumer<Boolean> proxCallback,
DozeLog dozeLog,
ProximitySensor proximitySensor,
SecureSettings secureSettings,
AuthController authController,
DevicePostureController devicePostureController,
UserTracker userTracker
) {
mSensorManager = sensorManager;
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
mSecureSettings = secureSettings;
mSensorCallback = sensorCallback;
mDozeLog = dozeLog;
mProximitySensor = proximitySensor;
mProximitySensor.setTag(TAG);
mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
mListeningProxSensors = !mSelectivelyRegisterProxSensors;
mScreenOffUdfpsEnabled =
config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
mDevicePostureController = devicePostureController;
mDevicePosture = mDevicePostureController.getDevicePosture();
mAuthController = authController;
mUserTracker = userTracker;
mUdfpsEnrolled =
mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
mAuthController.addCallback(mAuthControllerCallback);
mTriggerSensors = new TriggerSensor[] {
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
null /* setting */,
dozeParameters.getPulseOnSigMotion(),
DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
false /* touchCoords */,
false /* touchscreen */
),
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
resources.getBoolean(
R.bool.config_dozePickupGestureEnabled) /* settingDef */,
config.dozePickupSensorAvailable(),
DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
false /* touchscreen */,
false /* ignoresSetting */,
false /* requires prox */,
true /* immediatelyReRegister */,
false /* requiresAod */
),
new TriggerSensor(
findSensor(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
DozeLog.REASON_SENSOR_DOUBLE_TAP,
dozeParameters.doubleTapReportsTouchCoordinates(),
true /* touchscreen */
),
new TriggerSensor(
findSensors(config.tapSensorTypeMapping()),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* settingDef */,
true /* configured */,
DozeLog.REASON_SENSOR_TAP,
false /* reports touch coordinates */,
true /* touchscreen */,
false /* ignoresSetting */,
dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */,
true /* immediatelyReRegister */,
mDevicePosture,
false
),
new TriggerSensor(
findSensor(config.longPressSensorType()),
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
false /* settingDef */,
true /* configured */,
DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
false /* ignoresSetting */,
dozeParameters.longPressUsesProx() /* requiresProx */,
true /* immediatelyReRegister */,
false /* requiresAod */
),
new TriggerSensor(
findSensor(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
udfpsLongPressConfigured(),
DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
false /* ignoresSetting */,
dozeParameters.longPressUsesProx(),
false /* immediatelyReRegister */,
true /* requiresAod */
),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable()
&& mConfig.alwaysOnEnabled(mUserTracker.getUserId()),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
false /* touchscreen */
),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
mConfig.wakeScreenGestureAvailable(),
DozeLog.PULSE_REASON_SENSOR_WAKE_REACH,
false /* reports touch coordinates */,
false /* touchscreen */,
mConfig.getWakeLockScreenDebounce()
),
new TriggerSensor(
findSensor(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
true /* setting default */,
quickPickUpConfigured(),
DozeLog.REASON_SENSOR_QUICK_PICKUP,
false /* requiresTouchCoordinates */,
false /* requiresTouchscreen */,
false /* ignoresSetting */,
false /* requiresProx */,
true /* immediatelyReRegister */,
false /* requiresAod */
),
};
setProxListening(false); // Don't immediately start listening when we register.
mProximitySensor.register(
proximityEvent -> {
if (proximityEvent != null) {
mProxCallback.accept(!proximityEvent.getBelow());
}
});
mDevicePostureController.addCallback(mDevicePostureCallback);
}
private boolean udfpsLongPressConfigured() {
return mUdfpsEnrolled
&& (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) || mScreenOffUdfpsEnabled);
}
private boolean quickPickUpConfigured() {
return mUdfpsEnrolled
&& mConfig.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser());
}
/**
* Unregister all sensors and callbacks.
*/
public void destroy() {
// Unregisters everything, which is enough to allow gc.
for (TriggerSensor triggerSensor : mTriggerSensors) {
triggerSensor.setListening(false);
}
mProximitySensor.destroy();
mDevicePostureController.removeCallback(mDevicePostureCallback);
mAuthController.removeCallback(mAuthControllerCallback);
}
/**
* Temporarily disable some sensors to avoid turning on the device while the user is
* turning it off.
*/
public void requestTemporaryDisable() {
mDebounceFrom = SystemClock.uptimeMillis();
}
private Sensor findSensor(String type) {
return findSensor(mSensorManager, type, null);
}
@NonNull
private Sensor[] findSensors(@NonNull String[] types) {
Sensor[] sensorMap = new Sensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
// Map of sensorType => Sensor, so we reuse the same sensor if it's the same between
// postures
Map<String, Sensor> typeToSensorMap = new HashMap<>();
for (int i = 0; i < types.length; i++) {
String sensorType = types[i];
if (!typeToSensorMap.containsKey(sensorType)) {
typeToSensorMap.put(sensorType, findSensor(sensorType));
}
sensorMap[i] = typeToSensorMap.get(sensorType);
}
return sensorMap;
}
/**
* Utility method to find a {@link Sensor} for the supplied string type and string name.
*
* Return the first sensor in the list that matches the specified inputs. Ignores type or name
* if the input is null or empty.
*
* @param type sensorType
* @parm name sensorName, to differentiate between sensors with the same type
*/
public static Sensor findSensor(SensorManager sensorManager, String type, String name) {
final boolean isNameSpecified = !TextUtils.isEmpty(name);
final boolean isTypeSpecified = !TextUtils.isEmpty(type);
if (isNameSpecified || isTypeSpecified) {
final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : sensors) {
if ((!isNameSpecified || name.equals(sensor.getName()))
&& (!isTypeSpecified || type.equals(sensor.getStringType()))) {
return sensor;
}
}
}
return null;
}
/**
* If sensors should be registered and sending signals.
*/
public void setListening(boolean listen, boolean includeTouchScreenSensors,
boolean includeAodOnlySensors) {
if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors
&& mListeningAodOnlySensors == includeAodOnlySensors) {
return;
}
mListening = listen;
mListeningTouchScreenSensors = includeTouchScreenSensors;
mListeningAodOnlySensors = includeAodOnlySensors;
updateListening();
}
/**
* If sensors should be registered and sending signals.
*/
public void setListeningWithPowerState(boolean listen, boolean includeTouchScreenSensors,
boolean includeAodRequiringSensors, boolean lowPowerStateOrOff) {
final boolean shouldRegisterProxSensors =
!mSelectivelyRegisterProxSensors || lowPowerStateOrOff;
if (mListening == listen
&& mListeningTouchScreenSensors == includeTouchScreenSensors
&& mListeningProxSensors == shouldRegisterProxSensors
&& mListeningAodOnlySensors == includeAodRequiringSensors
) {
return;
}
mListening = listen;
mListeningTouchScreenSensors = includeTouchScreenSensors;
mListeningProxSensors = shouldRegisterProxSensors;
mListeningAodOnlySensors = includeAodRequiringSensors;
updateListening();
}
/**
* Registers/unregisters sensors based on internal state.
*/
private void updateListening() {
boolean anyListening = false;
for (TriggerSensor s : mTriggerSensors) {
boolean listen = mListening
&& (!s.mRequiresTouchscreen || mListeningTouchScreenSensors)
&& (!s.mRequiresProx || mListeningProxSensors)
&& (!s.mRequiresAod || mListeningAodOnlySensors);
s.setListening(listen);
if (listen) {
anyListening = true;
}
}
if (!anyListening) {
mSecureSettings.unregisterContentObserver(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mTriggerSensors) {
s.registerSettingsObserver(mSettingsObserver);
}
}
mSettingRegistered = anyListening;
}
/** Set the listening state of only the sensors that require the touchscreen. */
public void setTouchscreenSensorsListening(boolean listening) {
for (TriggerSensor sensor : mTriggerSensors) {
if (sensor.mRequiresTouchscreen) {
sensor.setListening(listening);
}
}
}
public void onUserSwitched() {
for (TriggerSensor s : mTriggerSensors) {
s.updateListening();
}
}
void onScreenState(int state) {
mProximitySensor.setSecondarySafe(
state == Display.STATE_DOZE
|| state == Display.STATE_DOZE_SUSPEND
|| state == Display.STATE_OFF);
}
public void setProxListening(boolean listen) {
if (mProximitySensor.isRegistered() && listen) {
mProximitySensor.alertListeners();
} else {
if (listen) {
mProximitySensor.resume();
} else {
mProximitySensor.pause();
}
}
}
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
if (userId != mUserTracker.getUserId()) {
return;
}
for (TriggerSensor s : mTriggerSensors) {
s.updateListening();
}
}
};
/** Ignore the setting value of only the sensors that require the touchscreen. */
public void ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore) {
for (TriggerSensor sensor : mTriggerSensors) {
if (sensor.mRequiresTouchscreen) {
sensor.ignoreSetting(ignore);
}
}
}
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("mListening=" + mListening);
pw.println("mDevicePosture="
+ DevicePostureController.devicePostureToString(mDevicePosture));
pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
pw.println("mSelectivelyRegisterProxSensors=" + mSelectivelyRegisterProxSensors);
pw.println("mListeningProxSensors=" + mListeningProxSensors);
pw.println("mScreenOffUdfpsEnabled=" + mScreenOffUdfpsEnabled);
pw.println("mUdfpsEnrolled=" + mUdfpsEnrolled);
IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
idpw.increaseIndent();
for (TriggerSensor s : mTriggerSensors) {
idpw.println("Sensor: " + s.toString());
}
idpw.println("ProxSensor: " + mProximitySensor.toString());
}
/**
* @return true if prox is currently near, false if far or null if unknown.
*/
public Boolean isProximityCurrentlyNear() {
return mProximitySensor.isNear();
}
@VisibleForTesting
class TriggerSensor extends TriggerEventListener {
@NonNull final Sensor[] mSensors; // index = posture, value = sensor
boolean mConfigured;
final int mPulseReason;
private final String mSetting;
private final boolean mReportsTouchCoordinates;
private final boolean mSettingDefault;
private final boolean mRequiresTouchscreen;
private final boolean mRequiresProx;
// Whether the sensor should only register if the device is in AOD
private final boolean mRequiresAod;
// Whether to immediately re-register this sensor after the sensor is triggered.
// If false, the sensor registration will be updated on the next AOD state transition.
private final boolean mImmediatelyReRegister;
protected boolean mRequested;
protected boolean mRegistered;
protected boolean mDisabled;
protected boolean mIgnoresSetting;
private @DevicePostureController.DevicePostureInt int mPosture;
TriggerSensor(
Sensor sensor,
String setting,
boolean configured,
int pulseReason,
boolean reportsTouchCoordinates,
boolean requiresTouchscreen
) {
this(
sensor,
setting,
true /* settingDef */,
configured,
pulseReason,
reportsTouchCoordinates,
requiresTouchscreen,
false /* ignoresSetting */,
false /* requiresProx */,
true /* immediatelyReRegister */,
false
);
}
TriggerSensor(
Sensor sensor,
String setting,
boolean settingDef,
boolean configured,
int pulseReason,
boolean reportsTouchCoordinates,
boolean requiresTouchscreen,
boolean ignoresSetting,
boolean requiresProx,
boolean immediatelyReRegister,
boolean requiresAod
) {
this(
new Sensor[]{ sensor },
setting,
settingDef,
configured,
pulseReason,
reportsTouchCoordinates,
requiresTouchscreen,
ignoresSetting,
requiresProx,
immediatelyReRegister,
DevicePostureController.DEVICE_POSTURE_UNKNOWN,
requiresAod
);
}
TriggerSensor(
@NonNull Sensor[] sensors,
String setting,
boolean settingDef,
boolean configured,
int pulseReason,
boolean reportsTouchCoordinates,
boolean requiresTouchscreen,
boolean ignoresSetting,
boolean requiresProx,
boolean immediatelyReRegister,
@DevicePostureController.DevicePostureInt int posture,
boolean requiresAod
) {
mSensors = sensors;
mSetting = setting;
mSettingDefault = settingDef;
mConfigured = configured;
mPulseReason = pulseReason;
mReportsTouchCoordinates = reportsTouchCoordinates;
mRequiresTouchscreen = requiresTouchscreen;
mIgnoresSetting = ignoresSetting;
mRequiresProx = requiresProx;
mRequiresAod = requiresAod;
mPosture = posture;
mImmediatelyReRegister = immediatelyReRegister;
}
/**
* @return true if the sensor changed based for the new posture
*/
public boolean setPosture(@DevicePostureController.DevicePostureInt int posture) {
if (mPosture == posture
|| mSensors.length < 2
|| posture >= mSensors.length) {
return false;
}
Sensor oldSensor = mSensors[mPosture];
Sensor newSensor = mSensors[posture];
if (Objects.equals(oldSensor, newSensor)) {
mPosture = posture;
// uses the same sensor for the new posture
return false;
}
// cancel the previous sensor:
if (mRegistered) {
final boolean rt = mSensorManager.cancelTriggerSensor(this, oldSensor);
mDozeLog.traceSensorUnregisterAttempt(oldSensor.toString(), rt, "posture changed");
mRegistered = false;
}
// update the new sensor:
mPosture = posture;
updateListening();
mDozeLog.tracePostureChanged(mPosture, "DozeSensors swap "
+ "{" + oldSensor + "} => {" + newSensor + "}, mRegistered=" + mRegistered);
return true;
}
public void setListening(boolean listen) {
if (mRequested == listen) return;
mRequested = listen;
updateListening();
}
public void setDisabled(boolean disabled) {
if (mDisabled == disabled) return;
mDisabled = disabled;
updateListening();
}
public void ignoreSetting(boolean ignored) {
if (mIgnoresSetting == ignored) return;
mIgnoresSetting = ignored;
updateListening();
}
/**
* Update configured state.
*/
public void setConfigured(boolean configured) {
if (mConfigured == configured) return;
mConfigured = configured;
updateListening();
}
public void updateListening() {
final Sensor sensor = mSensors[mPosture];
if (!mConfigured || sensor == null) return;
if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)) {
if (!mRegistered) {
mRegistered = mSensorManager.requestTriggerSensor(this, sensor);
mDozeLog.traceSensorRegisterAttempt(sensor.toString(), mRegistered);
} else {
mDozeLog.traceSkipRegisterSensor(sensor.toString());
}
} else if (mRegistered) {
final boolean rt = mSensorManager.cancelTriggerSensor(this, sensor);
mDozeLog.traceSensorUnregisterAttempt(sensor.toString(), rt);
mRegistered = false;
}
}
protected boolean enabledBySetting() {
if (!mConfig.enabled(mUserTracker.getUserId())) {
return false;
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
mUserTracker.getUserId()) != 0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{")
.append("mRegistered=").append(mRegistered)
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
.append(", mIgnoresSetting=").append(mIgnoresSetting)
.append(", mSensors=").append(Arrays.toString(mSensors));
if (mSensors.length > 2) {
sb.append(", mPosture=")
.append(DevicePostureController.devicePostureToString(mDevicePosture));
}
return sb.append("}").toString();
}
@Override
@AnyThread
public void onTrigger(TriggerEvent event) {
final Sensor sensor = mSensors[mPosture];
mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
if (sensor != null && sensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP);
}
mRegistered = false;
float screenX = -1;
float screenY = -1;
if (mReportsTouchCoordinates && event.values.length >= 2) {
screenX = event.values[0];
screenY = event.values[1];
}
mSensorCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values);
if (!mRegistered && mImmediatelyReRegister) {
updateListening();
}
}));
}
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
mSecureSettings.registerContentObserverForUser(
mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
protected String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("SensorEvent[")
.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();
}
}
/**
* A Sensor that is injected via plugin.
*/
@VisibleForTesting
class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener {
final SensorManagerPlugin.Sensor mPluginSensor;
private long mDebounce;
PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
this(sensor, setting, configured, pulseReason, reportsTouchCoordinates,
requiresTouchscreen, 0L /* debounce */);
}
PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
long debounce) {
super(null, setting, configured, pulseReason, reportsTouchCoordinates,
requiresTouchscreen);
mPluginSensor = sensor;
mDebounce = debounce;
}
@Override
public void updateListening() {
if (!mConfigured) return;
AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
&& !mRegistered) {
asyncSensorManager.registerPluginListener(mPluginSensor, this);
mRegistered = true;
mDozeLog.tracePluginSensorUpdate(true /* registered */);
} else if (mRegistered) {
asyncSensorManager.unregisterPluginListener(mPluginSensor, this);
mRegistered = false;
mDozeLog.tracePluginSensorUpdate(false /* registered */);
}
}
@Override
public String toString() {
return new StringBuilder("{mRegistered=").append(mRegistered)
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
.append(", mIgnoresSetting=").append(mIgnoresSetting)
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
private String triggerEventToString(SensorManagerPlugin.SensorEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
.append(event.getSensor()).append(',')
.append(event.getVendorType());
if (event.getValues() != null) {
for (int i = 0; i < event.getValues().length; i++) {
sb.append(',').append(event.getValues()[i]);
}
}
return sb.append(']').toString();
}
@Override
public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
final long now = SystemClock.uptimeMillis();
if (now < mDebounceFrom + mDebounce) {
mDozeLog.traceSensorEventDropped(mPulseReason, "debounce");
return;
}
mSensorCallback.onSensorPulse(mPulseReason, -1, -1, event.getValues());
}));
}
}
private final DevicePostureController.Callback mDevicePostureCallback = posture -> {
if (mDevicePosture == posture) {
return;
}
mDevicePosture = posture;
for (TriggerSensor triggerSensor : mTriggerSensors) {
triggerSensor.setPosture(mDevicePosture);
}
};
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
if (modality == TYPE_FINGERPRINT) {
updateUdfpsEnrolled();
}
}
@Override
public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
if (modality == TYPE_FINGERPRINT) {
updateUdfpsEnrolled();
}
}
private void updateUdfpsEnrolled() {
mUdfpsEnrolled = mAuthController.isUdfpsEnrolled(
KeyguardUpdateMonitor.getCurrentUser());
for (TriggerSensor sensor : mTriggerSensors) {
if (REASON_SENSOR_QUICK_PICKUP == sensor.mPulseReason) {
sensor.setConfigured(quickPickUpConfigured());
} else if (REASON_SENSOR_UDFPS_LONG_PRESS == sensor.mPulseReason) {
sensor.setConfigured(udfpsLongPressConfigured());
}
}
}
};
public interface Callback {
/**
* Called when a sensor requests a pulse
* @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
* @param screenX the location on the screen where the sensor fired or -1
* if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
* @param rawValues raw values array from the event.
*/
void onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues);
}
}