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