Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 14 | * limitations under the License. |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 15 | */ |
| 16 | |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 17 | package com.android.systemui.util.sensors; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 18 | |
| 19 | import android.content.Context; |
| 20 | import android.hardware.HardwareBuffer; |
| 21 | import android.hardware.Sensor; |
| 22 | import android.hardware.SensorAdditionalInfo; |
| 23 | import android.hardware.SensorDirectChannel; |
| 24 | import android.hardware.SensorEvent; |
| 25 | import android.hardware.SensorEventListener; |
| 26 | import android.hardware.SensorManager; |
| 27 | import android.hardware.TriggerEventListener; |
| 28 | import android.os.Handler; |
| 29 | import android.os.MemoryFile; |
| 30 | import android.os.SystemClock; |
| 31 | import android.util.ArraySet; |
| 32 | |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 33 | import com.android.internal.util.Preconditions; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 34 | |
| 35 | import java.lang.reflect.Constructor; |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 36 | import java.lang.reflect.Field; |
| 37 | import java.lang.reflect.Method; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 38 | import java.util.ArrayList; |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 39 | import java.util.Arrays; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 40 | import java.util.List; |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 41 | import java.util.stream.Collectors; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 42 | |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 43 | import javax.annotation.Nullable; |
| 44 | |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 45 | /** |
| 46 | * Rudimentary fake for SensorManager |
| 47 | * |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 48 | * Currently only supports proximity, light and tap sensors. |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 49 | * |
| 50 | * Note that this class ignores the "Handler" argument, so the test is responsible for calling the |
| 51 | * listener on the right thread. |
| 52 | */ |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 53 | public class FakeSensorManager extends SensorManager { |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 54 | |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 55 | public static final String TAP_SENSOR_TYPE = "tapSensorType"; |
| 56 | |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 57 | private final FakeProximitySensor mFakeProximitySensor; |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 58 | private final FakeGenericSensor mFakeLightSensor; |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 59 | private final FakeGenericSensor mFakeTapSensor; |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 60 | private final FakeGenericSensor[] mSensors; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 61 | |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 62 | public FakeSensorManager(Context context) throws Exception { |
| 63 | Sensor proxSensor = context.getSystemService(SensorManager.class) |
| 64 | .getDefaultSensor(Sensor.TYPE_PROXIMITY); |
| 65 | if (proxSensor == null) { |
| 66 | // No prox? Let's create a fake one! |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 67 | proxSensor = createSensor(Sensor.TYPE_PROXIMITY, null); |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 68 | } |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 69 | |
| 70 | mSensors = new FakeGenericSensor[]{ |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 71 | mFakeProximitySensor = new FakeProximitySensor(proxSensor), |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 72 | mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT, null)), |
| 73 | mFakeTapSensor = new FakeGenericSensor(createSensor(99, TAP_SENSOR_TYPE)) |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 74 | }; |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 75 | } |
| 76 | |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 77 | public FakeProximitySensor getFakeProximitySensor() { |
| 78 | return mFakeProximitySensor; |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 79 | } |
| 80 | |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 81 | public FakeGenericSensor getFakeLightSensor() { |
| 82 | return mFakeLightSensor; |
| 83 | } |
| 84 | |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 85 | public FakeGenericSensor getFakeTapSensor() { |
| 86 | return mFakeTapSensor; |
| 87 | } |
| 88 | |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 89 | @Override |
| 90 | public Sensor getDefaultSensor(int type) { |
| 91 | Sensor s = super.getDefaultSensor(type); |
| 92 | if (s != null) { |
| 93 | return s; |
| 94 | } |
Dave Mankoff | d94c969 | 2019-10-18 12:38:21 -0400 | [diff] [blame^] | 95 | switch(type) { |
| 96 | case Sensor.TYPE_PROXIMITY: |
| 97 | try { |
| 98 | return createSensor(Sensor.TYPE_PROXIMITY, null); |
| 99 | } catch (Exception e) { |
| 100 | // fall through |
| 101 | } |
| 102 | break; |
| 103 | default: |
| 104 | break; |
| 105 | |
| 106 | } |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 107 | // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just |
| 108 | // return non-wakeup sensors if we can't find a wakeup sensor. |
| 109 | return getDefaultSensor(type, false /* wakeup */); |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | @Override |
| 113 | protected List<Sensor> getFullSensorList() { |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 114 | return Arrays |
| 115 | .stream(mSensors) |
| 116 | .map(i -> i.mSensor) |
| 117 | .collect(Collectors.toList()); |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | @Override |
| 121 | protected List<Sensor> getFullDynamicSensorList() { |
| 122 | return new ArrayList<>(); |
| 123 | } |
| 124 | |
| 125 | @Override |
| 126 | protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 127 | Preconditions.checkNotNull(listener); |
| 128 | for (FakeGenericSensor s : mSensors) { |
| 129 | if (sensor == null || s.mSensor == sensor) { |
| 130 | s.mListeners.remove(listener); |
| 131 | } |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 132 | } |
| 133 | } |
| 134 | |
| 135 | @Override |
| 136 | protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, |
| 137 | int delayUs, |
| 138 | Handler handler, int maxReportLatencyUs, int reservedFlags) { |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 139 | Preconditions.checkNotNull(sensor); |
| 140 | Preconditions.checkNotNull(listener); |
| 141 | for (FakeGenericSensor s : mSensors) { |
| 142 | if (s.mSensor == sensor) { |
| 143 | s.mListeners.add(listener); |
| 144 | return true; |
| 145 | } |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 146 | } |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | @Override |
| 151 | protected boolean flushImpl(SensorEventListener listener) { |
| 152 | return false; |
| 153 | } |
| 154 | |
| 155 | @Override |
| 156 | protected SensorDirectChannel createDirectChannelImpl(MemoryFile memoryFile, |
| 157 | HardwareBuffer hardwareBuffer) { |
| 158 | return null; |
| 159 | } |
| 160 | |
| 161 | @Override |
| 162 | protected void destroyDirectChannelImpl(SensorDirectChannel channel) { |
| 163 | |
| 164 | } |
| 165 | |
| 166 | @Override |
| 167 | protected int configureDirectChannelImpl(SensorDirectChannel channel, Sensor s, int rate) { |
| 168 | return 0; |
| 169 | } |
| 170 | |
| 171 | @Override |
| 172 | protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback, |
| 173 | Handler handler) { |
| 174 | |
| 175 | } |
| 176 | |
| 177 | @Override |
| 178 | protected void unregisterDynamicSensorCallbackImpl( |
| 179 | DynamicSensorCallback callback) { |
| 180 | |
| 181 | } |
| 182 | |
| 183 | @Override |
| 184 | protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) { |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 185 | return true; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | @Override |
| 189 | protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor, |
| 190 | boolean disable) { |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 191 | return true; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | @Override |
| 195 | protected boolean initDataInjectionImpl(boolean enable) { |
| 196 | return false; |
| 197 | } |
| 198 | |
| 199 | @Override |
| 200 | protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy, |
| 201 | long timestamp) { |
| 202 | return false; |
| 203 | } |
| 204 | |
| 205 | @Override |
| 206 | protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) { |
| 207 | return false; |
| 208 | } |
| 209 | |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 210 | private Sensor createSensor(int type, @Nullable String stringType) throws Exception { |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 211 | Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); |
| 212 | constr.setAccessible(true); |
| 213 | Sensor sensor = constr.newInstance(); |
| 214 | |
| 215 | setSensorType(sensor, type); |
Lucas Dupin | 3174c66 | 2019-07-15 15:49:54 -0700 | [diff] [blame] | 216 | if (stringType != null) { |
| 217 | setSensorField(sensor, "mStringType", stringType); |
| 218 | } |
Adrian Roos | 84a187f | 2017-04-28 14:10:51 -0700 | [diff] [blame] | 219 | setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); |
| 220 | setSensorField(sensor, "mVendor", "Mock Vendor"); |
| 221 | setSensorField(sensor, "mVersion", 1); |
| 222 | setSensorField(sensor, "mHandle", -1); |
| 223 | setSensorField(sensor, "mMaxRange", 10); |
| 224 | setSensorField(sensor, "mResolution", 1); |
| 225 | setSensorField(sensor, "mPower", 1); |
| 226 | setSensorField(sensor, "mMinDelay", 1000); |
| 227 | setSensorField(sensor, "mMaxDelay", 1000000000); |
| 228 | setSensorField(sensor, "mFlags", 0); |
| 229 | setSensorField(sensor, "mId", -1); |
| 230 | |
| 231 | return sensor; |
| 232 | } |
| 233 | |
| 234 | private void setSensorField(Sensor sensor, String fieldName, Object value) throws Exception { |
| 235 | Field field = Sensor.class.getDeclaredField(fieldName); |
| 236 | field.setAccessible(true); |
| 237 | field.set(sensor, value); |
| 238 | } |
| 239 | |
| 240 | private void setSensorType(Sensor sensor, int type) throws Exception { |
| 241 | Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); |
| 242 | setter.setAccessible(true); |
| 243 | setter.invoke(sensor, type); |
| 244 | } |
| 245 | |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 246 | public class FakeProximitySensor extends FakeGenericSensor { |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 247 | |
Dave Mankoff | 63a1282 | 2019-09-16 14:38:06 -0400 | [diff] [blame] | 248 | private FakeProximitySensor(Sensor sensor) { |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 249 | super(sensor); |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 250 | } |
| 251 | |
| 252 | public void sendProximityResult(boolean far) { |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 253 | sendSensorEvent(far ? getSensor().getMaximumRange() : 0); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | public class FakeGenericSensor { |
| 258 | |
| 259 | private final Sensor mSensor; |
| 260 | private final ArraySet<SensorEventListener> mListeners = new ArraySet<>(); |
| 261 | |
| 262 | public FakeGenericSensor( |
| 263 | Sensor sensor) { |
| 264 | this.mSensor = sensor; |
| 265 | } |
| 266 | |
| 267 | public Sensor getSensor() { |
| 268 | return mSensor; |
| 269 | } |
| 270 | |
| 271 | public void sendSensorEvent(float... values) { |
| 272 | SensorEvent event = createSensorEvent(values.length); |
| 273 | System.arraycopy(values, 0, event.values, 0, values.length); |
| 274 | for (SensorEventListener listener : mListeners) { |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 275 | listener.onSensorChanged(event); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | private SensorEvent createSensorEvent(int valuesSize) { |
| 280 | SensorEvent event; |
| 281 | try { |
| 282 | Constructor<SensorEvent> constr = |
| 283 | SensorEvent.class.getDeclaredConstructor(Integer.TYPE); |
| 284 | constr.setAccessible(true); |
| 285 | event = constr.newInstance(valuesSize); |
| 286 | } catch (Exception e) { |
| 287 | throw new RuntimeException(e); |
| 288 | } |
Adrian Roos | 2981eb0 | 2017-05-26 18:40:09 -0700 | [diff] [blame] | 289 | event.sensor = mSensor; |
Adrian Roos | d35d4ca | 2017-04-19 14:31:03 -0700 | [diff] [blame] | 290 | event.timestamp = SystemClock.elapsedRealtimeNanos(); |
| 291 | |
| 292 | return event; |
| 293 | } |
| 294 | } |
| 295 | } |