1 /* 2 * Copyright (C) 2020 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.util.sensors; 18 19 import android.content.res.Resources; 20 import android.hardware.Sensor; 21 import android.hardware.SensorEvent; 22 import android.hardware.SensorEventListener; 23 import android.hardware.SensorManager; 24 import android.text.TextUtils; 25 import android.util.Log; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.systemui.dagger.qualifiers.Main; 29 import com.android.systemui.util.concurrency.Execution; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 34 import javax.inject.Inject; 35 36 /** 37 * Sensor that will only trigger beyond some lower and upper threshold. 38 */ 39 public class ThresholdSensorImpl implements ThresholdSensor { 40 private static final String TAG = "ThresholdSensor"; 41 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 42 43 private final AsyncSensorManager mSensorManager; 44 private final Execution mExecution; 45 private final Sensor mSensor; 46 private final float mThreshold; 47 private boolean mRegistered; 48 private boolean mPaused; 49 private List<Listener> mListeners = new ArrayList<>(); 50 private Boolean mLastBelow; 51 private String mTag; 52 private final float mThresholdLatch; 53 private int mSensorDelay; 54 55 private SensorEventListener mSensorEventListener = new SensorEventListener() { 56 @Override 57 public void onSensorChanged(SensorEvent event) { 58 boolean below = event.values[0] < mThreshold; 59 boolean above = event.values[0] >= mThresholdLatch; 60 logDebug("Sensor value: " + event.values[0]); 61 onSensorEvent(below, above, event.timestamp); 62 } 63 64 @Override 65 public void onAccuracyChanged(Sensor sensor, int accuracy) { 66 } 67 }; 68 ThresholdSensorImpl(AsyncSensorManager sensorManager, Sensor sensor, Execution execution, float threshold, float thresholdLatch, int sensorDelay)69 private ThresholdSensorImpl(AsyncSensorManager sensorManager, Sensor sensor, 70 Execution execution, float threshold, float thresholdLatch, int sensorDelay) { 71 mSensorManager = sensorManager; 72 mExecution = execution; 73 mSensor = sensor; 74 mThreshold = threshold; 75 mThresholdLatch = thresholdLatch; 76 mSensorDelay = sensorDelay; 77 } 78 79 @Override setTag(String tag)80 public void setTag(String tag) { 81 mTag = tag; 82 } 83 84 @Override setDelay(int delay)85 public void setDelay(int delay) { 86 if (delay == mSensorDelay) { 87 return; 88 } 89 90 mSensorDelay = delay; 91 if (isLoaded()) { 92 unregisterInternal(); 93 registerInternal(); 94 } 95 } 96 97 @Override isLoaded()98 public boolean isLoaded() { 99 return mSensor != null; 100 } 101 102 @VisibleForTesting isRegistered()103 boolean isRegistered() { 104 return mRegistered; 105 } 106 107 /** 108 * Registers the listener with the sensor. 109 * 110 * Multiple listeners are not supported at this time. 111 * 112 * Returns true if the listener was successfully registered. False otherwise. 113 */ 114 @Override register(Listener listener)115 public void register(Listener listener) { 116 mExecution.assertIsMainThread(); 117 if (!mListeners.contains(listener)) { 118 mListeners.add(listener); 119 } 120 registerInternal(); 121 } 122 123 @Override unregister(Listener listener)124 public void unregister(Listener listener) { 125 mExecution.assertIsMainThread(); 126 mListeners.remove(listener); 127 unregisterInternal(); 128 } 129 130 /** 131 * Unregister with the {@link SensorManager} without unsetting listeners on this object. 132 */ 133 @Override pause()134 public void pause() { 135 mExecution.assertIsMainThread(); 136 mPaused = true; 137 unregisterInternal(); 138 } 139 140 /** 141 * Register with the {@link SensorManager}. No-op if no listeners are registered on this object. 142 */ 143 @Override resume()144 public void resume() { 145 mExecution.assertIsMainThread(); 146 mPaused = false; 147 registerInternal(); 148 } 149 alertListenersInternal(boolean below, long timestampNs)150 private void alertListenersInternal(boolean below, long timestampNs) { 151 List<Listener> listeners = new ArrayList<>(mListeners); 152 listeners.forEach(listener -> 153 listener.onThresholdCrossed(new ThresholdSensorEvent(below, timestampNs))); 154 } 155 registerInternal()156 private void registerInternal() { 157 mExecution.assertIsMainThread(); 158 if (mRegistered || mPaused || mListeners.isEmpty()) { 159 return; 160 } 161 logDebug("Registering sensor listener"); 162 mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay); 163 mRegistered = true; 164 } 165 unregisterInternal()166 private void unregisterInternal() { 167 mExecution.assertIsMainThread(); 168 if (!mRegistered) { 169 return; 170 } 171 logDebug("Unregister sensor listener"); 172 mSensorManager.unregisterListener(mSensorEventListener); 173 mRegistered = false; 174 mLastBelow = null; // Forget what we know. 175 } 176 177 /** 178 * Call when the sensor reports a new value. 179 * 180 * Separate below-threshold and above-thresholds are specified. this allows latching behavior, 181 * where a different threshold can be specified for triggering the sensor depending on if it's 182 * going from above to below or below to above. To outside listeners of this class, the class 183 * still appears entirely binary. 184 */ onSensorEvent(boolean belowThreshold, boolean aboveThreshold, long timestampNs)185 private void onSensorEvent(boolean belowThreshold, boolean aboveThreshold, long timestampNs) { 186 mExecution.assertIsMainThread(); 187 if (!mRegistered) { 188 return; 189 } 190 if (mLastBelow != null) { 191 // If we last reported below and are not yet above, change nothing. 192 if (mLastBelow && !aboveThreshold) { 193 return; 194 } 195 // If we last reported above and are not yet below, change nothing. 196 if (!mLastBelow && !belowThreshold) { 197 return; 198 } 199 } 200 mLastBelow = belowThreshold; 201 logDebug("Alerting below: " + belowThreshold); 202 alertListenersInternal(belowThreshold, timestampNs); 203 } 204 205 @Override getName()206 public String getName() { 207 return mSensor != null ? mSensor.getName() : null; 208 } 209 210 @Override getType()211 public String getType() { 212 return mSensor != null ? mSensor.getStringType() : null; 213 } 214 215 @Override toString()216 public String toString() { 217 return String.format("{isLoaded=%s, registered=%s, paused=%s, threshold=%s, sensor=%s}", 218 isLoaded(), mRegistered, mPaused, mThreshold, mSensor); 219 } 220 logDebug(String msg)221 private void logDebug(String msg) { 222 if (DEBUG) { 223 Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg); 224 } 225 } 226 227 /** 228 * Use to build a ThresholdSensor. Should only be used once per sensor built, since 229 * parameters are not reset after calls to build(). For ease of retrievingnew Builders, use 230 * {@link BuilderFactory}. 231 */ 232 public static class Builder { 233 private final Resources mResources; 234 private final AsyncSensorManager mSensorManager; 235 private final Execution mExecution; 236 private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;; 237 private float mThresholdValue; 238 private float mThresholdLatchValue; 239 private Sensor mSensor; 240 private boolean mSensorSet; 241 private boolean mThresholdSet; 242 private boolean mThresholdLatchValueSet; 243 244 @Inject Builder(@ain Resources resources, AsyncSensorManager sensorManager, Execution execution)245 Builder(@Main Resources resources, AsyncSensorManager sensorManager, Execution execution) { 246 mResources = resources; 247 mSensorManager = sensorManager; 248 mExecution = execution; 249 } 250 setSensorDelay(int sensorDelay)251 Builder setSensorDelay(int sensorDelay) { 252 mSensorDelay = sensorDelay; 253 return this; 254 } 255 /** 256 * If requiresWakeUp is false, the first sensor with sensorType (regardless of whether the 257 * sensor is a wakeup sensor or not) will be set. 258 */ setSensorResourceId(int sensorResourceId, boolean requireWakeUp)259 Builder setSensorResourceId(int sensorResourceId, boolean requireWakeUp) { 260 setSensorType(mResources.getString(sensorResourceId), requireWakeUp); 261 return this; 262 } 263 setThresholdResourceId(int thresholdResourceId)264 Builder setThresholdResourceId(int thresholdResourceId) { 265 try { 266 setThresholdValue(mResources.getFloat(thresholdResourceId)); 267 } catch (Resources.NotFoundException e) { 268 // no-op 269 } 270 return this; 271 } 272 setThresholdLatchResourceId(int thresholdLatchResourceId)273 Builder setThresholdLatchResourceId(int thresholdLatchResourceId) { 274 try { 275 setThresholdLatchValue(mResources.getFloat(thresholdLatchResourceId)); 276 } catch (Resources.NotFoundException e) { 277 // no-op 278 } 279 return this; 280 } 281 282 /** 283 * If requiresWakeUp is false, the first sensor with sensorType (regardless of whether the 284 * sensor is a wakeup sensor or not) will be set. 285 */ setSensorType(String sensorType, boolean requireWakeUp)286 Builder setSensorType(String sensorType, boolean requireWakeUp) { 287 Sensor sensor = findSensorByType(sensorType, requireWakeUp); 288 if (sensor != null) { 289 setSensor(sensor); 290 } 291 return this; 292 } 293 setThresholdValue(float thresholdValue)294 Builder setThresholdValue(float thresholdValue) { 295 mThresholdValue = thresholdValue; 296 mThresholdSet = true; 297 if (!mThresholdLatchValueSet) { 298 mThresholdLatchValue = mThresholdValue; 299 } 300 return this; 301 } 302 setThresholdLatchValue(float thresholdLatchValue)303 Builder setThresholdLatchValue(float thresholdLatchValue) { 304 mThresholdLatchValue = thresholdLatchValue; 305 mThresholdLatchValueSet = true; 306 return this; 307 } 308 setSensor(Sensor sensor)309 Builder setSensor(Sensor sensor) { 310 mSensor = sensor; 311 mSensorSet = true; 312 return this; 313 } 314 315 /** 316 * Creates a {@link ThresholdSensor} backed by a {@link ThresholdSensorImpl}. 317 */ build()318 public ThresholdSensor build() { 319 if (!mSensorSet) { 320 throw new IllegalStateException("A sensor was not successfully set."); 321 } 322 323 if (!mThresholdSet) { 324 throw new IllegalStateException("A threshold was not successfully set."); 325 } 326 327 if (mThresholdValue > mThresholdLatchValue) { 328 throw new IllegalStateException( 329 "Threshold must be less than or equal to Threshold Latch"); 330 } 331 332 return new ThresholdSensorImpl( 333 mSensorManager, mSensor, mExecution, 334 mThresholdValue, mThresholdLatchValue, mSensorDelay); 335 } 336 337 @VisibleForTesting findSensorByType(String sensorType, boolean requireWakeUp)338 Sensor findSensorByType(String sensorType, boolean requireWakeUp) { 339 if (TextUtils.isEmpty(sensorType)) { 340 return null; 341 } 342 343 List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); 344 Sensor sensor = null; 345 for (Sensor s : sensorList) { 346 if (sensorType.equals(s.getStringType())) { 347 sensor = s; 348 if (!requireWakeUp || sensor.isWakeUpSensor()) { 349 break; 350 } 351 } 352 } 353 354 return sensor; 355 } 356 } 357 358 /** 359 * Factory that creates a new ThresholdSensorImpl.Builder. In general, Builders should not be 360 * reused after creating a ThresholdSensor or else there may be default threshold and sensor 361 * values set from the previous built sensor. 362 */ 363 public static class BuilderFactory { 364 private final Resources mResources; 365 private final AsyncSensorManager mSensorManager; 366 private final Execution mExecution; 367 368 @Inject BuilderFactory( @ain Resources resources, AsyncSensorManager sensorManager, Execution execution)369 BuilderFactory( 370 @Main Resources resources, 371 AsyncSensorManager sensorManager, 372 Execution execution) { 373 mResources = resources; 374 mSensorManager = sensorManager; 375 mExecution = execution; 376 } 377 createBuilder()378 ThresholdSensorImpl.Builder createBuilder() { 379 return new Builder(mResources, mSensorManager, mExecution); 380 } 381 } 382 } 383