xref: /aosp_15_r20/cts/tests/tests/virtualdevice/sensor/src/android/virtualdevice/cts/sensor/VirtualSensorTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2023 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 android.virtualdevice.cts.sensor;
18 
19 import static android.hardware.Sensor.TYPE_ACCELEROMETER;
20 import static android.hardware.SensorDirectChannel.RATE_NORMAL;
21 import static android.hardware.SensorDirectChannel.RATE_STOP;
22 import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER;
23 import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.junit.Assert.assertThrows;
28 import static org.junit.Assert.fail;
29 import static org.junit.Assume.assumeTrue;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.ArgumentMatchers.anyInt;
32 import static org.mockito.ArgumentMatchers.eq;
33 import static org.mockito.Mockito.doAnswer;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.timeout;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.verifyNoMoreInteractions;
38 
39 import static java.util.concurrent.TimeUnit.MICROSECONDS;
40 
41 import android.companion.virtual.VirtualDeviceManager;
42 import android.companion.virtual.VirtualDeviceParams;
43 import android.companion.virtual.sensor.VirtualSensor;
44 import android.companion.virtual.sensor.VirtualSensorCallback;
45 import android.companion.virtual.sensor.VirtualSensorConfig;
46 import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
47 import android.companion.virtual.sensor.VirtualSensorDirectChannelWriter;
48 import android.companion.virtual.sensor.VirtualSensorEvent;
49 import android.companion.virtualdevice.flags.Flags;
50 import android.content.Context;
51 import android.hardware.HardwareBuffer;
52 import android.hardware.Sensor;
53 import android.hardware.SensorDirectChannel;
54 import android.hardware.SensorEvent;
55 import android.hardware.SensorEventListener;
56 import android.hardware.SensorManager;
57 import android.os.MemoryFile;
58 import android.os.SharedMemory;
59 import android.platform.test.annotations.AppModeFull;
60 import android.platform.test.annotations.RequiresFlagsEnabled;
61 import android.system.ErrnoException;
62 import android.virtualdevice.cts.common.VirtualDeviceRule;
63 
64 import androidx.annotation.Nullable;
65 import androidx.test.ext.junit.runners.AndroidJUnit4;
66 import androidx.test.platform.app.InstrumentationRegistry;
67 
68 import org.junit.After;
69 import org.junit.Before;
70 import org.junit.Rule;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.ArgumentCaptor;
74 import org.mockito.Mock;
75 import org.mockito.MockitoAnnotations;
76 import org.mockito.stubbing.Answer;
77 
78 import java.io.UncheckedIOException;
79 import java.nio.ByteBuffer;
80 import java.nio.ByteOrder;
81 import java.time.Duration;
82 import java.util.Arrays;
83 import java.util.List;
84 import java.util.Random;
85 import java.util.concurrent.BlockingQueue;
86 import java.util.concurrent.LinkedBlockingQueue;
87 import java.util.concurrent.TimeUnit;
88 
89 
90 @RunWith(AndroidJUnit4.class)
91 @AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps")
92 public class VirtualSensorTest {
93 
94     private static final String VIRTUAL_SENSOR_NAME = "VirtualAccelerometer";
95     private static final String SECOND_SENSOR_NAME = VIRTUAL_SENSOR_NAME + "2";
96     private static final String VIRTUAL_SENSOR_VENDOR = "VirtualDeviceVendor";
97 
98     private static final int CUSTOM_SENSOR_TYPE = Sensor.TYPE_DEVICE_PRIVATE_BASE + 9999;
99 
100     private static final int SENSOR_TIMEOUT_MILLIS = 1000;
101 
102     private static final int SENSOR_EVENT_SIZE = 104;
103     private static final int SENSOR_EVENT_COUNT = 100;
104     private static final int SHARED_MEMORY_SIZE = SENSOR_EVENT_COUNT * SENSOR_EVENT_SIZE;
105 
106     @Rule
107     public VirtualDeviceRule mVirtualDeviceRule = VirtualDeviceRule.createDefault();
108     @Nullable
109     private VirtualDeviceManager.VirtualDevice mVirtualDevice;
110 
111     private SensorManager mSensorManager;
112     @Mock
113     private SensorManager.DynamicSensorCallback mDynamicSensorCallback;
114 
115     private SensorManager mVirtualDeviceSensorManager;
116     private VirtualSensor mVirtualSensor;
117     private MemoryFile mMemoryFile;
118     private SensorDirectChannel mDirectChannel;
119     private VirtualSensorDirectChannelWriter mVirtualSensorDirectChannelWriter;
120     @Mock
121     private VirtualSensorCallback mVirtualSensorCallback;
122     @Mock
123     private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback;
124     private final VirtualSensorEventListener mSensorEventListener =
125             new VirtualSensorEventListener();
126 
127     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
128 
129     // Synchronize the IO to reduce test flakiness.
130     private final Object mDirectChannelIoLock = new Object();
131 
132     @Before
setUp()133     public void setUp() {
134         MockitoAnnotations.initMocks(this);
135         mSensorManager = mContext.getSystemService(SensorManager.class);
136         assumeTrue(mSensorManager.getSensorList(Sensor.TYPE_ALL).size() > 0);
137     }
138 
setUpVirtualSensor(VirtualSensorConfig sensorConfig)139     private VirtualSensor setUpVirtualSensor(VirtualSensorConfig sensorConfig) {
140         VirtualDeviceParams.Builder builder = new VirtualDeviceParams.Builder()
141                 .setDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS,
142                         VirtualDeviceParams.DEVICE_POLICY_CUSTOM);
143         if (sensorConfig != null) {
144             builder = builder
145                     .addVirtualSensorConfig(sensorConfig)
146                     .setVirtualSensorCallback(
147                             mContext.getMainExecutor(), mVirtualSensorCallback);
148             if (sensorConfig.getDirectChannelTypesSupported() > 0) {
149                 builder = builder.setVirtualSensorDirectChannelCallback(
150                         mContext.getMainExecutor(), mVirtualSensorDirectChannelCallback);
151             }
152         }
153         mVirtualDevice = mVirtualDeviceRule.createManagedVirtualDevice(builder.build());
154         Context deviceContext = mContext.createDeviceContext(mVirtualDevice.getDeviceId());
155         mVirtualDeviceSensorManager = deviceContext.getSystemService(SensorManager.class);
156         if (sensorConfig == null) {
157             return null;
158         } else {
159             return mVirtualDevice.getVirtualSensorList().get(0);
160         }
161     }
162 
setUpDirectChannel()163     private void setUpDirectChannel() throws Exception {
164         mVirtualSensor = setUpVirtualSensor(
165                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
166                         .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
167                         .setHighestDirectReportRateLevel(RATE_NORMAL)
168                         .build());
169 
170         mMemoryFile = new MemoryFile("Sensor Channel", SHARED_MEMORY_SIZE);
171         mDirectChannel = mVirtualDeviceSensorManager.createDirectChannel(mMemoryFile);
172     }
173 
174     @After
tearDown()175     public void tearDown() {
176         if (mMemoryFile != null) {
177             mMemoryFile.close();
178         }
179         if (mDirectChannel != null) {
180             mDirectChannel.close();
181         }
182     }
183 
184     @Test
buildVirtualSensorConfig_hardwareBufferDirectChannel_throwsException()185     public void buildVirtualSensorConfig_hardwareBufferDirectChannel_throwsException() {
186         assertThrows(
187                 IllegalArgumentException.class,
188                 () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
189                         .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE)
190                         .build());
191     }
192 
193     @Test
getSensorList_noVirtualSensors_returnsEmptyList()194     public void getSensorList_noVirtualSensors_returnsEmptyList() {
195         mVirtualSensor = setUpVirtualSensor(/* sensorConfig= */ null);
196         assertThat(mVirtualSensor).isNull();
197         assertThat(mVirtualDeviceSensorManager.getSensorList(TYPE_ACCELEROMETER)).isEmpty();
198     }
199 
200     @Test
getSensorList_returnsVirtualSensor()201     public void getSensorList_returnsVirtualSensor() {
202         mVirtualSensor = setUpVirtualSensor(
203                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
204                         .setVendor(VIRTUAL_SENSOR_VENDOR)
205                         .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
206                         .setHighestDirectReportRateLevel(RATE_NORMAL)
207                         .setMaximumRange(1.2f)
208                         .setResolution(3.4f)
209                         .setPower(5.6f)
210                         .setMinDelay(7)
211                         .setMaxDelay(8)
212                         .build());
213 
214         assertThat(mVirtualSensor.getType()).isEqualTo(TYPE_ACCELEROMETER);
215         assertThat(mVirtualSensor.getName()).isEqualTo(VIRTUAL_SENSOR_NAME);
216         assertThat(mVirtualSensor.getDeviceId()).isEqualTo(mVirtualDevice.getDeviceId());
217 
218         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
219         List<Sensor> sensors = mVirtualDeviceSensorManager.getSensorList(TYPE_ACCELEROMETER);
220         Sensor defaultDeviceSensor = mSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
221 
222         assertThat(sensors).containsExactly(sensor);
223         assertThat(sensor).isNotEqualTo(defaultDeviceSensor);
224 
225         assertThat(sensor.getName()).isEqualTo(VIRTUAL_SENSOR_NAME);
226         assertThat(sensor.getVendor()).isEqualTo(VIRTUAL_SENSOR_VENDOR);
227         assertThat(sensor.getType()).isEqualTo(TYPE_ACCELEROMETER);
228         assertThat(sensor.isDynamicSensor()).isFalse();
229         assertThat(sensor.isDirectChannelTypeSupported(TYPE_MEMORY_FILE)).isTrue();
230         assertThat(sensor.isDirectChannelTypeSupported(TYPE_HARDWARE_BUFFER)).isFalse();
231         assertThat(sensor.getHighestDirectReportRateLevel()).isEqualTo(RATE_NORMAL);
232         assertThat(sensor.getMaximumRange()).isEqualTo(1.2f);
233         assertThat(sensor.getResolution()).isEqualTo(3.4f);
234         assertThat(sensor.getPower()).isEqualTo(5.6f);
235         assertThat(sensor.getMinDelay()).isEqualTo(7);
236         assertThat(sensor.getMaxDelay()).isEqualTo(8);
237         assertThat(sensor.getStringType()).isEqualTo(Sensor.STRING_TYPE_ACCELEROMETER);
238         assertThat(sensor.getReportingMode()).isEqualTo(Sensor.REPORTING_MODE_CONTINUOUS);
239         assertThat(sensor.isWakeUpSensor()).isFalse();
240     }
241 
242     @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
243     @Test
getDefaultSensor_wakeUpSensor()244     public void getDefaultSensor_wakeUpSensor() {
245         mVirtualSensor = setUpVirtualSensor(
246                 new VirtualSensorConfig.Builder(Sensor.TYPE_PROXIMITY, VIRTUAL_SENSOR_NAME)
247                         .setWakeUpSensor(true)
248                         .setReportingMode(Sensor.REPORTING_MODE_ON_CHANGE)
249                         .build());
250 
251         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
252         assertThat(sensor.getName()).isEqualTo(VIRTUAL_SENSOR_NAME);
253         assertThat(sensor.getReportingMode()).isEqualTo(Sensor.REPORTING_MODE_ON_CHANGE);
254         assertThat(sensor.isWakeUpSensor()).isTrue();
255     }
256 
257     @Test
getSensorList_isCached()258     public void getSensorList_isCached() {
259         mVirtualSensor = setUpVirtualSensor(
260                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
261 
262         final List<Sensor> allSensors = mVirtualDeviceSensorManager.getSensorList(Sensor.TYPE_ALL);
263         assertThat(allSensors).isSameInstanceAs(
264                 mVirtualDeviceSensorManager.getSensorList(Sensor.TYPE_ALL));
265 
266         final List<Sensor> sensors = mVirtualDeviceSensorManager.getSensorList(TYPE_ACCELEROMETER);
267         assertThat(sensors).isSameInstanceAs(
268                 mVirtualDeviceSensorManager.getSensorList(TYPE_ACCELEROMETER));
269     }
270 
271     @Test
createVirtualSensor_multipleSensorsSameType()272     public void createVirtualSensor_multipleSensorsSameType() {
273         VirtualDeviceParams.Builder builder = new VirtualDeviceParams.Builder()
274                 .setDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS,
275                         VirtualDeviceParams.DEVICE_POLICY_CUSTOM)
276                 .addVirtualSensorConfig(
277                         new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
278                                 .build())
279                 .addVirtualSensorConfig(
280                         new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SECOND_SENSOR_NAME)
281                                 .build())
282                 .setVirtualSensorCallback(
283                         mContext.getMainExecutor(), mVirtualSensorCallback);
284         mVirtualDevice = mVirtualDeviceRule.createManagedVirtualDevice(builder.build());
285         Context deviceContext = mContext.createDeviceContext(mVirtualDevice.getDeviceId());
286         mVirtualDeviceSensorManager = deviceContext.getSystemService(SensorManager.class);
287 
288         List<VirtualSensor> virtualSensors = mVirtualDevice.getVirtualSensorList();
289         assertThat(virtualSensors).hasSize(2);
290         assertThat(virtualSensors.get(0).getType()).isEqualTo(TYPE_ACCELEROMETER);
291         assertThat(virtualSensors.get(0).getName()).isEqualTo(VIRTUAL_SENSOR_NAME);
292         assertThat(virtualSensors.get(0).getDeviceId()).isEqualTo(mVirtualDevice.getDeviceId());
293         assertThat(virtualSensors.get(1).getType()).isEqualTo(TYPE_ACCELEROMETER);
294         assertThat(virtualSensors.get(1).getName()).isEqualTo(SECOND_SENSOR_NAME);
295         assertThat(virtualSensors.get(1).getDeviceId()).isEqualTo(mVirtualDevice.getDeviceId());
296 
297         List<Sensor> sensors = mVirtualDeviceSensorManager.getSensorList(TYPE_ACCELEROMETER);
298         assertThat(sensors).hasSize(2);
299         assertThat(sensors.get(0).getName()).isEqualTo(VIRTUAL_SENSOR_NAME);
300         assertThat(sensors.get(0).getType()).isEqualTo(TYPE_ACCELEROMETER);
301         assertThat(sensors.get(1).getName()).isEqualTo(SECOND_SENSOR_NAME);
302         assertThat(sensors.get(1).getType()).isEqualTo(TYPE_ACCELEROMETER);
303     }
304 
305     @Test
createVirtualSensor_dynamicSensorCallback_notCalled()306     public void createVirtualSensor_dynamicSensorCallback_notCalled() {
307         mSensorManager.registerDynamicSensorCallback(mDynamicSensorCallback);
308 
309         mVirtualSensor = setUpVirtualSensor(
310                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
311 
312         verify(mDynamicSensorCallback, never()).onDynamicSensorConnected(any());
313 
314         mSensorManager.unregisterDynamicSensorCallback(mDynamicSensorCallback);
315     }
316 
317     @Test
closeVirtualDevice_removesSensor()318     public void closeVirtualDevice_removesSensor() {
319         mVirtualSensor = setUpVirtualSensor(
320                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
321         mVirtualDevice.close();
322 
323         // The virtual device ID is no longer valid, SensorManager falls back to default device.
324         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
325         Sensor defaultDeviceSensor = mSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
326 
327         if (defaultDeviceSensor == null) {
328           assertThat(sensor).isNull();
329         } else {
330           assertThat(sensor.getHandle()).isEqualTo(defaultDeviceSensor.getHandle());
331           assertThat(sensor.getName()).isEqualTo(defaultDeviceSensor.getName());
332         }
333     }
334 
335     @Test
registerListener_triggersVirtualSensorCallback()336     public void registerListener_triggersVirtualSensorCallback() {
337         mVirtualSensor = setUpVirtualSensor(
338                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
339 
340         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
341 
342         final int samplingPeriodMicros = 2345000;
343         final int maxReportLatencyMicros = 678000;
344         mVirtualDeviceSensorManager.registerListener(
345                 mSensorEventListener, sensor, samplingPeriodMicros, maxReportLatencyMicros);
346 
347         final Duration expectedSamplingPeriod =
348                 Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros));
349         final Duration expectedReportLatency =
350                 Duration.ofNanos(MICROSECONDS.toNanos(maxReportLatencyMicros));
351 
352         ArgumentCaptor<VirtualSensor> virtualSensor = ArgumentCaptor.forClass(VirtualSensor.class);
353         verify(mVirtualSensorCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
354                 .onConfigurationChanged(virtualSensor.capture(), eq(true),
355                         eq(expectedSamplingPeriod), eq(expectedReportLatency));
356         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
357 
358         mVirtualDeviceSensorManager.unregisterListener(mSensorEventListener);
359 
360         verify(mVirtualSensorCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
361                 .onConfigurationChanged(virtualSensor.capture(), eq(false), any(Duration.class),
362                         any(Duration.class));
363         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
364 
365         verifyNoMoreInteractions(mVirtualSensorCallback);
366     }
367 
368     @Test
sendEvent_reachesRegisteredListeners()369     public void sendEvent_reachesRegisteredListeners() {
370         mVirtualSensor = setUpVirtualSensor(
371                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
372 
373         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
374 
375         mVirtualDeviceSensorManager.registerListener(
376                 mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
377 
378         final VirtualSensorEvent firstEvent =
379                 new VirtualSensorEvent.Builder(new float[] {0.1f, 2.3f, 4.5f}).build();
380         mVirtualSensor.sendEvent(firstEvent);
381 
382         mSensorEventListener.assertReceivedSensorEvent(sensor, firstEvent);
383 
384         final VirtualSensorEvent secondEvent =
385                 new VirtualSensorEvent.Builder(new float[] {6.7f, 8.9f, 0.1f}).build();
386         mVirtualSensor.sendEvent(secondEvent);
387 
388         mSensorEventListener.assertReceivedSensorEvent(sensor, secondEvent);
389 
390         mVirtualDeviceSensorManager.unregisterListener(mSensorEventListener);
391 
392         final VirtualSensorEvent thirdEvent =
393                 new VirtualSensorEvent.Builder(new float[] {2.3f, 4.5f, 6.7f}).build();
394         mVirtualSensor.sendEvent(thirdEvent);
395 
396         mSensorEventListener.assertNoMoreEvents();
397     }
398 
399     @Test
sendEvent_explicitTimestampSpecified()400     public void sendEvent_explicitTimestampSpecified() {
401         mVirtualSensor = setUpVirtualSensor(
402                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
403         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
404         mVirtualDeviceSensorManager.registerListener(
405                 mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
406 
407         VirtualSensorEvent event =
408                 new VirtualSensorEvent.Builder(new float[] {0.1f, 2.3f, 4.5f})
409                         .setTimestampNanos(System.nanoTime())
410                         .build();
411         mVirtualSensor.sendEvent(event);
412 
413         mSensorEventListener.assertReceivedSensorEvent(sensor, event);
414 
415         mVirtualDeviceSensorManager.unregisterListener(mSensorEventListener);
416 
417         mSensorEventListener.assertNoMoreEvents();
418     }
419 
420     @Test
sendEvent_invalidValues_eventIsDropped()421     public void sendEvent_invalidValues_eventIsDropped() {
422         mVirtualSensor = setUpVirtualSensor(
423                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
424 
425         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
426 
427         mVirtualDeviceSensorManager.registerListener(
428                 mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
429 
430         final VirtualSensorEvent firstEvent =
431                 new VirtualSensorEvent.Builder(new float[] {0.1f}).build();
432         mVirtualSensor.sendEvent(firstEvent);
433 
434         final VirtualSensorEvent secondEvent =
435                 new VirtualSensorEvent.Builder(new float[] {2.3f, 4.5f, 6.7f, 8.9f}).build();
436         mVirtualSensor.sendEvent(secondEvent);
437 
438         final VirtualSensorEvent thirdEvent =
439                 new VirtualSensorEvent.Builder(new float[] {7.7f, 8.8f}).build();
440         mVirtualSensor.sendEvent(thirdEvent);
441 
442         mSensorEventListener.assertNoMoreEvents();
443 
444         mVirtualDeviceSensorManager.unregisterListener(mSensorEventListener);
445     }
446 
447     @Test
virtualSensor_arbitrarySensorType()448     public void virtualSensor_arbitrarySensorType() {
449         mVirtualSensor = setUpVirtualSensor(
450                 new VirtualSensorConfig.Builder(CUSTOM_SENSOR_TYPE, VIRTUAL_SENSOR_NAME).build());
451 
452         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(CUSTOM_SENSOR_TYPE);
453 
454         mVirtualDeviceSensorManager.registerListener(
455                 mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
456 
457         float[] values = new float[16];
458         Arrays.fill(values, 1.2f);
459 
460         final VirtualSensorEvent validEvent = new VirtualSensorEvent.Builder(values).build();
461         mVirtualSensor.sendEvent(validEvent);
462 
463         mSensorEventListener.assertReceivedSensorEvent(sensor, validEvent);
464 
465         // Sensor events can have at most 16 values. Check that events with more values are dropped.
466         float[] invalidValues = new float[17];
467         Arrays.fill(values, 3.4f);
468         final VirtualSensorEvent invalidEvent =
469                 new VirtualSensorEvent.Builder(invalidValues).build();
470         mVirtualSensor.sendEvent(invalidEvent);
471 
472         mSensorEventListener.assertNoMoreEvents();
473 
474         mVirtualDeviceSensorManager.unregisterListener(mSensorEventListener);
475     }
476 
477     @Test
directConnection_memoryFile_notSupported()478     public void directConnection_memoryFile_notSupported() throws Exception {
479         mVirtualSensor = setUpVirtualSensor(
480                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
481 
482         MemoryFile memoryFile = new MemoryFile("Sensor Channel", SHARED_MEMORY_SIZE);
483         SensorDirectChannel channel = mVirtualDeviceSensorManager.createDirectChannel(memoryFile);
484 
485         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
486         assertThat(channel.configure(sensor, RATE_NORMAL)).isEqualTo(0);
487     }
488 
489     @Test
directConnection_hardwareBuffer_throwsException()490     public void directConnection_hardwareBuffer_throwsException() {
491         // Skip this test if hardware buffer direct channel is generally not supported on the device
492         assumeTrue(mSensorManager.getSensorList(Sensor.TYPE_ALL).stream().anyMatch(
493                 s -> s.isDirectChannelTypeSupported(TYPE_HARDWARE_BUFFER)));
494 
495         mVirtualSensor = setUpVirtualSensor(
496                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME).build());
497 
498         try (HardwareBuffer hardwareBuffer = createSensorsHardwareBuffer()) {
499             assertThrows(UncheckedIOException.class,
500                     () -> mVirtualDeviceSensorManager.createDirectChannel(hardwareBuffer));
501         }
502     }
503 
504     @Test
directConnection_memoryFile_onlyValidForVirtualDevice()505     public void directConnection_memoryFile_onlyValidForVirtualDevice() throws Exception {
506         setUpDirectChannel();
507 
508         // The channel is created for the virtual device ID, configuring it for a sensor of the
509         // default device should not be allowed.
510         Sensor defaultDeviceSensor =
511               mSensorManager.getSensorList(Sensor.TYPE_ALL).stream().findFirst().orElse(null);
512         assumeTrue(defaultDeviceSensor != null);
513         assertThat(mDirectChannel.configure(defaultDeviceSensor, RATE_NORMAL)).isEqualTo(0);
514     }
515 
516     @Test
directConnectionForDefaultDevice_memoryFile_notValidForVirtualDevice()517     public void directConnectionForDefaultDevice_memoryFile_notValidForVirtualDevice()
518             throws Exception {
519         // Skip this test if memory file direct channel is generally not supported on the device.
520         assumeTrue(mSensorManager.getSensorList(Sensor.TYPE_ALL).stream().anyMatch(
521                 s -> s.isDirectChannelTypeSupported(TYPE_MEMORY_FILE)));
522 
523         MemoryFile memoryFile = new MemoryFile("Sensor Channel", SHARED_MEMORY_SIZE);
524         SensorDirectChannel channel = mSensorManager.createDirectChannel(memoryFile);
525 
526         mVirtualSensor = setUpVirtualSensor(
527                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
528                         .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
529                         .setHighestDirectReportRateLevel(RATE_NORMAL)
530                         .build());
531 
532         // The channel is created for the default device ID, configuring it for a sensor of the
533         // virtual device should not be allowed.
534         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
535         assertThat(channel.configure(sensor, RATE_NORMAL)).isEqualTo(0);
536     }
537 
538     @Test
directConnection_memoryFile_notValidForAnotherVirtualDevice()539     public void directConnection_memoryFile_notValidForAnotherVirtualDevice() throws Exception {
540         setUpDirectChannel();
541 
542         // Creates a new virtual device, while the direct channel is associated with the Sensor
543         // manager of the first virtual device.
544         mVirtualSensor = setUpVirtualSensor(
545                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
546                         .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
547                         .setHighestDirectReportRateLevel(RATE_NORMAL)
548                         .build());
549 
550         // The channel is configured for the first device and the sensor does not belong to it.
551         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
552         assertThat(mDirectChannel.configure(sensor, RATE_NORMAL)).isEqualTo(0);
553     }
554 
555     @Test
directConnectionForDefaultDevice_hardwareBuffer_notValidForVirtualDevice()556     public void directConnectionForDefaultDevice_hardwareBuffer_notValidForVirtualDevice() {
557         // Skip this test if hardware buffer direct channel is generally not supported on the device
558         assumeTrue(mSensorManager.getSensorList(Sensor.TYPE_ALL).stream().anyMatch(
559                 s -> s.isDirectChannelTypeSupported(TYPE_HARDWARE_BUFFER)));
560 
561         try (HardwareBuffer hardwareBuffer = createSensorsHardwareBuffer()) {
562             SensorDirectChannel channel = mSensorManager.createDirectChannel(hardwareBuffer);
563 
564             mVirtualSensor = setUpVirtualSensor(
565                     new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER,
566                             VIRTUAL_SENSOR_NAME).build());
567 
568             // The channel is created for the default device ID, configuring it for a sensor of the
569             // virtual device should not be allowed.
570             Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
571             assertThat(channel.configure(sensor, RATE_NORMAL)).isEqualTo(0);
572         }
573     }
574 
575     @Test
directConnection_memoryFile_triggersVirtualSensorCallback()576     public void directConnection_memoryFile_triggersVirtualSensorCallback() throws Exception {
577         setUpDirectChannel();
578 
579         ArgumentCaptor<Integer> channelHandle = ArgumentCaptor.forClass(Integer.class);
580         ArgumentCaptor<SharedMemory> sharedMemory = ArgumentCaptor.forClass(SharedMemory.class);
581         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
582                 .onDirectChannelCreated(channelHandle.capture(), sharedMemory.capture());
583 
584         doAnswer((Answer<Void>) i -> {
585             sharedMemory.getValue().close();
586             return null;
587         }).when(mVirtualSensorDirectChannelCallback)
588                 .onDirectChannelDestroyed(channelHandle.getValue());
589 
590         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
591         int reportToken = mDirectChannel.configure(sensor, RATE_NORMAL);
592         assertThat(reportToken).isGreaterThan(0);
593 
594         ArgumentCaptor<VirtualSensor> virtualSensor = ArgumentCaptor.forClass(VirtualSensor.class);
595         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
596                 .onDirectChannelConfigured(eq(channelHandle.getValue()), virtualSensor.capture(),
597                         eq(RATE_NORMAL), eq(reportToken));
598         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
599 
600         assertThat(mDirectChannel.configure(sensor, RATE_STOP)).isEqualTo(1);
601 
602         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
603                 .onDirectChannelConfigured(eq(channelHandle.getValue()), virtualSensor.capture(),
604                         eq(RATE_STOP), eq(reportToken));
605         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
606 
607         mDirectChannel.close();
608         mDirectChannel = null;
609         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
610                 .onDirectChannelDestroyed(eq(channelHandle.getValue()));
611     }
612 
613     @Test
directConnection_memoryFile_stopAll_triggersVirtualSensorCallback()614     public void directConnection_memoryFile_stopAll_triggersVirtualSensorCallback()
615             throws Exception {
616         setUpDirectChannel();
617 
618         ArgumentCaptor<Integer> channelHandle = ArgumentCaptor.forClass(Integer.class);
619         ArgumentCaptor<SharedMemory> sharedMemory = ArgumentCaptor.forClass(SharedMemory.class);
620         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
621                 .onDirectChannelCreated(channelHandle.capture(), sharedMemory.capture());
622 
623         doAnswer((Answer<Void>) i -> {
624             sharedMemory.getValue().close();
625             return null;
626         }).when(mVirtualSensorDirectChannelCallback)
627                 .onDirectChannelDestroyed(channelHandle.getValue());
628 
629         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
630         int reportToken = mDirectChannel.configure(sensor, RATE_NORMAL);
631         assertThat(reportToken).isGreaterThan(0);
632 
633         ArgumentCaptor<VirtualSensor> virtualSensor = ArgumentCaptor.forClass(VirtualSensor.class);
634         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
635                 .onDirectChannelConfigured(eq(channelHandle.getValue()), virtualSensor.capture(),
636                         eq(RATE_NORMAL), eq(reportToken));
637         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
638 
639         assertThat(mDirectChannel.configure(/*sensor=*/null, RATE_STOP)).isEqualTo(1);
640 
641         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
642                 .onDirectChannelConfigured(eq(channelHandle.getValue()), virtualSensor.capture(),
643                         eq(RATE_STOP), eq(reportToken));
644         assertThat(virtualSensor.getValue().getHandle()).isEqualTo(mVirtualSensor.getHandle());
645     }
646 
647     @Test
directConnection_memoryFile_injectEvents()648     public void directConnection_memoryFile_injectEvents() throws Exception {
649         setUpDirectChannel();
650 
651         ArgumentCaptor<Integer> channelHandle = ArgumentCaptor.forClass(Integer.class);
652         ArgumentCaptor<SharedMemory> sharedMemory = ArgumentCaptor.forClass(SharedMemory.class);
653         verify(mVirtualSensorDirectChannelCallback, timeout(SENSOR_TIMEOUT_MILLIS).times(1))
654                 .onDirectChannelCreated(channelHandle.capture(), sharedMemory.capture());
655 
656         doAnswer((Answer<Void>) i -> {
657             int reportToken = (int) i.getArguments()[3];
658             writeDirectChannelEvents(reportToken, sharedMemory.getValue());
659             return null;
660         }).when(mVirtualSensorDirectChannelCallback)
661                 .onDirectChannelConfigured(
662                         eq(channelHandle.getValue()), any(), eq(RATE_NORMAL), anyInt());
663 
664         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
665         int reportToken = mDirectChannel.configure(sensor, RATE_NORMAL);
666         verifyDirectChannelEvents(reportToken);
667 
668         doAnswer((Answer<Void>) i -> {
669             sharedMemory.getValue().close();
670             return null;
671         }).when(mVirtualSensorDirectChannelCallback)
672                 .onDirectChannelDestroyed(channelHandle.getValue());
673     }
674 
675     @Test
directConnection_memoryFile_injectEvents_withHelperWriter()676     public void directConnection_memoryFile_injectEvents_withHelperWriter() throws Exception {
677         mVirtualSensorDirectChannelWriter = new VirtualSensorDirectChannelWriter();
678 
679         doAnswer((Answer<Void>) i -> {
680             int channelHandle = (int) i.getArguments()[0];
681             SharedMemory sharedMemory = (SharedMemory) i.getArguments()[1];
682             mVirtualSensorDirectChannelWriter.addChannel(channelHandle, sharedMemory);
683             return null;
684         }).when(mVirtualSensorDirectChannelCallback).onDirectChannelCreated(anyInt(), any());
685 
686         doAnswer((Answer<Void>) i -> {
687             int channelHandle = (int) i.getArguments()[0];
688             mVirtualSensorDirectChannelWriter.removeChannel(channelHandle);
689             return null;
690         }).when(mVirtualSensorDirectChannelCallback).onDirectChannelDestroyed(anyInt());
691 
692         doAnswer((Answer<Void>) i -> {
693             int channelHandle = (int) i.getArguments()[0];
694             VirtualSensor sensor = (VirtualSensor) i.getArguments()[1];
695             int rateLevel = (int) i.getArguments()[2];
696             int reportToken = (int) i.getArguments()[3];
697             assertThat(mVirtualSensorDirectChannelWriter.configureChannel(
698                     channelHandle, sensor, rateLevel, reportToken)).isTrue();
699             Random random = new Random();
700 
701             for (int eventCount = 0; eventCount < SENSOR_EVENT_COUNT * 2; ++eventCount) {
702                 float[] values = new float[] {
703                         reportToken + eventCount * 0.01f,
704                         reportToken + eventCount * 0.02f,
705                         reportToken + eventCount * 0.03f,
706                 };
707                 synchronized (mDirectChannelIoLock) {
708                     assertThat(mVirtualSensorDirectChannelWriter.writeSensorEvent(sensor,
709                             new VirtualSensorEvent.Builder(values)
710                                     .setTimestampNanos(System.nanoTime())
711                                     .build()))
712                             .isTrue();
713                 }
714                 try {
715                     Thread.sleep(random.nextInt(10));  // Sleep random time of 0-20ms.
716                 } catch (InterruptedException e) {
717                     fail("Interrupted while writing sensor events: " + e);
718                 }
719             }
720             return null;
721         }).when(mVirtualSensorDirectChannelCallback)
722                 .onDirectChannelConfigured(anyInt(), any(), eq(RATE_NORMAL), anyInt());
723 
724         setUpDirectChannel();
725 
726         Sensor sensor = mVirtualDeviceSensorManager.getDefaultSensor(TYPE_ACCELEROMETER);
727         int reportToken = mDirectChannel.configure(sensor, RATE_NORMAL);
728         verifyDirectChannelEvents(reportToken);
729     }
730 
731     private static class VirtualSensorEventListener implements SensorEventListener {
732         private final BlockingQueue<SensorEvent> mEvents = new LinkedBlockingQueue<>();
733 
734         @Override
onSensorChanged(SensorEvent event)735         public void onSensorChanged(SensorEvent event) {
736             try {
737                 mEvents.put(event);
738             } catch (InterruptedException ex) {
739                 fail("Interrupted while adding a SensorEvent to the queue");
740             }
741         }
742 
743         @Override
onAccuracyChanged(Sensor sensor, int accuracy)744         public void onAccuracyChanged(Sensor sensor, int accuracy) {
745         }
746 
assertReceivedSensorEvent(Sensor sensor, VirtualSensorEvent expected)747         public void assertReceivedSensorEvent(Sensor sensor, VirtualSensorEvent expected) {
748             SensorEvent event = waitForEvent();
749             if (event == null) {
750                 fail("Did not receive SensorEvent with values "
751                         + Arrays.toString(expected.getValues()));
752             }
753 
754             assertThat(event.sensor).isEqualTo(sensor);
755             assertThat(event.values).isEqualTo(expected.getValues());
756             assertThat(event.timestamp).isEqualTo(expected.getTimestampNanos());
757         }
758 
assertNoMoreEvents()759         public void assertNoMoreEvents() {
760             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
761             SensorEvent event = waitForEvent();
762             if (event != null) {
763                 fail("Received extra SensorEvent with values: " + Arrays.toString(event.values));
764             }
765         }
766 
waitForEvent()767         private SensorEvent waitForEvent() {
768             try {
769                 return mEvents.poll(5, TimeUnit.SECONDS);
770             } catch (InterruptedException e) {
771                 fail("Interrupted while waiting for SensorEvent");
772                 return null;
773             }
774         }
775     }
776 
writeDirectChannelEvents(int reportToken, SharedMemory sharedMemory)777     private void writeDirectChannelEvents(int reportToken, SharedMemory sharedMemory) {
778         int offset = 0;
779         int eventCount = 0;
780         ByteBuffer event = ByteBuffer.allocate(SENSOR_EVENT_SIZE);
781         event.order(ByteOrder.nativeOrder());
782         Random random = new Random();
783         ByteBuffer memoryMapping = null;
784         try {
785             memoryMapping = sharedMemory.mapReadWrite();
786         } catch (ErrnoException e) {
787             sharedMemory.close();
788             fail("Could not map the shared memory for IO: " + e);
789         }
790 
791         while (eventCount < SENSOR_EVENT_COUNT * 2) {
792             event.position(0);
793             event.putInt(SENSOR_EVENT_SIZE);
794             event.putInt(reportToken);
795             event.putInt(TYPE_ACCELEROMETER);
796             event.putInt(++eventCount);
797             event.putLong(System.nanoTime());
798             // sensor values
799             event.putFloat(reportToken + eventCount * 0.01f);
800             event.putFloat(reportToken + eventCount * 0.02f);
801             event.putFloat(reportToken + eventCount * 0.03f);
802 
803             memoryMapping.position(offset);
804             synchronized (mDirectChannelIoLock) {
805                 memoryMapping.put(event.array(), 0, SENSOR_EVENT_SIZE);
806             }
807             try {
808                 Thread.sleep(random.nextInt(10));  // Sleep random time of 0-20ms.
809             } catch (InterruptedException e) {
810                 sharedMemory.close();
811                 fail("Interrupted while writing sensor events: " + e);
812             }
813 
814             offset += SENSOR_EVENT_SIZE;
815             if (offset + SENSOR_EVENT_SIZE >= sharedMemory.getSize()) {
816                 offset = 0;
817             }
818         }
819     }
820 
verifyDirectChannelEvents(int reportToken)821     private void verifyDirectChannelEvents(int reportToken) throws Exception {
822         int offset = 0;
823         int eventCount = 0;
824         ByteBuffer byteBuffer = ByteBuffer.allocate(SENSOR_EVENT_SIZE);
825         byteBuffer.order(ByteOrder.nativeOrder());
826 
827         while (eventCount < SENSOR_EVENT_COUNT * 2) {
828             synchronized (mDirectChannelIoLock) {
829                 assertThat(mMemoryFile.readBytes(byteBuffer.array(), offset, 0, SENSOR_EVENT_SIZE))
830                         .isEqualTo(SENSOR_EVENT_SIZE);
831             }
832             byteBuffer.position(0);
833             int eventSize = byteBuffer.getInt();
834             int actualReportToken = byteBuffer.getInt();
835             if (reportToken != actualReportToken) {
836                 Thread.sleep(10);
837                 continue;
838             }
839             int sensorType = byteBuffer.getInt();
840             int eventCounter = byteBuffer.getInt();
841 
842             if (eventCounter > 0) {
843                 if (eventCounter != eventCount + 1) {
844                     Thread.sleep(10);
845                     continue;
846                 }
847                 eventCount++;
848 
849                 assertThat(eventSize).isEqualTo(SENSOR_EVENT_SIZE);
850                 assertThat(sensorType).isEqualTo(TYPE_ACCELEROMETER);
851 
852                 byteBuffer.getLong();  // timestamp
853 
854                 // verify the sensor values
855                 assertThat(byteBuffer.getFloat())
856                         .isEqualTo(reportToken + eventCounter * 0.1f);
857                 assertThat(byteBuffer.getFloat())
858                         .isEqualTo(reportToken + eventCounter * 0.2f);
859                 assertThat(byteBuffer.getFloat())
860                         .isEqualTo(reportToken + eventCounter * 0.3f);
861 
862                 offset += SENSOR_EVENT_SIZE;
863                 if (offset + SENSOR_EVENT_SIZE >= SHARED_MEMORY_SIZE) {
864                     offset = 0;
865                 }
866             }
867         }
868     }
869 
createSensorsHardwareBuffer()870     private static HardwareBuffer createSensorsHardwareBuffer() {
871         return HardwareBuffer.create(
872                 /*width=*/SHARED_MEMORY_SIZE, /*height=*/1, HardwareBuffer.BLOB, /*layers=*/1,
873                 HardwareBuffer.USAGE_CPU_READ_OFTEN
874                         | HardwareBuffer.USAGE_GPU_DATA_BUFFER
875                         | HardwareBuffer.USAGE_SENSOR_DIRECT_DATA);
876     }
877 }
878