DozeSensors.java revision 67cca7442bfeffc0bace506a796cfdd39e5d511c
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 17package com.android.systemui.doze; 18 19import android.annotation.AnyThread; 20import android.app.ActivityManager; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.database.ContentObserver; 24import android.hardware.Sensor; 25import android.hardware.SensorEvent; 26import android.hardware.SensorEventListener; 27import android.hardware.SensorManager; 28import android.hardware.TriggerEvent; 29import android.hardware.TriggerEventListener; 30import android.net.Uri; 31import android.os.Handler; 32import android.os.UserHandle; 33import android.provider.Settings; 34import android.text.TextUtils; 35import android.util.Log; 36 37import com.android.internal.hardware.AmbientDisplayConfiguration; 38import com.android.internal.logging.MetricsLogger; 39import com.android.internal.logging.nano.MetricsProto; 40import com.android.systemui.statusbar.phone.DozeParameters; 41import com.android.systemui.util.wakelock.WakeLock; 42 43import java.io.PrintWriter; 44import java.util.List; 45import java.util.function.Consumer; 46 47public class DozeSensors { 48 49 private static final boolean DEBUG = DozeService.DEBUG; 50 51 private static final String TAG = "DozeSensors"; 52 53 private final Context mContext; 54 private final SensorManager mSensorManager; 55 private final TriggerSensor[] mSensors; 56 private final ContentResolver mResolver; 57 private final TriggerSensor mPickupSensor; 58 private final DozeParameters mDozeParameters; 59 private final AmbientDisplayConfiguration mConfig; 60 private final WakeLock mWakeLock; 61 private final Consumer<Boolean> mProxCallback; 62 private final Callback mCallback; 63 64 private final Handler mHandler = new Handler(); 65 private final ProxSensor mProxSensor; 66 67 68 public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters, 69 AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, 70 Consumer<Boolean> proxCallback) { 71 mContext = context; 72 mSensorManager = sensorManager; 73 mDozeParameters = dozeParameters; 74 mConfig = config; 75 mWakeLock = wakeLock; 76 mProxCallback = proxCallback; 77 mResolver = mContext.getContentResolver(); 78 79 mSensors = new TriggerSensor[] { 80 new TriggerSensor( 81 mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), 82 null /* setting */, 83 dozeParameters.getPulseOnSigMotion(), 84 DozeLog.PULSE_REASON_SENSOR_SIGMOTION), 85 mPickupSensor = new TriggerSensor( 86 mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), 87 Settings.Secure.DOZE_PULSE_ON_PICK_UP, 88 config.pulseOnPickupAvailable(), 89 DozeLog.PULSE_REASON_SENSOR_PICKUP), 90 new TriggerSensor( 91 findSensorWithType(config.doubleTapSensorType()), 92 Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, 93 true /* configured */, 94 DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP) 95 }; 96 97 mProxSensor = new ProxSensor(); 98 mCallback = callback; 99 } 100 101 private Sensor findSensorWithType(String type) { 102 if (TextUtils.isEmpty(type)) { 103 return null; 104 } 105 List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); 106 for (Sensor s : sensorList) { 107 if (type.equals(s.getStringType())) { 108 return s; 109 } 110 } 111 return null; 112 } 113 114 public void setListening(boolean listen) { 115 for (TriggerSensor s : mSensors) { 116 s.setListening(listen); 117 if (listen) { 118 s.registerSettingsObserver(mSettingsObserver); 119 } 120 } 121 if (!listen) { 122 mResolver.unregisterContentObserver(mSettingsObserver); 123 } 124 } 125 126 public void reregisterAllSensors() { 127 for (TriggerSensor s : mSensors) { 128 s.setListening(false); 129 } 130 for (TriggerSensor s : mSensors) { 131 s.setListening(true); 132 } 133 } 134 135 public void onUserSwitched() { 136 for (TriggerSensor s : mSensors) { 137 s.updateListener(); 138 } 139 } 140 141 public void setProxListening(boolean listen) { 142 mProxSensor.setRegistered(listen); 143 } 144 145 private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 146 @Override 147 public void onChange(boolean selfChange, Uri uri, int userId) { 148 if (userId != ActivityManager.getCurrentUser()) { 149 return; 150 } 151 for (TriggerSensor s : mSensors) { 152 s.updateListener(); 153 } 154 } 155 }; 156 157 public void setDisableSensorsInterferingWithProximity(boolean disable) { 158 mPickupSensor.setDisabled(disable); 159 } 160 161 /** Dump current state */ 162 public void dump(PrintWriter pw) { 163 for (TriggerSensor s : mSensors) { 164 pw.print("Sensor: "); pw.println(s.toString()); 165 } 166 } 167 168 private class ProxSensor implements SensorEventListener { 169 170 boolean mRegistered; 171 Boolean mCurrentlyFar; 172 173 void setRegistered(boolean register) { 174 if (mRegistered == register) { 175 // Send an update even if we don't re-register. 176 mHandler.post(() -> { 177 if (mCurrentlyFar != null) { 178 mProxCallback.accept(mCurrentlyFar); 179 } 180 }); 181 return; 182 } 183 if (register) { 184 mRegistered = mSensorManager.registerListener(this, 185 mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY), 186 SensorManager.SENSOR_DELAY_NORMAL, mHandler); 187 } else { 188 mSensorManager.unregisterListener(this); 189 mRegistered = false; 190 mCurrentlyFar = null; 191 } 192 } 193 194 @Override 195 public void onSensorChanged(SensorEvent event) { 196 mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange(); 197 mProxCallback.accept(mCurrentlyFar); 198 } 199 200 @Override 201 public void onAccuracyChanged(Sensor sensor, int accuracy) { 202 } 203 } 204 205 private class TriggerSensor extends TriggerEventListener { 206 final Sensor mSensor; 207 final boolean mConfigured; 208 final int mPulseReason; 209 final String mSetting; 210 211 private boolean mRequested; 212 private boolean mRegistered; 213 private boolean mDisabled; 214 215 public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason) { 216 mSensor = sensor; 217 mSetting = setting; 218 mConfigured = configured; 219 mPulseReason = pulseReason; 220 } 221 222 public void setListening(boolean listen) { 223 if (mRequested == listen) return; 224 mRequested = listen; 225 updateListener(); 226 } 227 228 public void setDisabled(boolean disabled) { 229 if (mDisabled == disabled) return; 230 mDisabled = disabled; 231 updateListener(); 232 } 233 234 public void updateListener() { 235 if (!mConfigured || mSensor == null) return; 236 if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) { 237 mRegistered = mSensorManager.requestTriggerSensor(this, mSensor); 238 if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered); 239 } else if (mRegistered) { 240 final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor); 241 if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt); 242 mRegistered = false; 243 } 244 } 245 246 private boolean enabledBySetting() { 247 if (TextUtils.isEmpty(mSetting)) { 248 return true; 249 } 250 return Settings.Secure.getIntForUser(mResolver, mSetting, 1, 251 UserHandle.USER_CURRENT) != 0; 252 } 253 254 @Override 255 public String toString() { 256 return new StringBuilder("{mRegistered=").append(mRegistered) 257 .append(", mRequested=").append(mRequested) 258 .append(", mDisabled=").append(mDisabled) 259 .append(", mConfigured=").append(mConfigured) 260 .append(", mSensor=").append(mSensor).append("}").toString(); 261 } 262 263 @Override 264 @AnyThread 265 public void onTrigger(TriggerEvent event) { 266 mHandler.post(mWakeLock.wrap(() -> { 267 if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event)); 268 boolean sensorPerformsProxCheck = false; 269 if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { 270 int subType = (int) event.values[0]; 271 MetricsLogger.action( 272 mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE, 273 subType); 274 sensorPerformsProxCheck = 275 mDozeParameters.getPickupSubtypePerformsProxCheck(subType); 276 } 277 278 mRegistered = false; 279 mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck); 280 updateListener(); // reregister, this sensor only fires once 281 })); 282 } 283 284 public void registerSettingsObserver(ContentObserver settingsObserver) { 285 if (mConfigured && !TextUtils.isEmpty(mSetting)) { 286 mResolver.registerContentObserver( 287 Settings.Secure.getUriFor(mSetting), false /* descendants */, 288 mSettingsObserver, UserHandle.USER_ALL); 289 } 290 } 291 292 private String triggerEventToString(TriggerEvent event) { 293 if (event == null) return null; 294 final StringBuilder sb = new StringBuilder("TriggerEvent[") 295 .append(event.timestamp).append(',') 296 .append(event.sensor.getName()); 297 if (event.values != null) { 298 for (int i = 0; i < event.values.length; i++) { 299 sb.append(',').append(event.values[i]); 300 } 301 } 302 return sb.append(']').toString(); 303 } 304 } 305 306 public interface Callback { 307 308 /** 309 * Called when a sensor requests a pulse 310 * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP} 311 * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity. 312 */ 313 void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck); 314 } 315} 316