blob: 52d46476df835a6566f3bc33133abe18909eae94 [file] [log] [blame]
Dave Mankoff63a12822019-09-16 14:38:06 -04001/*
2 * Copyright (C) 2019 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.util.sensors;
18
Dave Mankoffd94c9692019-10-18 12:38:21 -040019import android.content.res.Resources;
Dave Mankoff63a12822019-09-16 14:38:06 -040020import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
Dave Mankoff63a12822019-09-16 14:38:06 -040024import android.util.Log;
25
Dave Mankoffbf52f4b2019-09-20 14:34:28 -040026import com.android.internal.annotations.VisibleForTesting;
Dave Mankoff63a12822019-09-16 14:38:06 -040027import com.android.systemui.R;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050028import com.android.systemui.dagger.qualifiers.Main;
Dave Mankoffdddcae92020-05-13 12:46:18 -040029import com.android.systemui.util.concurrency.DelayableExecutor;
Dave Mankoff63a12822019-09-16 14:38:06 -040030
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Locale;
Dave Mankoffdddcae92020-05-13 12:46:18 -040034import java.util.concurrent.atomic.AtomicBoolean;
Dave Mankoff63a12822019-09-16 14:38:06 -040035import java.util.function.Consumer;
36
37import javax.inject.Inject;
38
39/**
40 * Simple wrapper around SensorManager customized for the Proximity sensor.
41 */
Dave Mankoffbc3091c2019-09-18 11:44:29 -040042public class ProximitySensor {
Dave Mankoff63a12822019-09-16 14:38:06 -040043 private static final String TAG = "ProxSensor";
Dave Mankoff6e636792020-02-19 10:22:21 -050044 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Dave Mankoff63a12822019-09-16 14:38:06 -040045
46 private final Sensor mSensor;
47 private final AsyncSensorManager mSensorManager;
Dave Mankoff55d5d8c2020-02-13 16:15:39 -050048 private final float mThreshold;
Dave Mankoff63a12822019-09-16 14:38:06 -040049 private List<ProximitySensorListener> mListeners = new ArrayList<>();
50 private String mTag = null;
Dave Mankoffbf52f4b2019-09-20 14:34:28 -040051 @VisibleForTesting ProximityEvent mLastEvent;
Dave Mankoff63a12822019-09-16 14:38:06 -040052 private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
Dave Mankoffdddcae92020-05-13 12:46:18 -040053 @VisibleForTesting protected boolean mPaused;
Dave Mankoff63a12822019-09-16 14:38:06 -040054 private boolean mRegistered;
Dave Mankoffdddcae92020-05-13 12:46:18 -040055 private final AtomicBoolean mAlerting = new AtomicBoolean();
Dave Mankoff63a12822019-09-16 14:38:06 -040056
57 private SensorEventListener mSensorEventListener = new SensorEventListener() {
58 @Override
59 public synchronized void onSensorChanged(SensorEvent event) {
60 onSensorEvent(event);
61 }
62
63 @Override
64 public void onAccuracyChanged(Sensor sensor, int accuracy) {
65 }
66 };
67
68 @Inject
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050069 public ProximitySensor(@Main Resources resources,
Dave Mankoffd94c9692019-10-18 12:38:21 -040070 AsyncSensorManager sensorManager) {
Dave Mankoff63a12822019-09-16 14:38:06 -040071 mSensorManager = sensorManager;
Dave Mankoff63a12822019-09-16 14:38:06 -040072
Dave Mankoff55d5d8c2020-02-13 16:15:39 -050073 Sensor sensor = findCustomProxSensor(resources);
74 float threshold = 0;
75 if (sensor != null) {
76 try {
77 threshold = getCustomProxThreshold(resources);
78 } catch (IllegalStateException e) {
79 Log.e(TAG, "Can not load custom proximity sensor.", e);
80 sensor = null;
81 }
82 }
Dave Mankoff63a12822019-09-16 14:38:06 -040083 if (sensor == null) {
Dave Mankoff63a12822019-09-16 14:38:06 -040084 sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
Dave Mankoff55d5d8c2020-02-13 16:15:39 -050085 if (sensor != null) {
86 threshold = sensor.getMaximumRange();
87 }
Dave Mankoff63a12822019-09-16 14:38:06 -040088 }
Dave Mankoff55d5d8c2020-02-13 16:15:39 -050089
90 mThreshold = threshold;
91
Dave Mankoff63a12822019-09-16 14:38:06 -040092 mSensor = sensor;
Dave Mankoff63a12822019-09-16 14:38:06 -040093 }
94
95 public void setTag(String tag) {
96 mTag = tag;
97 }
98
99 public void setSensorDelay(int sensorDelay) {
100 mSensorDelay = sensorDelay;
101 }
102
103 /**
104 * Unregister with the {@link SensorManager} without unsetting listeners on this object.
105 */
106 public void pause() {
107 mPaused = true;
108 unregisterInternal();
109 }
110
111 /**
112 * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
113 */
114 public void resume() {
115 mPaused = false;
116 registerInternal();
117 }
Dave Mankoff55d5d8c2020-02-13 16:15:39 -0500118 /**
119 * Returns a brightness sensor that can be used for proximity purposes.
120 */
121 private Sensor findCustomProxSensor(Resources resources) {
122 String sensorType = resources.getString(R.string.proximity_sensor_type);
123 if (sensorType.isEmpty()) {
124 return null;
125 }
Dave Mankoff63a12822019-09-16 14:38:06 -0400126
Dave Mankoff63a12822019-09-16 14:38:06 -0400127 List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
128 Sensor sensor = null;
129 for (Sensor s : sensorList) {
130 if (sensorType.equals(s.getStringType())) {
131 sensor = s;
132 break;
133 }
134 }
135
136 return sensor;
137 }
138
139 /**
Dave Mankoff55d5d8c2020-02-13 16:15:39 -0500140 * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
141 */
142 private float getCustomProxThreshold(Resources resources) {
143 try {
144 return resources.getFloat(R.dimen.proximity_sensor_threshold);
145 } catch (Resources.NotFoundException e) {
146 throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set.");
147 }
148 }
149
150 /**
Dave Mankoff63a12822019-09-16 14:38:06 -0400151 * Returns true if we are registered with the SensorManager.
152 */
153 public boolean isRegistered() {
154 return mRegistered;
155 }
156
157 /**
158 * Returns {@code false} if a Proximity sensor is not available.
159 */
160 public boolean getSensorAvailable() {
161 return mSensor != null;
162 }
163
164 /**
165 * Add a listener.
166 *
167 * Registers itself with the {@link SensorManager} if this is the first listener
168 * added. If a cool down is currently running, the sensor will be registered when it is over.
169 */
170 public boolean register(ProximitySensorListener listener) {
171 if (!getSensorAvailable()) {
172 return false;
173 }
174
Dave Mankoff682d86f2020-03-06 14:59:00 -0500175 if (mListeners.contains(listener)) {
176 Log.d(TAG, "ProxListener registered multiple times: " + listener);
177 } else {
178 mListeners.add(listener);
179 }
Dave Mankoff63a12822019-09-16 14:38:06 -0400180 registerInternal();
181
182 return true;
183 }
184
Dave Mankoffbf52f4b2019-09-20 14:34:28 -0400185 protected void registerInternal() {
Dave Mankoffbc3091c2019-09-18 11:44:29 -0400186 if (mRegistered || mPaused || mListeners.isEmpty()) {
Dave Mankoff63a12822019-09-16 14:38:06 -0400187 return;
188 }
189 logDebug("Registering sensor listener");
190 mRegistered = true;
191 mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
192 }
193
194 /**
195 * Remove a listener.
196 *
197 * If all listeners are removed from an instance of this class,
198 * it will unregister itself with the SensorManager.
199 */
200 public void unregister(ProximitySensorListener listener) {
201 mListeners.remove(listener);
202 if (mListeners.size() == 0) {
203 unregisterInternal();
204 }
205 }
206
Dave Mankoffbf52f4b2019-09-20 14:34:28 -0400207 protected void unregisterInternal() {
Dave Mankoff63a12822019-09-16 14:38:06 -0400208 if (!mRegistered) {
209 return;
210 }
211 logDebug("unregistering sensor listener");
212 mSensorManager.unregisterListener(mSensorEventListener);
213 mRegistered = false;
214 }
215
216 public Boolean isNear() {
217 return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
218 }
219
220 /** Update all listeners with the last value this class received from the sensor. */
221 public void alertListeners() {
Dave Mankoffdddcae92020-05-13 12:46:18 -0400222 if (mAlerting.getAndSet(true)) {
223 return;
224 }
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400225
226 List<ProximitySensorListener> listeners = new ArrayList<>(mListeners);
227 listeners.forEach(proximitySensorListener ->
Dave Mankoff63a12822019-09-16 14:38:06 -0400228 proximitySensorListener.onSensorEvent(mLastEvent));
Dave Mankoffdddcae92020-05-13 12:46:18 -0400229 mAlerting.set(false);
Dave Mankoff63a12822019-09-16 14:38:06 -0400230 }
231
232 private void onSensorEvent(SensorEvent event) {
Dave Mankoff55d5d8c2020-02-13 16:15:39 -0500233 boolean near = event.values[0] < mThreshold;
Dave Mankoff63a12822019-09-16 14:38:06 -0400234 mLastEvent = new ProximityEvent(near, event.timestamp);
235 alertListeners();
Dave Mankoff63a12822019-09-16 14:38:06 -0400236 }
237
238 @Override
239 public String toString() {
Dave Mankoffbc3091c2019-09-18 11:44:29 -0400240 return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
241 isRegistered(), mPaused, isNear(), mSensor);
Dave Mankoff63a12822019-09-16 14:38:06 -0400242 }
243
244 /**
245 * Convenience class allowing for briefly checking the proximity sensor.
246 */
247 public static class ProximityCheck implements Runnable {
248
249 private final ProximitySensor mSensor;
Dave Mankoffdddcae92020-05-13 12:46:18 -0400250 private final DelayableExecutor mDelayableExecutor;
Dave Mankoff63a12822019-09-16 14:38:06 -0400251 private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400252 private final ProximitySensor.ProximitySensorListener mListener;
253 private final AtomicBoolean mRegistered = new AtomicBoolean();
Dave Mankoff63a12822019-09-16 14:38:06 -0400254
255 @Inject
Dave Mankoffb132e212020-06-24 11:44:20 -0400256 public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) {
Dave Mankoff63a12822019-09-16 14:38:06 -0400257 mSensor = sensor;
258 mSensor.setTag("prox_check");
Dave Mankoffdddcae92020-05-13 12:46:18 -0400259 mDelayableExecutor = delayableExecutor;
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400260 mListener = this::onProximityEvent;
Dave Mankoff63a12822019-09-16 14:38:06 -0400261 }
262
263 /** Set a descriptive tag for the sensors registration. */
264 public void setTag(String tag) {
265 mSensor.setTag(tag);
266 }
267
268 @Override
269 public void run() {
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400270 unregister();
Dave Mankoff63a12822019-09-16 14:38:06 -0400271 mSensor.alertListeners();
272 }
273
274 /**
275 * Query the proximity sensor, timing out if no result.
276 */
277 public void check(long timeoutMs, Consumer<Boolean> callback) {
278 if (!mSensor.getSensorAvailable()) {
279 callback.accept(null);
280 }
281 mCallbacks.add(callback);
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400282 if (!mRegistered.getAndSet(true)) {
283 mSensor.register(mListener);
Dave Mankoffdddcae92020-05-13 12:46:18 -0400284 mDelayableExecutor.executeDelayed(this, timeoutMs);
Dave Mankoff63a12822019-09-16 14:38:06 -0400285 }
286 }
Dave Mankofff54f3eb2020-05-21 16:04:14 -0400287
288 private void unregister() {
289 mSensor.unregister(mListener);
290 mRegistered.set(false);
291 }
292
293 private void onProximityEvent(ProximityEvent proximityEvent) {
294 mCallbacks.forEach(
295 booleanConsumer ->
296 booleanConsumer.accept(
297 proximityEvent == null ? null : proximityEvent.getNear()));
298 mCallbacks.clear();
299 unregister();
300 mRegistered.set(false);
301 }
Dave Mankoff63a12822019-09-16 14:38:06 -0400302 }
303
304 /** Implement to be notified of ProximityEvents. */
305 public interface ProximitySensorListener {
306 /** Called when the ProximitySensor changes. */
307 void onSensorEvent(ProximityEvent proximityEvent);
308 }
309
310 /**
311 * Returned when the near/far state of a {@link ProximitySensor} changes.
312 */
313 public static class ProximityEvent {
314 private final boolean mNear;
315 private final long mTimestampNs;
316
317 public ProximityEvent(boolean near, long timestampNs) {
318 mNear = near;
319 mTimestampNs = timestampNs;
320 }
321
322 public boolean getNear() {
323 return mNear;
324 }
325
326 public long getTimestampNs() {
327 return mTimestampNs;
328 }
329
330 public long getTimestampMs() {
331 return mTimestampNs / 1000000;
332 }
333
334 @Override
335 public String toString() {
336 return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
337 }
338
339 }
340
341 private void logDebug(String msg) {
342 if (DEBUG) {
343 Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
344 }
345 }
346}