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.Sensor.TYPE_GYROSCOPE; 21 import static android.hardware.SensorDirectChannel.RATE_NORMAL; 22 import static android.hardware.SensorDirectChannel.RATE_STOP; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.assertThrows; 27 28 import android.companion.virtual.sensor.VirtualSensor; 29 import android.companion.virtual.sensor.VirtualSensorDirectChannelWriter; 30 import android.companion.virtual.sensor.VirtualSensorEvent; 31 import android.os.SharedMemory; 32 import android.platform.test.annotations.AppModeFull; 33 34 import androidx.test.ext.junit.runners.AndroidJUnit4; 35 36 import org.junit.After; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.util.List; 43 44 45 @RunWith(AndroidJUnit4.class) 46 @AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps") 47 public class VirtualSensorDirectChannelWriterTest { 48 49 private static final int CHANNEL_HANDLE = 7; 50 private static final int SENSOR_HANDLE = 42; 51 private static final int REPORT_TOKEN = 15; 52 private static final String ACCELEROMETER_SENSOR_NAME = "VirtualAccelerometer"; 53 private static final String GYROSCOPE_SENSOR_NAME = "VirtualGyroscope"; 54 private static final String SHARED_MEMORY_NAME = "VirtualSensorSharedMemory"; 55 private static final int SENSOR_EVENT_SIZE = 104; 56 private static final int SHARED_MEMORY_SIZE = SENSOR_EVENT_SIZE * 3; 57 58 private final VirtualSensor mAccelerometer = new VirtualSensor( 59 SENSOR_HANDLE, TYPE_ACCELEROMETER, ACCELEROMETER_SENSOR_NAME); 60 private final VirtualSensor mGyroscope = new VirtualSensor( 61 SENSOR_HANDLE + 1, TYPE_GYROSCOPE, GYROSCOPE_SENSOR_NAME); 62 63 private final VirtualSensorDirectChannelWriter mWriter = new VirtualSensorDirectChannelWriter(); 64 65 @After tearDown()66 public void tearDown() { 67 mWriter.close(); 68 } 69 70 @Test configureChannel_rateNormal_noChannelAdded_returnsError()71 public void configureChannel_rateNormal_noChannelAdded_returnsError() { 72 assertThat( 73 mWriter.configureChannel(CHANNEL_HANDLE, mAccelerometer, RATE_NORMAL, REPORT_TOKEN)) 74 .isFalse(); 75 } 76 77 @Test configureChannel_rateStop_noChannelAdded_returnsError()78 public void configureChannel_rateStop_noChannelAdded_returnsError() { 79 assertThat( 80 mWriter.configureChannel(CHANNEL_HANDLE, mAccelerometer, RATE_STOP, REPORT_TOKEN)) 81 .isFalse(); 82 } 83 84 @Test addChannel_nullSharedMemory_throwsException()85 public void addChannel_nullSharedMemory_throwsException() { 86 assertThrows( 87 NullPointerException.class, 88 () -> mWriter.addChannel(CHANNEL_HANDLE, /*sharedMemory=*/null)); 89 } 90 91 @Test removeChannel_closesSharedMemory()92 public void removeChannel_closesSharedMemory() throws Exception { 93 SharedMemory sharedMemory = SharedMemory.create(SHARED_MEMORY_NAME, SHARED_MEMORY_SIZE); 94 assertThat(sharedMemory.getSize()).isGreaterThan(0); 95 96 mWriter.addChannel(CHANNEL_HANDLE, sharedMemory); 97 mWriter.removeChannel(CHANNEL_HANDLE); 98 99 // The shared memory's file descriptor is closed. 100 assertThrows(IllegalStateException.class, sharedMemory::getSize); 101 } 102 103 @Test writeEvent_nullEvent_throwsException()104 public void writeEvent_nullEvent_throwsException() { 105 assertThrows( 106 NullPointerException.class, 107 () -> mWriter.writeSensorEvent(mAccelerometer, /*event=*/null)); 108 } 109 110 @Test writeEvent_nullSensor_throwsException()111 public void writeEvent_nullSensor_throwsException() { 112 assertThrows( 113 NullPointerException.class, 114 () -> mWriter.writeSensorEvent(/*sensor=*/null, 115 new VirtualSensorEvent.Builder(new float[]{1, 2, 3}).build())); 116 } 117 118 @Test writeEvent_noConfiguredChannel_returnsError()119 public void writeEvent_noConfiguredChannel_returnsError() { 120 assertThat(mWriter.writeSensorEvent( 121 mAccelerometer, new VirtualSensorEvent.Builder(new float[]{1, 2, 3}).build())) 122 .isFalse(); 123 } 124 125 @Test writeEvent_channelStopped_returnsError()126 public void writeEvent_channelStopped_returnsError() throws Exception { 127 SharedMemory sharedMemory = SharedMemory.create(SHARED_MEMORY_NAME, SHARED_MEMORY_SIZE); 128 mWriter.addChannel(CHANNEL_HANDLE, sharedMemory); 129 130 assertThat( 131 mWriter.configureChannel(CHANNEL_HANDLE, mAccelerometer, RATE_NORMAL, REPORT_TOKEN)) 132 .isTrue(); 133 assertThat( 134 mWriter.configureChannel(CHANNEL_HANDLE, mAccelerometer, RATE_STOP, REPORT_TOKEN)) 135 .isTrue(); 136 137 assertThat(mWriter.writeSensorEvent( 138 mAccelerometer, new VirtualSensorEvent.Builder(new float[]{1, 2, 3}).build())) 139 .isFalse(); 140 } 141 142 @Test writeEvent_channelRemoved_returnsError()143 public void writeEvent_channelRemoved_returnsError() throws Exception { 144 SharedMemory sharedMemory = SharedMemory.create(SHARED_MEMORY_NAME, SHARED_MEMORY_SIZE); 145 mWriter.addChannel(CHANNEL_HANDLE, sharedMemory); 146 assertThat( 147 mWriter.configureChannel(CHANNEL_HANDLE, mAccelerometer, RATE_NORMAL, REPORT_TOKEN)) 148 .isTrue(); 149 mWriter.removeChannel(CHANNEL_HANDLE); 150 151 assertThat(mWriter.writeSensorEvent( 152 mAccelerometer, new VirtualSensorEvent.Builder(new float[]{1, 2, 3}).build())) 153 .isFalse(); 154 } 155 156 @Test writeEvent_singleChannel()157 public void writeEvent_singleChannel() throws Exception { 158 testWriteSensorEvents( 159 List.of(new SharedMemoryWrapper(CHANNEL_HANDLE, SHARED_MEMORY_SIZE)), 160 List.of(mAccelerometer)); 161 } 162 163 @Test writeEvent_multipleChannels()164 public void writeEvent_multipleChannels() throws Exception { 165 testWriteSensorEvents( 166 List.of( 167 new SharedMemoryWrapper(CHANNEL_HANDLE, SHARED_MEMORY_SIZE), 168 new SharedMemoryWrapper(CHANNEL_HANDLE + 1, SHARED_MEMORY_SIZE * 2)), 169 List.of(mAccelerometer)); 170 } 171 172 @Test writeEvent_multipleSensors_singleChannel()173 public void writeEvent_multipleSensors_singleChannel() throws Exception { 174 testWriteSensorEvents( 175 List.of(new SharedMemoryWrapper(CHANNEL_HANDLE, SHARED_MEMORY_SIZE)), 176 List.of(mAccelerometer, mGyroscope)); 177 } 178 179 @Test writeEvent_multipleSensors_multipleChannels()180 public void writeEvent_multipleSensors_multipleChannels() throws Exception { 181 testWriteSensorEvents( 182 List.of( 183 new SharedMemoryWrapper(CHANNEL_HANDLE, SHARED_MEMORY_SIZE), 184 new SharedMemoryWrapper(CHANNEL_HANDLE + 1, SHARED_MEMORY_SIZE * 2)), 185 List.of(mAccelerometer, mGyroscope)); 186 } 187 testWriteSensorEvents( List<SharedMemoryWrapper> channels, List<VirtualSensor> sensors)188 private void testWriteSensorEvents( 189 List<SharedMemoryWrapper> channels, List<VirtualSensor> sensors) { 190 for (SharedMemoryWrapper channel : channels) { 191 for (VirtualSensor sensor : sensors) { 192 assertThat( 193 mWriter.configureChannel( 194 channel.mChannelHandle, sensor, RATE_NORMAL, 195 /*reportToken=*/sensor.getHandle())) 196 .isTrue(); 197 } 198 } 199 200 for (int i = 0; i < 8; ++i) { 201 VirtualSensorEvent event = new VirtualSensorEvent.Builder(new float[]{i, i + 1, i + 2}) 202 .setTimestampNanos(System.nanoTime()) 203 .build(); 204 205 for (VirtualSensor sensor : sensors) { 206 int sensorIndex = sensors.indexOf(sensor) + 1; 207 if (i % sensorIndex == 0) { 208 assertThat(mWriter.writeSensorEvent(sensor, event)).isTrue(); 209 210 for (SharedMemoryWrapper channel : channels) { 211 channel.verifyEvent(sensor, event, /*eventCount=*/i / sensorIndex + 1); 212 } 213 } 214 } 215 } 216 } 217 218 private final class SharedMemoryWrapper { 219 private final int mChannelHandle; 220 private final SharedMemory mSharedMemory; 221 private final ByteBuffer mByteBuffer; 222 private int mReadOffset = 0; 223 SharedMemoryWrapper(int channelHandle, int size)224 SharedMemoryWrapper(int channelHandle, int size) throws Exception { 225 mChannelHandle = channelHandle; 226 mSharedMemory = SharedMemory.create(SHARED_MEMORY_NAME, size); 227 mByteBuffer = mSharedMemory.mapReadOnly(); 228 mByteBuffer.order(ByteOrder.nativeOrder()); 229 230 mWriter.addChannel(mChannelHandle, mSharedMemory); 231 } 232 verifyEvent(VirtualSensor sensor, VirtualSensorEvent event, int eventCount)233 void verifyEvent(VirtualSensor sensor, VirtualSensorEvent event, int eventCount) { 234 mByteBuffer.position(mReadOffset); 235 assertThat(mByteBuffer.getInt()).isEqualTo(SENSOR_EVENT_SIZE); 236 assertThat(mByteBuffer.getInt()).isEqualTo(/*reportToken=*/sensor.getHandle()); 237 assertThat(mByteBuffer.getInt()).isEqualTo(sensor.getType()); 238 assertThat(mByteBuffer.getInt()).isEqualTo(eventCount); 239 assertThat(mByteBuffer.getLong()).isEqualTo(event.getTimestampNanos()); 240 for (int i = 0; i < 16; ++i) { 241 if (i < event.getValues().length) { 242 assertThat(mByteBuffer.getFloat()).isEqualTo(event.getValues()[i]); 243 } else { 244 assertThat(mByteBuffer.getFloat()).isEqualTo(0); 245 } 246 } 247 assertThat(mByteBuffer.getInt()).isEqualTo(0); 248 249 mReadOffset += SENSOR_EVENT_SIZE; 250 if (mReadOffset + SENSOR_EVENT_SIZE >= mSharedMemory.getSize()) { 251 mReadOffset = 0; 252 } 253 } 254 255 } 256 } 257