/* * 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 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 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, true /* 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 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 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 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); } }