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