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.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY; 20 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; 21 22 import android.annotation.AnyThread; 23 import android.app.ActivityManager; 24 import android.app.AlarmManager; 25 import android.content.ContentResolver; 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.Log; 40 import android.view.Display; 41 42 import androidx.annotation.VisibleForTesting; 43 44 import com.android.internal.logging.MetricsLogger; 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.internal.logging.nano.MetricsProto; 49 import com.android.internal.util.IndentingPrintWriter; 50 import com.android.systemui.plugins.SensorManagerPlugin; 51 import com.android.systemui.statusbar.phone.DozeParameters; 52 import com.android.systemui.util.sensors.AsyncSensorManager; 53 import com.android.systemui.util.sensors.ProximitySensor; 54 import com.android.systemui.util.wakelock.WakeLock; 55 56 import java.io.PrintWriter; 57 import java.util.Collection; 58 import java.util.List; 59 import java.util.function.Consumer; 60 61 public class DozeSensors { 62 63 private static final boolean DEBUG = DozeService.DEBUG; 64 private static final String TAG = "DozeSensors"; 65 private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); 66 67 private final Context mContext; 68 private final AlarmManager mAlarmManager; 69 private final AsyncSensorManager mSensorManager; 70 private final ContentResolver mResolver; 71 private final DozeParameters mDozeParameters; 72 private final AmbientDisplayConfiguration mConfig; 73 private final WakeLock mWakeLock; 74 private final Consumer<Boolean> mProxCallback; 75 private final Callback mCallback; 76 @VisibleForTesting 77 protected TriggerSensor[] mSensors; 78 79 private final Handler mHandler = new Handler(); 80 private final ProximitySensor mProximitySensor; 81 private long mDebounceFrom; 82 private boolean mSettingRegistered; 83 private boolean mListening; 84 private boolean mListeningTouchScreenSensors; 85 86 @VisibleForTesting 87 public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum { 88 @UiEvent(doc = "User performs pickup gesture that activates the ambient display") 89 ACTION_AMBIENT_GESTURE_PICKUP(459); 90 91 private final int mId; 92 DozeSensorsUiEvent(int id)93 DozeSensorsUiEvent(int id) { 94 mId = id; 95 } 96 97 @Override getId()98 public int getId() { 99 return mId; 100 } 101 } 102 DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog, ProximitySensor proximitySensor)103 public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, 104 DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, 105 Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog, 106 ProximitySensor proximitySensor) { 107 mContext = context; 108 mAlarmManager = alarmManager; 109 mSensorManager = sensorManager; 110 mDozeParameters = dozeParameters; 111 mConfig = config; 112 mWakeLock = wakeLock; 113 mProxCallback = proxCallback; 114 mResolver = mContext.getContentResolver(); 115 mCallback = callback; 116 mProximitySensor = proximitySensor; 117 118 boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT); 119 mSensors = new TriggerSensor[] { 120 new TriggerSensor( 121 mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), 122 null /* setting */, 123 dozeParameters.getPulseOnSigMotion(), 124 DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */, 125 false /* touchscreen */, dozeLog), 126 new TriggerSensor( 127 mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), 128 Settings.Secure.DOZE_PICK_UP_GESTURE, 129 true /* settingDef */, 130 config.dozePickupSensorAvailable(), 131 DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */, 132 false /* touchscreen */, 133 false /* ignoresSetting */, 134 dozeLog), 135 new TriggerSensor( 136 findSensorWithType(config.doubleTapSensorType()), 137 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 138 true /* configured */, 139 DozeLog.REASON_SENSOR_DOUBLE_TAP, 140 dozeParameters.doubleTapReportsTouchCoordinates(), 141 true /* touchscreen */, 142 dozeLog), 143 new TriggerSensor( 144 findSensorWithType(config.tapSensorType()), 145 Settings.Secure.DOZE_TAP_SCREEN_GESTURE, 146 true /* configured */, 147 DozeLog.REASON_SENSOR_TAP, 148 false /* reports touch coordinates */, 149 true /* touchscreen */, 150 dozeLog), 151 new TriggerSensor( 152 findSensorWithType(config.longPressSensorType()), 153 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, 154 false /* settingDef */, 155 true /* configured */, 156 DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 157 true /* reports touch coordinates */, 158 true /* touchscreen */, 159 dozeLog), 160 new PluginSensor( 161 new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), 162 Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, 163 mConfig.wakeScreenGestureAvailable() && alwaysOn, 164 DozeLog.REASON_SENSOR_WAKE_UP, 165 false /* reports touch coordinates */, 166 false /* touchscreen */, 167 dozeLog), 168 new PluginSensor( 169 new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), 170 Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, 171 mConfig.wakeScreenGestureAvailable(), 172 DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 173 false /* reports touch coordinates */, 174 false /* touchscreen */, 175 mConfig.getWakeLockScreenDebounce(), 176 dozeLog), 177 }; 178 179 setProxListening(false); // Don't immediately start listening when we register. 180 mProximitySensor.register( 181 proximityEvent -> { 182 if (proximityEvent != null) { 183 mProxCallback.accept(!proximityEvent.getBelow()); 184 } 185 }); 186 } 187 188 /** 189 * Unregister any sensors. 190 */ destroy()191 public void destroy() { 192 // Unregisters everything, which is enough to allow gc. 193 for (TriggerSensor triggerSensor : mSensors) { 194 triggerSensor.setListening(false); 195 } 196 mProximitySensor.pause(); 197 } 198 199 /** 200 * Temporarily disable some sensors to avoid turning on the device while the user is 201 * turning it off. 202 */ requestTemporaryDisable()203 public void requestTemporaryDisable() { 204 mDebounceFrom = SystemClock.uptimeMillis(); 205 } 206 findSensorWithType(String type)207 private Sensor findSensorWithType(String type) { 208 return findSensorWithType(mSensorManager, type); 209 } 210 findSensorWithType(SensorManager sensorManager, String type)211 static Sensor findSensorWithType(SensorManager sensorManager, String type) { 212 if (TextUtils.isEmpty(type)) { 213 return null; 214 } 215 List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL); 216 for (Sensor s : sensorList) { 217 if (type.equals(s.getStringType())) { 218 return s; 219 } 220 } 221 return null; 222 } 223 224 /** 225 * If sensors should be registered and sending signals. 226 */ setListening(boolean listen, boolean includeTouchScreenSensors)227 public void setListening(boolean listen, boolean includeTouchScreenSensors) { 228 if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) { 229 return; 230 } 231 mListening = listen; 232 mListeningTouchScreenSensors = includeTouchScreenSensors; 233 updateListening(); 234 } 235 236 /** 237 * Registers/unregisters sensors based on internal state. 238 */ updateListening()239 private void updateListening() { 240 boolean anyListening = false; 241 for (TriggerSensor s : mSensors) { 242 boolean listen = mListening 243 && (!s.mRequiresTouchscreen || mListeningTouchScreenSensors); 244 s.setListening(listen); 245 if (listen) { 246 anyListening = true; 247 } 248 } 249 250 if (!anyListening) { 251 mResolver.unregisterContentObserver(mSettingsObserver); 252 } else if (!mSettingRegistered) { 253 for (TriggerSensor s : mSensors) { 254 s.registerSettingsObserver(mSettingsObserver); 255 } 256 } 257 mSettingRegistered = anyListening; 258 } 259 260 /** Set the listening state of only the sensors that require the touchscreen. */ setTouchscreenSensorsListening(boolean listening)261 public void setTouchscreenSensorsListening(boolean listening) { 262 for (TriggerSensor sensor : mSensors) { 263 if (sensor.mRequiresTouchscreen) { 264 sensor.setListening(listening); 265 } 266 } 267 } 268 onUserSwitched()269 public void onUserSwitched() { 270 for (TriggerSensor s : mSensors) { 271 s.updateListening(); 272 } 273 } 274 onScreenState(int state)275 void onScreenState(int state) { 276 mProximitySensor.setSecondarySafe( 277 state == Display.STATE_DOZE 278 || state == Display.STATE_DOZE_SUSPEND 279 || state == Display.STATE_OFF); 280 } 281 setProxListening(boolean listen)282 public void setProxListening(boolean listen) { 283 if (mProximitySensor.isRegistered() && listen) { 284 mProximitySensor.alertListeners(); 285 } else { 286 if (listen) { 287 mProximitySensor.resume(); 288 } else { 289 mProximitySensor.pause(); 290 } 291 } 292 } 293 294 private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 295 @Override 296 public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { 297 if (userId != ActivityManager.getCurrentUser()) { 298 return; 299 } 300 for (TriggerSensor s : mSensors) { 301 s.updateListening(); 302 } 303 } 304 }; 305 306 /** Ignore the setting value of only the sensors that require the touchscreen. */ ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore)307 public void ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore) { 308 for (TriggerSensor sensor : mSensors) { 309 if (sensor.mRequiresTouchscreen) { 310 sensor.ignoreSetting(ignore); 311 } 312 } 313 } 314 315 /** Dump current state */ dump(PrintWriter pw)316 public void dump(PrintWriter pw) { 317 pw.println("mListening=" + mListening); 318 pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors); 319 IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); 320 idpw.increaseIndent(); 321 for (TriggerSensor s : mSensors) { 322 idpw.println("Sensor: " + s.toString()); 323 } 324 idpw.println("ProxSensor: " + mProximitySensor.toString()); 325 } 326 327 /** 328 * @return true if prox is currently near, false if far or null if unknown. 329 */ isProximityCurrentlyNear()330 public Boolean isProximityCurrentlyNear() { 331 return mProximitySensor.isNear(); 332 } 333 334 @VisibleForTesting 335 class TriggerSensor extends TriggerEventListener { 336 final Sensor mSensor; 337 final boolean mConfigured; 338 final int mPulseReason; 339 private final String mSetting; 340 private final boolean mReportsTouchCoordinates; 341 private final boolean mSettingDefault; 342 private final boolean mRequiresTouchscreen; 343 344 protected boolean mRequested; 345 protected boolean mRegistered; 346 protected boolean mDisabled; 347 protected boolean mIgnoresSetting; 348 protected final DozeLog mDozeLog; 349 TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog)350 public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason, 351 boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog) { 352 this(sensor, setting, true /* settingDef */, configured, pulseReason, 353 reportsTouchCoordinates, requiresTouchscreen, dozeLog); 354 } 355 TriggerSensor(Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog)356 public TriggerSensor(Sensor sensor, String setting, boolean settingDef, 357 boolean configured, int pulseReason, boolean reportsTouchCoordinates, 358 boolean requiresTouchscreen, DozeLog dozeLog) { 359 this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates, 360 requiresTouchscreen, false /* ignoresSetting */, dozeLog); 361 } 362 TriggerSensor(Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, boolean ignoresSetting, DozeLog dozeLog)363 private TriggerSensor(Sensor sensor, String setting, boolean settingDef, 364 boolean configured, int pulseReason, boolean reportsTouchCoordinates, 365 boolean requiresTouchscreen, boolean ignoresSetting, DozeLog dozeLog) { 366 mSensor = sensor; 367 mSetting = setting; 368 mSettingDefault = settingDef; 369 mConfigured = configured; 370 mPulseReason = pulseReason; 371 mReportsTouchCoordinates = reportsTouchCoordinates; 372 mRequiresTouchscreen = requiresTouchscreen; 373 mIgnoresSetting = ignoresSetting; 374 mDozeLog = dozeLog; 375 } 376 setListening(boolean listen)377 public void setListening(boolean listen) { 378 if (mRequested == listen) return; 379 mRequested = listen; 380 updateListening(); 381 } 382 setDisabled(boolean disabled)383 public void setDisabled(boolean disabled) { 384 if (mDisabled == disabled) return; 385 mDisabled = disabled; 386 updateListening(); 387 } 388 ignoreSetting(boolean ignored)389 public void ignoreSetting(boolean ignored) { 390 if (mIgnoresSetting == ignored) return; 391 mIgnoresSetting = ignored; 392 updateListening(); 393 } 394 updateListening()395 public void updateListening() { 396 if (!mConfigured || mSensor == null) return; 397 if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) 398 && !mRegistered) { 399 mRegistered = mSensorManager.requestTriggerSensor(this, mSensor); 400 if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered); 401 } else if (mRegistered) { 402 final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor); 403 if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt); 404 mRegistered = false; 405 } 406 } 407 enabledBySetting()408 protected boolean enabledBySetting() { 409 if (!mConfig.enabled(UserHandle.USER_CURRENT)) { 410 return false; 411 } else if (TextUtils.isEmpty(mSetting)) { 412 return true; 413 } 414 return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0, 415 UserHandle.USER_CURRENT) != 0; 416 } 417 418 @Override toString()419 public String toString() { 420 return new StringBuilder("{mRegistered=").append(mRegistered) 421 .append(", mRequested=").append(mRequested) 422 .append(", mDisabled=").append(mDisabled) 423 .append(", mConfigured=").append(mConfigured) 424 .append(", mIgnoresSetting=").append(mIgnoresSetting) 425 .append(", mSensor=").append(mSensor).append("}").toString(); 426 } 427 428 @Override 429 @AnyThread onTrigger(TriggerEvent event)430 public void onTrigger(TriggerEvent event) { 431 mDozeLog.traceSensor(mPulseReason); 432 mHandler.post(mWakeLock.wrap(() -> { 433 if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event)); 434 if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { 435 int subType = (int) event.values[0]; 436 MetricsLogger.action( 437 mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE, 438 subType); 439 UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP); 440 } 441 442 mRegistered = false; 443 float screenX = -1; 444 float screenY = -1; 445 if (mReportsTouchCoordinates && event.values.length >= 2) { 446 screenX = event.values[0]; 447 screenY = event.values[1]; 448 } 449 mCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values); 450 if (!mRegistered) { 451 updateListening(); // reregister, this sensor only fires once 452 } 453 })); 454 } 455 registerSettingsObserver(ContentObserver settingsObserver)456 public void registerSettingsObserver(ContentObserver settingsObserver) { 457 if (mConfigured && !TextUtils.isEmpty(mSetting)) { 458 mResolver.registerContentObserver( 459 Settings.Secure.getUriFor(mSetting), false /* descendants */, 460 mSettingsObserver, UserHandle.USER_ALL); 461 } 462 } 463 triggerEventToString(TriggerEvent event)464 protected String triggerEventToString(TriggerEvent event) { 465 if (event == null) return null; 466 final StringBuilder sb = new StringBuilder("SensorEvent[") 467 .append(event.timestamp).append(',') 468 .append(event.sensor.getName()); 469 if (event.values != null) { 470 for (int i = 0; i < event.values.length; i++) { 471 sb.append(',').append(event.values[i]); 472 } 473 } 474 return sb.append(']').toString(); 475 } 476 } 477 478 /** 479 * A Sensor that is injected via plugin. 480 */ 481 @VisibleForTesting 482 class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener { 483 484 final SensorManagerPlugin.Sensor mPluginSensor; 485 private long mDebounce; 486 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog)487 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, 488 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, 489 DozeLog dozeLog) { 490 this(sensor, setting, configured, pulseReason, reportsTouchCoordinates, 491 requiresTouchscreen, 0L /* debounce */, dozeLog); 492 } 493 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, long debounce, DozeLog dozeLog)494 PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, 495 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, 496 long debounce, DozeLog dozeLog) { 497 super(null, setting, configured, pulseReason, reportsTouchCoordinates, 498 requiresTouchscreen, dozeLog); 499 mPluginSensor = sensor; 500 mDebounce = debounce; 501 } 502 503 @Override updateListening()504 public void updateListening() { 505 if (!mConfigured) return; 506 AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager; 507 if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) 508 && !mRegistered) { 509 asyncSensorManager.registerPluginListener(mPluginSensor, this); 510 mRegistered = true; 511 if (DEBUG) Log.d(TAG, "registerPluginListener"); 512 } else if (mRegistered) { 513 asyncSensorManager.unregisterPluginListener(mPluginSensor, this); 514 mRegistered = false; 515 if (DEBUG) Log.d(TAG, "unregisterPluginListener"); 516 } 517 } 518 519 @Override toString()520 public String toString() { 521 return new StringBuilder("{mRegistered=").append(mRegistered) 522 .append(", mRequested=").append(mRequested) 523 .append(", mDisabled=").append(mDisabled) 524 .append(", mConfigured=").append(mConfigured) 525 .append(", mIgnoresSetting=").append(mIgnoresSetting) 526 .append(", mSensor=").append(mPluginSensor).append("}").toString(); 527 } 528 triggerEventToString(SensorManagerPlugin.SensorEvent event)529 private String triggerEventToString(SensorManagerPlugin.SensorEvent event) { 530 if (event == null) return null; 531 final StringBuilder sb = new StringBuilder("PluginTriggerEvent[") 532 .append(event.getSensor()).append(',') 533 .append(event.getVendorType()); 534 if (event.getValues() != null) { 535 for (int i = 0; i < event.getValues().length; i++) { 536 sb.append(',').append(event.getValues()[i]); 537 } 538 } 539 return sb.append(']').toString(); 540 } 541 542 @Override onSensorChanged(SensorManagerPlugin.SensorEvent event)543 public void onSensorChanged(SensorManagerPlugin.SensorEvent event) { 544 mDozeLog.traceSensor(mPulseReason); 545 mHandler.post(mWakeLock.wrap(() -> { 546 final long now = SystemClock.uptimeMillis(); 547 if (now < mDebounceFrom + mDebounce) { 548 Log.d(TAG, "onSensorEvent dropped: " + triggerEventToString(event)); 549 return; 550 } 551 if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event)); 552 mCallback.onSensorPulse(mPulseReason, -1, -1, event.getValues()); 553 })); 554 } 555 } 556 557 public interface Callback { 558 559 /** 560 * Called when a sensor requests a pulse 561 * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP} 562 * @param screenX the location on the screen where the sensor fired or -1 563 * if the sensor doesn't support reporting screen locations. 564 * @param screenY the location on the screen where the sensor fired or -1 565 * @param rawValues raw values array from the event. 566 */ onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues)567 void onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues); 568 } 569 } 570