1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.doze; 18 19 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; 20 21 import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; 22 import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; 23 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY; 24 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; 25 26 import android.annotation.AnyThread; 27 import android.content.res.Resources; 28 import android.database.ContentObserver; 29 import android.hardware.Sensor; 30 import android.hardware.SensorManager; 31 import android.hardware.TriggerEvent; 32 import android.hardware.TriggerEventListener; 33 import android.hardware.biometrics.BiometricAuthenticator; 34 import android.hardware.display.AmbientDisplayConfiguration; 35 import android.net.Uri; 36 import android.os.Handler; 37 import android.os.SystemClock; 38 import android.os.UserHandle; 39 import android.provider.Settings; 40 import android.text.TextUtils; 41 import android.util.IndentingPrintWriter; 42 import android.view.Display; 43 44 import androidx.annotation.NonNull; 45 import androidx.annotation.VisibleForTesting; 46 47 import com.android.internal.R; 48 import com.android.internal.logging.UiEvent; 49 import com.android.internal.logging.UiEventLogger; 50 import com.android.internal.logging.UiEventLoggerImpl; 51 import com.android.systemui.biometrics.AuthController; 52 import com.android.systemui.plugins.SensorManagerPlugin; 53 import com.android.systemui.statusbar.phone.DozeParameters; 54 import com.android.systemui.statusbar.policy.DevicePostureController; 55 import com.android.systemui.user.domain.interactor.SelectedUserInteractor; 56 import com.android.systemui.util.sensors.AsyncSensorManager; 57 import com.android.systemui.util.sensors.ProximitySensor; 58 import com.android.systemui.util.settings.SecureSettings; 59 import com.android.systemui.util.wakelock.WakeLock; 60 61 import java.io.PrintWriter; 62 import java.util.Arrays; 63 import java.util.Collection; 64 import java.util.HashMap; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Objects; 68 import java.util.function.Consumer; 69 70 /** 71 * Tracks and registers/unregisters sensors while the device is dozing based on the config 72 * provided by {@link AmbientDisplayConfiguration} and parameters provided by {@link DozeParameters} 73 * 74 * Sensors registration depends on: 75 * - sensor existence/availability 76 * - user configuration (some can be toggled on/off via settings) 77 * - use of the proximity sensor (sometimes prox cannot be registered in certain display states) 78 * - touch state 79 * - device posture 80 * 81 * Sensors will trigger the provided Callback's {@link Callback#onSensorPulse} method. 82 * These sensors include: 83 * - pickup gesture 84 * - single and double tap gestures 85 * - udfps long-press gesture 86 * - reach and presence gestures 87 * - quick pickup gesture (low-threshold pickup gesture) 88 * 89 * This class also registers a ProximitySensor that reports near/far events and will 90 * trigger callbacks on the provided {@link mProxCallback}. 91 */ 92 public class DozeSensors { 93 private static final String TAG = "DozeSensors"; 94 private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); 95 96 private final AsyncSensorManager mSensorManager; 97 private final AmbientDisplayConfiguration mConfig; 98 private final WakeLock mWakeLock; 99 private final DozeLog mDozeLog; 100 private final SecureSettings mSecureSettings; 101 private final DevicePostureController mDevicePostureController; 102 private final AuthController mAuthController; 103 private final SelectedUserInteractor mSelectedUserInteractor; 104 private final boolean mScreenOffUdfpsEnabled; 105 106 // Sensors 107 @VisibleForTesting 108 protected TriggerSensor[] mTriggerSensors; 109 private final ProximitySensor mProximitySensor; 110 111 // Sensor callbacks 112 private final Callback mSensorCallback; // receives callbacks on registered sensor events 113 private final Consumer<Boolean> mProxCallback; // receives callbacks on near/far updates 114 115 private final Handler mHandler = new Handler(); 116 private long mDebounceFrom; 117 private boolean mSettingRegistered; 118 private boolean mListening; 119 private boolean mListeningTouchScreenSensors; 120 private boolean mListeningProxSensors; 121 private boolean mListeningAodOnlySensors; 122 private boolean mUdfpsEnrolled; 123 124 @DevicePostureController.DevicePostureInt 125 private int mDevicePosture; 126 127 // whether to only register sensors that use prox when the display state is dozing or off 128 private boolean mSelectivelyRegisterProxSensors; 129 130 @VisibleForTesting 131 public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum { 132 @UiEvent(doc = "User performs pickup gesture that activates the ambient display") 133 ACTION_AMBIENT_GESTURE_PICKUP(459); 134 135 private final int mId; 136 DozeSensorsUiEvent(int id)137 DozeSensorsUiEvent(int id) { 138 mId = id; 139 } 140 141 @Override getId()142 public int getId() { 143 return mId; 144 } 145 } 146 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, SelectedUserInteractor selectedUserInteractor )147 DozeSensors( 148 Resources resources, 149 AsyncSensorManager sensorManager, 150 DozeParameters dozeParameters, 151 AmbientDisplayConfiguration config, 152 WakeLock wakeLock, 153 Callback sensorCallback, 154 Consumer<Boolean> proxCallback, 155 DozeLog dozeLog, 156 ProximitySensor proximitySensor, 157 SecureSettings secureSettings, 158 AuthController authController, 159 DevicePostureController devicePostureController, 160 SelectedUserInteractor selectedUserInteractor 161 ) { 162 mSensorManager = sensorManager; 163 mConfig = config; 164 mWakeLock = wakeLock; 165 mProxCallback = proxCallback; 166 mSecureSettings = secureSettings; 167 mSensorCallback = sensorCallback; 168 mDozeLog = dozeLog; 169 mProximitySensor = proximitySensor; 170 mProximitySensor.setTag(TAG); 171 mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx(); 172 mListeningProxSensors = !mSelectivelyRegisterProxSensors; 173 mSelectedUserInteractor = selectedUserInteractor; 174 mScreenOffUdfpsEnabled = 175 config.screenOffUdfpsEnabled(mSelectedUserInteractor.getSelectedUserId()); 176 mDevicePostureController = devicePostureController; 177 mDevicePosture = mDevicePostureController.getDevicePosture(); 178 mAuthController = authController; 179 180 mUdfpsEnrolled = 181 mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId()); 182 mAuthController.addCallback(mAuthControllerCallback); 183 mTriggerSensors = new TriggerSensor[] { 184 new TriggerSensor( 185 mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), 186 null /* setting */, 187 dozeParameters.getPulseOnSigMotion(), 188 DozeLog.PULSE_REASON_SENSOR_SIGMOTION, 189 false /* touchCoords */, 190 false /* touchscreen */ 191 ), 192 new TriggerSensor( 193 mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), 194 Settings.Secure.DOZE_PICK_UP_GESTURE, 195 resources.getBoolean( 196 R.bool.config_dozePickupGestureEnabled) /* settingDef */, 197 config.dozePickupSensorAvailable(), 198 DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */, 199 false /* touchscreen */, 200 false /* ignoresSetting */, 201 false /* requires prox */, 202 true /* immediatelyReRegister */, 203 false /* requiresAod */ 204 ), 205 new TriggerSensor( 206 findSensor(config.doubleTapSensorType()), 207 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 208 true /* configured */, 209 DozeLog.REASON_SENSOR_DOUBLE_TAP, 210 dozeParameters.doubleTapReportsTouchCoordinates(), 211 true /* touchscreen */ 212 ), 213 new TriggerSensor( 214 findSensors(config.tapSensorTypeMapping()), 215 Settings.Secure.DOZE_TAP_SCREEN_GESTURE, 216 true /* settingDef */, 217 true /* configured */, 218 DozeLog.REASON_SENSOR_TAP, 219 true /* reports touch coordinates */, 220 true /* touchscreen */, 221 false /* ignoresSetting */, 222 dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */, 223 true /* immediatelyReRegister */, 224 mDevicePosture, 225 false 226 ), 227 new TriggerSensor( 228 findSensor(config.longPressSensorType()), 229 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, 230 false /* settingDef */, 231 true /* configured */, 232 DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 233 true /* reports touch coordinates */, 234 true /* touchscreen */, 235 false /* ignoresSetting */, 236 dozeParameters.longPressUsesProx() /* requiresProx */, 237 true /* immediatelyReRegister */, 238 false /* requiresAod */ 239 ), 240 new TriggerSensor( 241 findSensor(config.udfpsLongPressSensorType()), 242 "doze_pulse_on_auth", 243 true /* settingDef */, 244 udfpsLongPressConfigured(), 245 DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, 246 true /* reports touch coordinates */, 247 true /* touchscreen */, 248 false /* ignoresSetting */, 249 dozeParameters.longPressUsesProx(), 250 false /* immediatelyReRegister */, 251 true /* requiresAod */ 252 ), 253 new PluginSensor( 254 new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), 255 Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, 256 mConfig.wakeScreenGestureAvailable() 257 && mConfig.alwaysOnEnabled( 258 mSelectedUserInteractor.getSelectedUserId(true)), 259 DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE, 260 false /* reports touch coordinates */, 261 false /* touchscreen */ 262 ), 263 new PluginSensor( 264 new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), 265 Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, 266 mConfig.wakeScreenGestureAvailable(), 267 DozeLog.PULSE_REASON_SENSOR_WAKE_REACH, 268 false /* reports touch coordinates */, 269 false /* touchscreen */, 270 mConfig.getWakeLockScreenDebounce() 271 ), 272 new TriggerSensor( 273 findSensor(config.quickPickupSensorType()), 274 Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, 275 true /* setting default */, 276 quickPickUpConfigured(), 277 DozeLog.REASON_SENSOR_QUICK_PICKUP, 278 false /* requiresTouchCoordinates */, 279 false /* requiresTouchscreen */, 280 false /* ignoresSetting */, 281 false /* requiresProx */, 282 true /* immediatelyReRegister */, 283 false /* requiresAod */ 284 ), 285 }; 286 setProxListening(false); // Don't immediately start listening when we register. 287 mProximitySensor.register( 288 proximityEvent -> { 289 if (proximityEvent != null) { 290 mProxCallback.accept(!proximityEvent.getBelow()); 291 } 292 }); 293 294 mDevicePostureController.addCallback(mDevicePostureCallback); 295 } 296 udfpsLongPressConfigured()297 private boolean udfpsLongPressConfigured() { 298 return mUdfpsEnrolled 299 && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId(true)) 300 || mScreenOffUdfpsEnabled); 301 } 302 quickPickUpConfigured()303 private boolean quickPickUpConfigured() { 304 return mUdfpsEnrolled 305 && mConfig.quickPickupSensorEnabled(mSelectedUserInteractor.getSelectedUserId()); 306 } 307 308 /** 309 * Unregister all sensors and callbacks. 310 */ destroy()311 public void destroy() { 312 // Unregisters everything, which is enough to allow gc. 313 for (TriggerSensor triggerSensor : mTriggerSensors) { 314 triggerSensor.setListening(false); 315 } 316 mProximitySensor.destroy(); 317 318 mDevicePostureController.removeCallback(mDevicePostureCallback); 319 mAuthController.removeCallback(mAuthControllerCallback); 320 } 321 322 /** 323 * Temporarily disable some sensors to avoid turning on the device while the user is 324 * turning it off. 325 */ requestTemporaryDisable()326 public void requestTemporaryDisable() { 327 mDebounceFrom = SystemClock.uptimeMillis(); 328 } 329 findSensor(String type)330 private Sensor findSensor(String type) { 331 return findSensor(mSensorManager, type, null); 332 } 333 334 @NonNull findSensors(@onNull String[] types)335 private Sensor[] findSensors(@NonNull String[] types) { 336 Sensor[] sensorMap = new Sensor[DevicePostureController.SUPPORTED_POSTURES_SIZE]; 337 338 // Map of sensorType => Sensor, so we reuse the same sensor if it's the same between 339 // postures 340 Map<String, Sensor> typeToSensorMap = new HashMap<>(); 341 for (int i = 0; i < types.length; i++) { 342 String sensorType = types[i]; 343 if (!typeToSensorMap.containsKey(sensorType)) { 344 typeToSensorMap.put(sensorType, findSensor(sensorType)); 345 } 346 sensorMap[i] = typeToSensorMap.get(sensorType); 347 } 348 349 return sensorMap; 350 } 351 352 /** 353 * Utility method to find a {@link Sensor} for the supplied string type and string name. 354 * 355 * Return the first sensor in the list that matches the specified inputs. Ignores type or name 356 * if the input is null or empty. 357 * 358 * @param type sensorType 359 * @parm name sensorName, to differentiate between sensors with the same type 360 */ findSensor(SensorManager sensorManager, String type, String name)361 public static Sensor findSensor(SensorManager sensorManager, String type, String name) { 362 final boolean isNameSpecified = !TextUtils.isEmpty(name); 363 final boolean isTypeSpecified = !TextUtils.isEmpty(type); 364 if (isNameSpecified || isTypeSpecified) { 365 final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); 366 for (Sensor sensor : sensors) { 367 if ((!isNameSpecified || name.equals(sensor.getName())) 368 && (!isTypeSpecified || type.equals(sensor.getStringType()))) { 369 return sensor; 370 } 371 } 372 } 373 return null; 374 } 375 376 /** 377 * If sensors should be registered and sending signals. 378 */ setListening(boolean listen, boolean includeTouchScreenSensors, boolean includeAodOnlySensors)379 public void setListening(boolean listen, boolean includeTouchScreenSensors, 380 boolean includeAodOnlySensors) { 381 if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors 382 && mListeningAodOnlySensors == includeAodOnlySensors) { 383 return; 384 } 385 mListening = listen; 386 mListeningTouchScreenSensors = includeTouchScreenSensors; 387 mListeningAodOnlySensors = includeAodOnlySensors; 388 updateListening(); 389 } 390 391 /** 392 * If sensors should be registered and sending signals. 393 */ setListeningWithPowerState(boolean listen, boolean includeTouchScreenSensors, boolean includeAodRequiringSensors, boolean lowPowerStateOrOff)394 public void setListeningWithPowerState(boolean listen, boolean includeTouchScreenSensors, 395 boolean includeAodRequiringSensors, boolean lowPowerStateOrOff) { 396 final boolean shouldRegisterProxSensors = 397 !mSelectivelyRegisterProxSensors || lowPowerStateOrOff; 398 if (mListening == listen 399 && mListeningTouchScreenSensors == includeTouchScreenSensors 400 && mListeningProxSensors == shouldRegisterProxSensors 401 && mListeningAodOnlySensors == includeAodRequiringSensors 402 ) { 403 return; 404 } 405 mListening = listen; 406 mListeningTouchScreenSensors = includeTouchScreenSensors; 407 mListeningProxSensors = shouldRegisterProxSensors; 408 mListeningAodOnlySensors = includeAodRequiringSensors; 409 updateListening(); 410 } 411 412 /** 413 * Registers/unregisters sensors based on internal state. 414 */ updateListening()415 private void updateListening() { 416 boolean anyListening = false; 417 for (TriggerSensor s : mTriggerSensors) { 418 boolean listen = mListening 419 && (!s.mRequiresTouchscreen || mListeningTouchScreenSensors) 420 && (!s.mRequiresProx || mListeningProxSensors) 421 && (!s.mRequiresAod || mListeningAodOnlySensors); 422 s.setListening(listen); 423 if (listen) { 424 anyListening = true; 425 } 426 } 427 428 if (!anyListening) { 429 mSecureSettings.unregisterContentObserverSync(mSettingsObserver); 430 } else if (!mSettingRegistered) { 431 for (TriggerSensor s : mTriggerSensors) { 432 s.registerSettingsObserver(mSettingsObserver); 433 } 434 } 435 mSettingRegistered = anyListening; 436 } 437 438 /** Set the listening state of only the sensors that require the touchscreen. */ setTouchscreenSensorsListening(boolean listening)439 public void setTouchscreenSensorsListening(boolean listening) { 440 for (TriggerSensor sensor : mTriggerSensors) { 441 if (sensor.mRequiresTouchscreen) { 442 sensor.setListening(listening); 443 } 444 } 445 } 446 onUserSwitched()447 public void onUserSwitched() { 448 for (TriggerSensor s : mTriggerSensors) { 449 s.updateListening(); 450 } 451 } 452 onScreenState(int state)453 void onScreenState(int state) { 454 mProximitySensor.setSecondarySafe( 455 state == Display.STATE_DOZE 456 || state == Display.STATE_DOZE_SUSPEND 457 || state == Display.STATE_OFF); 458 } 459 setProxListening(boolean listen)460 public void setProxListening(boolean listen) { 461 if (mProximitySensor.isRegistered() && listen) { 462 mProximitySensor.alertListeners(); 463 } else { 464 if (listen) { 465 mProximitySensor.resume(); 466 } else { 467 mProximitySensor.pause(); 468 } 469 } 470 } 471 472 private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 473 @Override 474 public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { 475 if (userId != mSelectedUserInteractor.getSelectedUserId(true)) { 476 return; 477 } 478 for (TriggerSensor s : mTriggerSensors) { 479 s.updateListening(); 480 } 481 } 482 }; 483 484 /** Ignore the setting value of only the sensors that require the touchscreen. */ ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore)485 public void ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore) { 486 for (TriggerSensor sensor : mTriggerSensors) { 487 if (sensor.mRequiresTouchscreen) { 488 sensor.ignoreSetting(ignore); 489 } 490 } 491 } 492 493 /** Dump current state */ dump(PrintWriter pw)494 public void dump(PrintWriter pw) { 495 pw.println("mListening=" + mListening); 496 pw.println("mDevicePosture=" 497 + DevicePostureController.devicePostureToString(mDevicePosture)); 498 pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors); 499 pw.println("mSelectivelyRegisterProxSensors=" + mSelectivelyRegisterProxSensors); 500 pw.println("mListeningProxSensors=" + mListeningProxSensors); 501 pw.println("mScreenOffUdfpsEnabled=" + mScreenOffUdfpsEnabled); 502 pw.println("mUdfpsEnrolled=" + mUdfpsEnrolled); 503 IndentingPrintWriter idpw = new IndentingPrintWriter(pw); 504 idpw.increaseIndent(); 505 for (TriggerSensor s : mTriggerSensors) { 506 idpw.println("Sensor: " + s.toString()); 507 } 508 idpw.println("ProxSensor: " + mProximitySensor.toString()); 509 } 510 511 /** 512 * @return true if prox is currently near, false if far or null if unknown. 513 */ isProximityCurrentlyNear()514 public Boolean isProximityCurrentlyNear() { 515 return mProximitySensor.isNear(); 516 } 517 518 @VisibleForTesting 519 class TriggerSensor extends TriggerEventListener { 520 @NonNull final Sensor[] mSensors; // index = posture, value = sensor 521 boolean mConfigured; 522 final int mPulseReason; 523 private final String mSetting; 524 private final boolean mReportsTouchCoordinates; 525 private final boolean mSettingDefault; 526 private final boolean mRequiresTouchscreen; 527 private final boolean mRequiresProx; 528 529 // Whether the sensor should only register if the device is in AOD 530 private final boolean mRequiresAod; 531 532 // Whether to immediately re-register this sensor after the sensor is triggered. 533 // If false, the sensor registration will be updated on the next AOD state transition. 534 private final boolean mImmediatelyReRegister; 535 536 protected boolean mRequested; 537 protected boolean mRegistered; 538 protected boolean mDisabled; 539 protected boolean mIgnoresSetting; 540 private @DevicePostureController.DevicePostureInt int mPosture; 541 TriggerSensor( Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen )542 TriggerSensor( 543 Sensor sensor, 544 String setting, 545 boolean configured, 546 int pulseReason, 547 boolean reportsTouchCoordinates, 548 boolean requiresTouchscreen 549 ) { 550 this( 551 sensor, 552 setting, 553 true /* settingDef */, 554 configured, 555 pulseReason, 556 reportsTouchCoordinates, 557 requiresTouchscreen, 558 false /* ignoresSetting */, 559 false /* requiresProx */, 560 true /* immediatelyReRegister */, 561 false 562 ); 563 } 564 TriggerSensor( Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, boolean ignoresSetting, boolean requiresProx, boolean immediatelyReRegister, boolean requiresAod )565 TriggerSensor( 566 Sensor sensor, 567 String setting, 568 boolean settingDef, 569 boolean configured, 570 int pulseReason, 571 boolean reportsTouchCoordinates, 572 boolean requiresTouchscreen, 573 boolean ignoresSetting, 574 boolean requiresProx, 575 boolean immediatelyReRegister, 576 boolean requiresAod 577 ) { 578 this( 579 new Sensor[]{ sensor }, 580 setting, 581 settingDef, 582 configured, 583 pulseReason, 584 reportsTouchCoordinates, 585 requiresTouchscreen, 586 ignoresSetting, 587 requiresProx, 588 immediatelyReRegister, 589 DevicePostureController.DEVICE_POSTURE_UNKNOWN, 590 requiresAod 591 ); 592 } 593 TriggerSensor( @onNull 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 )594 TriggerSensor( 595 @NonNull Sensor[] sensors, 596 String setting, 597 boolean settingDef, 598 boolean configured, 599 int pulseReason, 600 boolean reportsTouchCoordinates, 601 boolean requiresTouchscreen, 602 boolean ignoresSetting, 603 boolean requiresProx, 604 boolean immediatelyReRegister, 605 @DevicePostureController.DevicePostureInt int posture, 606 boolean requiresAod 607 ) { 608 mSensors = sensors; 609 mSetting = setting; 610 mSettingDefault = settingDef; 611 mConfigured = configured; 612 mPulseReason = pulseReason; 613 mReportsTouchCoordinates = reportsTouchCoordinates; 614 mRequiresTouchscreen = requiresTouchscreen; 615 mIgnoresSetting = ignoresSetting; 616 mRequiresProx = requiresProx; 617 mRequiresAod = requiresAod; 618 mPosture = posture; 619 mImmediatelyReRegister = immediatelyReRegister; 620 } 621 622 /** 623 * @return true if the sensor changed based for the new posture 624 */ setPosture(@evicePostureController.DevicePostureInt int posture)625 public boolean setPosture(@DevicePostureController.DevicePostureInt int posture) { 626 if (mPosture == posture 627 || mSensors.length < 2 628 || posture >= mSensors.length) { 629 return false; 630 } 631 632 Sensor oldSensor = mSensors[mPosture]; 633 Sensor newSensor = mSensors[posture]; 634 if (Objects.equals(oldSensor, newSensor)) { 635 mPosture = posture; 636 // uses the same sensor for the new posture 637 return false; 638 } 639 640 // cancel the previous sensor: 641 if (mRegistered) { 642 final boolean rt = mSensorManager.cancelTriggerSensor(this, oldSensor); 643 mDozeLog.traceSensorUnregisterAttempt(oldSensor.toString(), rt, "posture changed"); 644 mRegistered = false; 645 } 646 647 // update the new sensor: 648 mPosture = posture; 649 updateListening(); 650 mDozeLog.tracePostureChanged(mPosture, "DozeSensors swap " 651 + "{" + oldSensor + "} => {" + newSensor + "}, mRegistered=" + mRegistered); 652 return true; 653 } 654 setListening(boolean listen)655 public void setListening(boolean listen) { 656 if (mRequested == listen) return; 657 mRequested = listen; 658 updateListening(); 659 } 660 setDisabled(boolean disabled)661 public void setDisabled(boolean disabled) { 662 if (mDisabled == disabled) return; 663 mDisabled = disabled; 664 updateListening(); 665 } 666 ignoreSetting(boolean ignored)667 public void ignoreSetting(boolean ignored) { 668 if (mIgnoresSetting == ignored) return; 669 mIgnoresSetting = ignored; 670 updateListening(); 671 } 672 673 /** 674 * Update configured state. 675 */ setConfigured(boolean configured)676 public void setConfigured(boolean configured) { 677 if (mConfigured == configured) return; 678 mConfigured = configured; 679 updateListening(); 680 } 681 updateListening()682 public void updateListening() { 683 final Sensor sensor = mSensors[mPosture]; 684 685 if (!mConfigured || sensor == null) return; 686 if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)) { 687 if (!mRegistered) { 688 mRegistered = mSensorManager.requestTriggerSensor(this, sensor); 689 mDozeLog.traceSensorRegisterAttempt(sensor.toString(), mRegistered); 690 } else { 691 mDozeLog.traceSkipRegisterSensor(sensor.toString()); 692 } 693 } else if (mRegistered) { 694 final boolean rt = mSensorManager.cancelTriggerSensor(this, sensor); 695 mDozeLog.traceSensorUnregisterAttempt(sensor.toString(), rt); 696 mRegistered = false; 697 } 698 } 699 enabledBySetting()700 protected boolean enabledBySetting() { 701 if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId(true))) { 702 return false; 703 } else if (TextUtils.isEmpty(mSetting)) { 704 return true; 705 } 706 return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0, 707 mSelectedUserInteractor.getSelectedUserId(true)) != 0; 708 } 709 710 @Override toString()711 public String toString() { 712 StringBuilder sb = new StringBuilder(); 713 sb.append("{") 714 .append("mRegistered=").append(mRegistered) 715 .append(", mRequested=").append(mRequested) 716 .append(", mDisabled=").append(mDisabled) 717 .append(", mConfigured=").append(mConfigured) 718 .append(", mIgnoresSetting=").append(mIgnoresSetting) 719 .append(", mSensors=").append(Arrays.toString(mSensors)); 720 if (mSensors.length > 2) { 721 sb.append(", mPosture=") 722 .append(DevicePostureController.devicePostureToString(mDevicePosture)); 723 } 724 return sb.append("}").toString(); 725 } 726 727 @Override 728 @AnyThread onTrigger(TriggerEvent event)729 public void onTrigger(TriggerEvent event) { 730 final Sensor sensor = mSensors[mPosture]; 731 mDozeLog.traceSensor(mPulseReason); 732 mHandler.post(mWakeLock.wrap(() -> { 733 if (sensor != null && sensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { 734 UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP); 735 } 736 737 mRegistered = false; 738 float screenX = -1; 739 float screenY = -1; 740 if (mReportsTouchCoordinates && event.values.length >= 2) { 741 screenX = event.values[0]; 742 screenY = event.values[1]; 743 } 744 mSensorCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values); 745 if (!mRegistered && mImmediatelyReRegister) { 746 updateListening(); 747 } 748 })); 749 } 750 registerSettingsObserver(ContentObserver settingsObserver)751 public void registerSettingsObserver(ContentObserver settingsObserver) { 752 if (mConfigured && !TextUtils.isEmpty(mSetting)) { 753 mSecureSettings.registerContentObserverForUserSync( 754 mSetting, mSettingsObserver, UserHandle.USER_ALL); 755 } 756 } 757 triggerEventToString(TriggerEvent event)758 protected String triggerEventToString(TriggerEvent event) { 759 if (event == null) return null; 760 final StringBuilder sb = new StringBuilder("SensorEvent[") 761 .append(event.timestamp).append(',') 762 .append(event.sensor.getName()); 763 if (event.values != null) { 764 for (int i = 0; i < event.values.length; i++) { 765 sb.append(',').append(event.values[i]); 766 } 767 } 768 return sb.append(']').toString(); 769 } 770 } 771 772 /** 773 * A Sensor that is injected via plugin. 774 */ 775 @VisibleForTesting 776 class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener { 777 778 final SensorManagerPlugin.Sensor mPluginSensor; 779 private long mDebounce; 780 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen)781 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, 782 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) { 783 this(sensor, setting, configured, pulseReason, reportsTouchCoordinates, 784 requiresTouchscreen, 0L /* debounce */); 785 } 786 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, long debounce)787 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, 788 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, 789 long debounce) { 790 super(null, setting, configured, pulseReason, reportsTouchCoordinates, 791 requiresTouchscreen); 792 mPluginSensor = sensor; 793 mDebounce = debounce; 794 } 795 796 @Override updateListening()797 public void updateListening() { 798 if (!mConfigured) return; 799 AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager; 800 if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) 801 && !mRegistered) { 802 asyncSensorManager.registerPluginListener(mPluginSensor, this); 803 mRegistered = true; 804 mDozeLog.tracePluginSensorUpdate(true /* registered */); 805 } else if (mRegistered) { 806 asyncSensorManager.unregisterPluginListener(mPluginSensor, this); 807 mRegistered = false; 808 mDozeLog.tracePluginSensorUpdate(false /* registered */); 809 } 810 } 811 812 @Override toString()813 public String toString() { 814 return new StringBuilder("{mRegistered=").append(mRegistered) 815 .append(", mRequested=").append(mRequested) 816 .append(", mDisabled=").append(mDisabled) 817 .append(", mConfigured=").append(mConfigured) 818 .append(", mIgnoresSetting=").append(mIgnoresSetting) 819 .append(", mSensor=").append(mPluginSensor).append("}").toString(); 820 } 821 triggerEventToString(SensorManagerPlugin.SensorEvent event)822 private String triggerEventToString(SensorManagerPlugin.SensorEvent event) { 823 if (event == null) return null; 824 final StringBuilder sb = new StringBuilder("PluginTriggerEvent[") 825 .append(event.getSensor()).append(',') 826 .append(event.getVendorType()); 827 if (event.getValues() != null) { 828 for (int i = 0; i < event.getValues().length; i++) { 829 sb.append(',').append(event.getValues()[i]); 830 } 831 } 832 return sb.append(']').toString(); 833 } 834 835 @Override onSensorChanged(SensorManagerPlugin.SensorEvent event)836 public void onSensorChanged(SensorManagerPlugin.SensorEvent event) { 837 mDozeLog.traceSensor(mPulseReason); 838 mHandler.post(mWakeLock.wrap(() -> { 839 final long now = SystemClock.uptimeMillis(); 840 if (now < mDebounceFrom + mDebounce) { 841 mDozeLog.traceSensorEventDropped(mPulseReason, "debounce"); 842 return; 843 } 844 mSensorCallback.onSensorPulse(mPulseReason, -1, -1, event.getValues()); 845 })); 846 } 847 } 848 849 private final DevicePostureController.Callback mDevicePostureCallback = posture -> { 850 if (mDevicePosture == posture) { 851 return; 852 } 853 mDevicePosture = posture; 854 855 for (TriggerSensor triggerSensor : mTriggerSensors) { 856 triggerSensor.setPosture(mDevicePosture); 857 } 858 }; 859 860 private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { 861 @Override 862 public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { 863 if (modality == TYPE_FINGERPRINT) { 864 updateUdfpsEnrolled(); 865 } 866 } 867 868 @Override 869 public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { 870 if (modality == TYPE_FINGERPRINT) { 871 updateUdfpsEnrolled(); 872 } 873 } 874 875 private void updateUdfpsEnrolled() { 876 mUdfpsEnrolled = mAuthController.isUdfpsEnrolled( 877 mSelectedUserInteractor.getSelectedUserId()); 878 for (TriggerSensor sensor : mTriggerSensors) { 879 if (REASON_SENSOR_QUICK_PICKUP == sensor.mPulseReason) { 880 sensor.setConfigured(quickPickUpConfigured()); 881 } else if (REASON_SENSOR_UDFPS_LONG_PRESS == sensor.mPulseReason) { 882 sensor.setConfigured(udfpsLongPressConfigured()); 883 } 884 } 885 } 886 }; 887 888 public interface Callback { 889 890 /** 891 * Called when a sensor requests a pulse 892 * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP} 893 * @param screenX the location on the screen where the sensor fired or -1 894 * if the sensor doesn't support reporting screen locations. 895 * @param screenY the location on the screen where the sensor fired or -1 896 * @param rawValues raw values array from the event. 897 */ onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues)898 void onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues); 899 } 900 } 901