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.companion.virtual.sensor; 18 19 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.hardware.SensorDirectChannel; 24 import android.os.SharedMemory; 25 import android.system.ErrnoException; 26 import android.util.Log; 27 import android.util.SparseArray; 28 29 import com.android.internal.annotations.GuardedBy; 30 31 import java.nio.ByteBuffer; 32 import java.nio.ByteOrder; 33 import java.util.Objects; 34 import java.util.concurrent.atomic.AtomicLong; 35 36 /** 37 * Helper class for writing sensor events to the relevant configured direct channels. 38 * 39 * <p>The virtual device owner can forward the {@link VirtualSensorDirectChannelCallback} 40 * invocations to a {@link VirtualSensorDirectChannelWriter} instance and use that writer to 41 * write the events from the relevant sensors directly to the shared memory regions of the 42 * corresponding {@link SensorDirectChannel} instances. 43 * 44 * <p>Example: 45 * <p>During sensor and virtual device creation: 46 * <pre> 47 * VirtualSensorDirectChannelWriter writer = new VirtualSensorDirectChannelWriter(); 48 * VirtualSensorDirectChannelCallback callback = new VirtualSensorDirectChannelCallback() { 49 * @Override 50 * public void onDirectChannelCreated(int channelHandle, SharedMemory sharedMemory) { 51 * writer.addChannel(channelHandle, sharedMemory); 52 * } 53 * @Override 54 * public void onDirectChannelDestroyed(int channelHandle); 55 * writer.removeChannel(channelHandle); 56 * } 57 * @Override 58 * public void onDirectChannelConfigured(int channelHandle, VirtualSensor sensor, int rateLevel, 59 * int reportToken) 60 * if (!writer.configureChannel(channelHandle, sensor, rateLevel, reportToken)) { 61 * // handle error 62 * } 63 * } 64 * } 65 * </pre> 66 * <p>During the virtual device lifetime: 67 * <pre> 68 * VirtualSensor sensor = ... 69 * while (shouldInjectEvents(sensor)) { 70 * if (!writer.writeSensorEvent(sensor, event)) { 71 * // handle error 72 * } 73 * } 74 * writer.close(); 75 * </pre> 76 * <p>Note that the virtual device owner should take the currently configured rate level into 77 * account when deciding whether and how often to inject events for a particular sensor. 78 * 79 * @see android.hardware.SensorDirectChannel#configure 80 * @see VirtualSensorDirectChannelCallback 81 * 82 * @hide 83 */ 84 @SystemApi 85 public final class VirtualSensorDirectChannelWriter implements AutoCloseable { 86 87 private static final String TAG = "VirtualSensorWriter"; 88 89 private static final long UINT32_MAX = 4294967295L; 90 91 // Mapping from channel handle to channel shared memory region. 92 @GuardedBy("mChannelsLock") 93 private final SparseArray<SharedMemoryWrapper> mChannels = new SparseArray<>(); 94 private final Object mChannelsLock = new Object(); 95 96 // Mapping from sensor handle to channel handle to direct sensor configuration. 97 @GuardedBy("mChannelsLock") 98 private final SparseArray<SparseArray<DirectChannelConfiguration>> mConfiguredChannels = 99 new SparseArray<>(); 100 101 @Override close()102 public void close() { 103 synchronized (mChannelsLock) { 104 for (int i = 0; i < mChannels.size(); ++i) { 105 mChannels.valueAt(i).close(); 106 } 107 mChannels.clear(); 108 mConfiguredChannels.clear(); 109 } 110 } 111 112 /** 113 * Adds a sensor direct channel handle and the relevant shared memory region. 114 * 115 * @throws ErrnoException if the mapping of the shared memory region failed. 116 * 117 * @see VirtualSensorDirectChannelCallback#onDirectChannelCreated 118 */ addChannel(@ntRangefrom = 1) int channelHandle, @NonNull SharedMemory sharedMemory)119 public void addChannel(@IntRange(from = 1) int channelHandle, 120 @NonNull SharedMemory sharedMemory) throws ErrnoException { 121 synchronized (mChannelsLock) { 122 if (mChannels.contains(channelHandle)) { 123 Log.w(TAG, "Channel with handle " + channelHandle + " already added."); 124 } else { 125 mChannels.put(channelHandle, 126 new SharedMemoryWrapper(Objects.requireNonNull(sharedMemory))); 127 } 128 } 129 } 130 131 /** 132 * Removes a sensor direct channel indicated by the handle and closes the relevant shared memory 133 * region. 134 * 135 * @see VirtualSensorDirectChannelCallback#onDirectChannelDestroyed 136 */ removeChannel(@ntRangefrom = 1) int channelHandle)137 public void removeChannel(@IntRange(from = 1) int channelHandle) { 138 synchronized (mChannelsLock) { 139 SharedMemoryWrapper sharedMemoryWrapper = mChannels.removeReturnOld(channelHandle); 140 if (sharedMemoryWrapper != null) { 141 sharedMemoryWrapper.close(); 142 } 143 for (int i = 0; i < mConfiguredChannels.size(); ++i) { 144 mConfiguredChannels.valueAt(i).remove(channelHandle); 145 } 146 } 147 } 148 149 /** 150 * Configures a sensor direct channel indicated by the handle and prepares it for sensor event 151 * writes for the given sensor. 152 * 153 * @return Whether the configuration was successful. 154 * 155 * @see VirtualSensorDirectChannelCallback#onDirectChannelConfigured 156 */ configureChannel(@ntRangefrom = 1) int channelHandle, @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, @IntRange(from = 1) int reportToken)157 public boolean configureChannel(@IntRange(from = 1) int channelHandle, 158 @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, 159 @IntRange(from = 1) int reportToken) { 160 synchronized (mChannelsLock) { 161 SparseArray<DirectChannelConfiguration> configs = mConfiguredChannels.get( 162 Objects.requireNonNull(sensor).getHandle()); 163 if (rateLevel == SensorDirectChannel.RATE_STOP) { 164 if (configs == null || configs.removeReturnOld(channelHandle) == null) { 165 Log.w(TAG, "Channel configuration failed - channel with handle " 166 + channelHandle + " not found"); 167 return false; 168 } 169 return true; 170 } 171 172 if (configs == null) { 173 configs = new SparseArray<>(); 174 mConfiguredChannels.put(sensor.getHandle(), configs); 175 } 176 177 SharedMemoryWrapper sharedMemoryWrapper = mChannels.get(channelHandle); 178 if (sharedMemoryWrapper == null) { 179 Log.w(TAG, "Channel configuration failed - channel with handle " 180 + channelHandle + " not found"); 181 return false; 182 } 183 configs.put(channelHandle, new DirectChannelConfiguration( 184 reportToken, sensor.getType(), sharedMemoryWrapper)); 185 return true; 186 } 187 } 188 189 /** 190 * Writes a sensor event for the given sensor to all configured sensor direct channels for that 191 * sensor. 192 * 193 * @return Whether the write was successful. 194 * 195 */ writeSensorEvent(@onNull VirtualSensor sensor, @NonNull VirtualSensorEvent event)196 public boolean writeSensorEvent(@NonNull VirtualSensor sensor, 197 @NonNull VirtualSensorEvent event) { 198 Objects.requireNonNull(event); 199 synchronized (mChannelsLock) { 200 SparseArray<DirectChannelConfiguration> configs = mConfiguredChannels.get( 201 Objects.requireNonNull(sensor).getHandle()); 202 if (configs == null || configs.size() == 0) { 203 Log.w(TAG, "Sensor event write failed - no direct sensor channels configured for " 204 + "sensor " + sensor.getName()); 205 return false; 206 } 207 208 for (int i = 0; i < configs.size(); ++i) { 209 configs.valueAt(i).write(Objects.requireNonNull(event)); 210 } 211 } 212 return true; 213 } 214 215 private static final class SharedMemoryWrapper { 216 217 private static final int SENSOR_EVENT_SIZE = 104; 218 219 // The limit of number of values for a single sensor event. 220 private static final int MAXIMUM_NUMBER_OF_SENSOR_VALUES = 16; 221 222 @GuardedBy("mWriteLock") 223 private final SharedMemory mSharedMemory; 224 @GuardedBy("mWriteLock") 225 private int mWriteOffset = 0; 226 @GuardedBy("mWriteLock") 227 private final ByteBuffer mEventBuffer = ByteBuffer.allocate(SENSOR_EVENT_SIZE); 228 @GuardedBy("mWriteLock") 229 private final ByteBuffer mMemoryMapping; 230 private final Object mWriteLock = new Object(); 231 SharedMemoryWrapper(SharedMemory sharedMemory)232 SharedMemoryWrapper(SharedMemory sharedMemory) throws ErrnoException { 233 mSharedMemory = sharedMemory; 234 mMemoryMapping = mSharedMemory.mapReadWrite(); 235 mEventBuffer.order(ByteOrder.nativeOrder()); 236 } 237 close()238 void close() { 239 synchronized (mWriteLock) { 240 mSharedMemory.close(); 241 } 242 } 243 write(int reportToken, int sensorType, long eventCounter, VirtualSensorEvent event)244 void write(int reportToken, int sensorType, long eventCounter, VirtualSensorEvent event) { 245 synchronized (mWriteLock) { 246 mEventBuffer.position(0); 247 mEventBuffer.putInt(SENSOR_EVENT_SIZE); 248 mEventBuffer.putInt(reportToken); 249 mEventBuffer.putInt(sensorType); 250 mEventBuffer.putInt((int) (eventCounter & UINT32_MAX)); 251 mEventBuffer.putLong(event.getTimestampNanos()); 252 253 for (int i = 0; i < MAXIMUM_NUMBER_OF_SENSOR_VALUES; ++i) { 254 if (i < event.getValues().length) { 255 mEventBuffer.putFloat(event.getValues()[i]); 256 } else { 257 mEventBuffer.putFloat(0f); 258 } 259 } 260 mEventBuffer.putInt(0); 261 262 mMemoryMapping.position(mWriteOffset); 263 mMemoryMapping.put(mEventBuffer.array(), 0, SENSOR_EVENT_SIZE); 264 265 mWriteOffset += SENSOR_EVENT_SIZE; 266 if (mWriteOffset + SENSOR_EVENT_SIZE >= mSharedMemory.getSize()) { 267 mWriteOffset = 0; 268 } 269 } 270 } 271 } 272 273 private static final class DirectChannelConfiguration { 274 private final int mReportToken; 275 private final int mSensorType; 276 private final AtomicLong mEventCounter; 277 private final SharedMemoryWrapper mSharedMemoryWrapper; 278 DirectChannelConfiguration(int reportToken, int sensorType, SharedMemoryWrapper sharedMemoryWrapper)279 DirectChannelConfiguration(int reportToken, int sensorType, 280 SharedMemoryWrapper sharedMemoryWrapper) { 281 mReportToken = reportToken; 282 mSensorType = sensorType; 283 mEventCounter = new AtomicLong(1); 284 mSharedMemoryWrapper = sharedMemoryWrapper; 285 } 286 write(VirtualSensorEvent event)287 void write(VirtualSensorEvent event) { 288 long currentCounter = mEventCounter.getAcquire(); 289 mSharedMemoryWrapper.write(mReportToken, mSensorType, currentCounter++, event); 290 if (currentCounter == UINT32_MAX + 1) { 291 currentCounter = 1; 292 } 293 mEventCounter.setRelease(currentCounter); 294 } 295 } 296 } 297