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