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