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