diff options
| author | 2023-03-24 13:20:06 +0000 | |
|---|---|---|
| committer | 2023-03-24 13:20:06 +0000 | |
| commit | 96bc9ff0b8171c20cbcd191592de2f13bfbf3aae (patch) | |
| tree | 5b9b411ca07f156ff37d339b22239be8b234a2e6 | |
| parent | 8ea6ef0c25c30f14c54bbd30788b8750f0389684 (diff) | |
| parent | 01b0fccd7056ad63b273a7fb0c011237073624c8 (diff) | |
Merge "Split VirtualSensor callback." into udc-dev
5 files changed, 459 insertions, 95 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6177a9f0500f..4ed7b5c61321 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3263,6 +3263,7 @@ package android.companion.virtual { method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorDirectChannelCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorDirectChannelCallback); } } @@ -3326,9 +3327,6 @@ package android.companion.virtual.sensor { public interface VirtualSensorCallback { method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); - method public default void onDirectChannelConfigured(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int); - method public default void onDirectChannelCreated(@IntRange(from=1) int, @NonNull android.os.SharedMemory); - method public default void onDirectChannelDestroyed(@IntRange(from=1) int); } public final class VirtualSensorConfig implements android.os.Parcelable { @@ -3350,6 +3348,21 @@ package android.companion.virtual.sensor { method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String); } + public interface VirtualSensorDirectChannelCallback { + method public void onDirectChannelConfigured(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int); + method public void onDirectChannelCreated(@IntRange(from=1) int, @NonNull android.os.SharedMemory); + method public void onDirectChannelDestroyed(@IntRange(from=1) int); + } + + public final class VirtualSensorDirectChannelWriter implements java.lang.AutoCloseable { + ctor public VirtualSensorDirectChannelWriter(); + method public void addChannel(@IntRange(from=1) int, @NonNull android.os.SharedMemory) throws android.system.ErrnoException; + method public void close(); + method public boolean configureChannel(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int); + method public void removeChannel(@IntRange(from=1) int); + method public boolean writeSensorEvent(@NonNull android.companion.virtual.sensor.VirtualSensor, @NonNull android.companion.virtual.sensor.VirtualSensorEvent); + } + public final class VirtualSensorEvent implements android.os.Parcelable { method public int describeContents(); method public long getTimestampNanos(); diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 3a60a695a294..9a34dbe2699c 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -32,6 +32,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensorConfig; +import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; @@ -381,7 +382,8 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Returns the callback to get notified about changes in the sensor listeners. + * Returns the callback to get notified about changes in the sensor listeners or sensor direct + * channel configuration. * @hide */ @Nullable @@ -533,19 +535,29 @@ public final class VirtualDeviceParams implements Parcelable { private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE; @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>(); - @Nullable - private IVirtualSensorCallback mVirtualSensorCallback; + @Nullable private Executor mVirtualSensorCallbackExecutor; + @Nullable private VirtualSensorCallback mVirtualSensorCallback; + @Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor; + @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback; private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { @NonNull private final Executor mExecutor; @NonNull private final VirtualSensorCallback mCallback; + @Nullable + private final Executor mDirectChannelExecutor; + @Nullable + private final VirtualSensorDirectChannelCallback mDirectChannelCallback; VirtualSensorCallbackDelegate(@NonNull @CallbackExecutor Executor executor, - @NonNull VirtualSensorCallback callback) { - mCallback = callback; + @NonNull VirtualSensorCallback callback, + @Nullable @CallbackExecutor Executor directChannelExecutor, + @Nullable VirtualSensorDirectChannelCallback directChannelCallback) { mExecutor = executor; + mCallback = callback; + mDirectChannelExecutor = directChannelExecutor; + mDirectChannelCallback = directChannelCallback; } @Override @@ -562,20 +574,29 @@ public final class VirtualDeviceParams implements Parcelable { @Override public void onDirectChannelCreated(int channelHandle, @NonNull SharedMemory sharedMemory) { - mExecutor.execute( - () -> mCallback.onDirectChannelCreated(channelHandle, sharedMemory)); + if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { + mDirectChannelExecutor.execute( + () -> mDirectChannelCallback.onDirectChannelCreated(channelHandle, + sharedMemory)); + } } @Override public void onDirectChannelDestroyed(int channelHandle) { - mExecutor.execute(() -> mCallback.onDirectChannelDestroyed(channelHandle)); + if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { + mDirectChannelExecutor.execute( + () -> mDirectChannelCallback.onDirectChannelDestroyed(channelHandle)); + } } @Override public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor, int rateLevel, int reportToken) { - mExecutor.execute(() -> mCallback.onDirectChannelConfigured( - channelHandle, sensor, rateLevel, reportToken)); + if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { + mDirectChannelExecutor.execute( + () -> mDirectChannelCallback.onDirectChannelConfigured( + channelHandle, sensor, rateLevel, reportToken)); + } } } @@ -783,20 +804,37 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Sets the callback to get notified about changes in the sensor listeners. + * Sets the callback to get notified about changes in the sensor configuration. * * @param executor The executor where the callback is executed on. * @param callback The callback to get notified when the state of the sensor - * listeners has changed, see {@link VirtualSensorCallback} + * configuration has changed, see {@link VirtualSensorCallback} */ @SuppressLint("MissingGetterMatchingBuilder") @NonNull public Builder setVirtualSensorCallback( @NonNull @CallbackExecutor Executor executor, @NonNull VirtualSensorCallback callback) { - mVirtualSensorCallback = new VirtualSensorCallbackDelegate( - Objects.requireNonNull(executor), - Objects.requireNonNull(callback)); + mVirtualSensorCallbackExecutor = Objects.requireNonNull(executor); + mVirtualSensorCallback = Objects.requireNonNull(callback); + return this; + } + + /** + * Sets the callback to get notified about changes in + * {@link android.hardware.SensorDirectChannel} configuration. + * + * @param executor The executor where the callback is executed on. + * @param callback The callback to get notified when the state of the sensor + * configuration has changed, see {@link VirtualSensorDirectChannelCallback} + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setVirtualSensorDirectChannelCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull VirtualSensorDirectChannelCallback callback) { + mVirtualSensorDirectChannelCallbackExecutor = Objects.requireNonNull(executor); + mVirtualSensorDirectChannelCallback = Objects.requireNonNull(callback); return this; } @@ -857,6 +895,7 @@ public final class VirtualDeviceParams implements Parcelable { */ @NonNull public VirtualDeviceParams build() { + VirtualSensorCallbackDelegate virtualSensorCallbackDelegate = null; if (!mVirtualSensorConfigs.isEmpty()) { if (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT) != DEVICE_POLICY_CUSTOM) { @@ -868,6 +907,22 @@ public final class VirtualDeviceParams implements Parcelable { throw new IllegalArgumentException( "VirtualSensorCallback is required for creating virtual sensors."); } + + for (int i = 0; i < mVirtualSensorConfigs.size(); ++i) { + if (mVirtualSensorConfigs.get(i).getDirectChannelTypesSupported() > 0) { + if (mVirtualSensorDirectChannelCallback == null) { + throw new IllegalArgumentException( + "VirtualSensorDirectChannelCallback is required for creating " + + "virtual sensors that support direct channel."); + } + break; + } + } + virtualSensorCallbackDelegate = new VirtualSensorCallbackDelegate( + mVirtualSensorCallbackExecutor, + mVirtualSensorCallback, + mVirtualSensorDirectChannelCallbackExecutor, + mVirtualSensorDirectChannelCallback); } if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE @@ -901,7 +956,7 @@ public final class VirtualDeviceParams implements Parcelable { mName, mDevicePolicies, mVirtualSensorConfigs, - mVirtualSensorCallback, + virtualSensorCallbackDelegate, mAudioPlaybackSessionId, mAudioRecordingSessionId); } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java index f7af283a749b..e6bd6daa060f 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java @@ -17,18 +17,14 @@ package android.companion.virtual.sensor; -import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.hardware.Sensor; -import android.hardware.SensorDirectChannel; -import android.os.MemoryFile; -import android.os.SharedMemory; import java.time.Duration; /** - * Interface for notifying the sensor owner about whether and how sensor events should be injected. + * Interface for notifying the virtual device owner about whether and how sensor events should be + * injected. * * <p>This callback can be used for controlling the sensor event injection - e.g. if the sensor is * not enabled, then no events should be injected. Similarly, the rate and delay of the injected @@ -45,6 +41,7 @@ public interface VirtualSensorCallback { * Called when the requested sensor event injection parameters have changed. * * <p>This is effectively called when the registered listeners to a virtual sensor have changed. + * The events for the corresponding sensor should be sent via {@link VirtualSensor#sendEvent}. * * @param sensor The sensor whose requested injection parameters have changed. * @param enabled Whether the sensor is enabled. True if any listeners are currently registered, @@ -55,74 +52,4 @@ public interface VirtualSensorCallback { */ void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency); - - /** - * Called when a {@link android.hardware.SensorDirectChannel} is created. - * - * <p>The {@link android.hardware.SensorManager} instance used to create the direct channel must - * be associated with the virtual device. - * - * <p>A typical order of callback invocations is: - * <ul> - * <li>{@code onDirectChannelCreated} - the channel handle and the associated shared memory - * should be stored by the virtual device</li> - * <li>{@code onDirectChannelConfigured} with a positive {@code rateLevel} - the virtual - * device should start writing to the shared memory for the associated channel with the - * requested parameters.</li> - * <li>{@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual - * device should stop writing to the shared memory for the associated channel.</li> - * <li>{@code onDirectChannelDestroyed} - the shared memory associated with the channel - * handle should be closed.</li> - * </ul> - * - * @param channelHandle Identifier of the newly created channel. - * @param sharedMemory writable shared memory region. - * - * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) - * @see #onDirectChannelConfigured - * @see #onDirectChannelDestroyed - */ - default void onDirectChannelCreated(@IntRange(from = 1) int channelHandle, - @NonNull SharedMemory sharedMemory) {} - - /** - * Called when a {@link android.hardware.SensorDirectChannel} is destroyed. - * - * <p>The virtual device must perform any clean-up and close the shared memory that was - * received with the {@link #onDirectChannelCreated} callback and the corresponding - * {@code channelHandle}. - * - * @param channelHandle Identifier of the channel that was destroyed. - * - * @see SensorDirectChannel#close() - */ - default void onDirectChannelDestroyed(@IntRange(from = 1) int channelHandle) {} - - /** - * Called when a {@link android.hardware.SensorDirectChannel} is configured. - * - * <p>Sensor events for the corresponding sensor should be written at the indicated rate to the - * shared memory region that was received with the {@link #onDirectChannelCreated} callback and - * the corresponding {@code channelHandle}. The events should be written in the correct format - * and with the provided {@code reportToken} until the channel is reconfigured with - * {@link SensorDirectChannel#RATE_STOP}. - * - * <p>The sensor must support direct channel in order for this callback to be invoked. Only - * {@link MemoryFile} sensor direct channels are supported for virtual sensors. - * - * @param channelHandle Identifier of the channel that was configured. - * @param sensor The sensor, for which the channel was configured. - * @param rateLevel The rate level used to configure the direct sensor channel. - * @param reportToken A positive sensor report token, used to differentiate between events from - * different sensors within the same channel. - * - * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int) - * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int) - * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) - * @see #onDirectChannelCreated - * @see SensorDirectChannel#configure(Sensor, int) - */ - default void onDirectChannelConfigured(@IntRange(from = 1) int channelHandle, - @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, - @IntRange(from = 1) int reportToken) {} } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java new file mode 100644 index 000000000000..d352f94ffd76 --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.hardware.Sensor; +import android.hardware.SensorDirectChannel; +import android.os.MemoryFile; +import android.os.SharedMemory; + +/** + * Interface for notifying the virtual device owner about any {@link SensorDirectChannel} events. + * + * <p>This callback can be used for controlling the sensor event injection to direct channels. A + * typical order of callback invocations is: + * <ul> + * <li>{@code onDirectChannelCreated} - the channel handle and the associated shared memory + * should be stored by the virtual device</li> + * <li>{@code onDirectChannelConfigured} with a positive {@code rateLevel} - the virtual + * device should start writing to the shared memory for the associated channel with the + * requested parameters.</li> + * <li>{@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual + * device should stop writing to the shared memory for the associated channel.</li> + * <li>{@code onDirectChannelDestroyed} - the shared memory associated with the channel + * handle should be closed.</li> + * </ul> + * + * <p>The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when + * the device is created and destroyed when the device is destroyed. + * + * @hide + */ +@SystemApi +public interface VirtualSensorDirectChannelCallback { + /** + * Called when a {@link android.hardware.SensorDirectChannel} is created. + * + * <p>The {@link android.hardware.SensorManager} instance used to create the direct channel must + * be associated with the virtual device. + * + * @param channelHandle Identifier of the newly created channel. + * @param sharedMemory writable shared memory region. + * + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelConfigured + * @see #onDirectChannelDestroyed + */ + void onDirectChannelCreated(@IntRange(from = 1) int channelHandle, + @NonNull SharedMemory sharedMemory); + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is destroyed. + * + * <p>The virtual device must perform any clean-up and close the shared memory that was + * received with the {@link #onDirectChannelCreated} callback and the corresponding + * {@code channelHandle}. + * + * @param channelHandle Identifier of the channel that was destroyed. + * + * @see SensorDirectChannel#close() + */ + void onDirectChannelDestroyed(@IntRange(from = 1) int channelHandle); + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is configured. + * + * <p>Sensor events for the corresponding sensor should be written at the indicated rate to the + * shared memory region that was received with the {@link #onDirectChannelCreated} callback and + * the corresponding {@code channelHandle}. The events should be written in the correct format + * and with the provided {@code reportToken} until the channel is reconfigured with + * {@link SensorDirectChannel#RATE_STOP}. + * + * <p>The sensor must support direct channel in order for this callback to be invoked. Only + * {@link MemoryFile} sensor direct channels are supported for virtual sensors. + * + * @param channelHandle Identifier of the channel that was configured. + * @param sensor The sensor, for which the channel was configured. + * @param rateLevel The rate level used to configure the direct sensor channel. + * @param reportToken A positive sensor report token, used to differentiate between events from + * different sensors within the same channel. + * + * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int) + * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int) + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelCreated + * @see SensorDirectChannel#configure(Sensor, int) + */ + void onDirectChannelConfigured(@IntRange(from = 1) int channelHandle, + @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, + @IntRange(from = 1) int reportToken); +} diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java new file mode 100644 index 000000000000..6aed96ff593e --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.hardware.SensorDirectChannel; +import android.os.SharedMemory; +import android.system.ErrnoException; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Helper class for writing sensor events to the relevant configured direct channels. + * + * <p>The virtual device owner can forward the {@link VirtualSensorDirectChannelCallback} + * invocations to a {@link VirtualSensorDirectChannelWriter} instance and use that writer to + * write the events from the relevant sensors directly to the shared memory regions of the + * corresponding {@link SensorDirectChannel} instances. + * + * @see android.hardware.SensorDirectChannel#configure + * @see VirtualSensorDirectChannelCallback + * + * @hide + */ +@SystemApi +public final class VirtualSensorDirectChannelWriter implements AutoCloseable { + + private static final String TAG = "VirtualSensorWriter"; + + private static final long UINT32_MAX = 4294967295L; + + // Mapping from channel handle to channel shared memory region. + @GuardedBy("mChannelsLock") + private final SparseArray<SharedMemoryWrapper> mChannels = new SparseArray<>(); + private final Object mChannelsLock = new Object(); + + // Mapping from sensor handle to channel handle to direct sensor configuration. + @GuardedBy("mChannelsLock") + private final SparseArray<SparseArray<DirectChannelConfiguration>> mConfiguredChannels = + new SparseArray<>(); + + @Override + public void close() { + synchronized (mChannelsLock) { + for (int i = 0; i < mChannels.size(); ++i) { + mChannels.valueAt(i).close(); + } + mChannels.clear(); + mConfiguredChannels.clear(); + } + } + + /** + * Adds a sensor direct channel handle and the relevant shared memory region. + * + * @throws ErrnoException if the mapping of the shared memory region failed. + * + * @see VirtualSensorDirectChannelCallback#onDirectChannelCreated + */ + public void addChannel(@IntRange(from = 1) int channelHandle, + @NonNull SharedMemory sharedMemory) throws ErrnoException { + synchronized (mChannelsLock) { + if (mChannels.contains(channelHandle)) { + Log.w(TAG, "Channel with handle " + channelHandle + " already added."); + } else { + mChannels.put(channelHandle, + new SharedMemoryWrapper(Objects.requireNonNull(sharedMemory))); + } + } + } + + /** + * Removes a sensor direct channel indicated by the handle and closes the relevant shared memory + * region. + * + * @see VirtualSensorDirectChannelCallback#onDirectChannelDestroyed + */ + public void removeChannel(@IntRange(from = 1) int channelHandle) { + synchronized (mChannelsLock) { + SharedMemoryWrapper sharedMemoryWrapper = mChannels.removeReturnOld(channelHandle); + if (sharedMemoryWrapper != null) { + sharedMemoryWrapper.close(); + } + for (int i = 0; i < mConfiguredChannels.size(); ++i) { + mConfiguredChannels.valueAt(i).remove(channelHandle); + } + } + } + + /** + * Configures a sensor direct channel indicated by the handle and prepares it for sensor event + * writes for the given sensor. + * + * @return Whether the configuration was successful. + * + * @see VirtualSensorDirectChannelCallback#onDirectChannelConfigured + */ + public boolean configureChannel(@IntRange(from = 1) int channelHandle, + @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, + @IntRange(from = 1) int reportToken) { + synchronized (mChannelsLock) { + SparseArray<DirectChannelConfiguration> configs = mConfiguredChannels.get( + Objects.requireNonNull(sensor).getHandle()); + if (rateLevel == SensorDirectChannel.RATE_STOP) { + if (configs == null || configs.removeReturnOld(channelHandle) == null) { + Log.w(TAG, "Channel configuration failed - channel with handle " + + channelHandle + " not found"); + return false; + } + return true; + } + + if (configs == null) { + configs = new SparseArray<>(); + mConfiguredChannels.put(sensor.getHandle(), configs); + } + + SharedMemoryWrapper sharedMemoryWrapper = mChannels.get(channelHandle); + if (sharedMemoryWrapper == null) { + Log.w(TAG, "Channel configuration failed - channel with handle " + + channelHandle + " not found"); + return false; + } + configs.put(channelHandle, new DirectChannelConfiguration( + reportToken, sensor.getType(), sharedMemoryWrapper)); + return true; + } + } + + /** + * Writes a sensor event for the given sensor to all configured sensor direct channels for that + * sensor. + * + * @return Whether the write was successful. + * + */ + public boolean writeSensorEvent(@NonNull VirtualSensor sensor, + @NonNull VirtualSensorEvent event) { + Objects.requireNonNull(event); + synchronized (mChannelsLock) { + SparseArray<DirectChannelConfiguration> configs = mConfiguredChannels.get( + Objects.requireNonNull(sensor).getHandle()); + if (configs == null || configs.size() == 0) { + Log.w(TAG, "Sensor event write failed - no direct sensor channels configured for " + + "sensor " + sensor.getName()); + return false; + } + + for (int i = 0; i < configs.size(); ++i) { + configs.valueAt(i).write(Objects.requireNonNull(event)); + } + } + return true; + } + + private static final class SharedMemoryWrapper { + + private static final int SENSOR_EVENT_SIZE = 104; + + // The limit of number of values for a single sensor event. + private static final int MAXIMUM_NUMBER_OF_SENSOR_VALUES = 16; + + @GuardedBy("mWriteLock") + private final SharedMemory mSharedMemory; + @GuardedBy("mWriteLock") + private int mWriteOffset = 0; + @GuardedBy("mWriteLock") + private final ByteBuffer mEventBuffer = ByteBuffer.allocate(SENSOR_EVENT_SIZE); + @GuardedBy("mWriteLock") + private final ByteBuffer mMemoryMapping; + private final Object mWriteLock = new Object(); + + SharedMemoryWrapper(SharedMemory sharedMemory) throws ErrnoException { + mSharedMemory = sharedMemory; + mMemoryMapping = mSharedMemory.mapReadWrite(); + mEventBuffer.order(ByteOrder.nativeOrder()); + } + + void close() { + synchronized (mWriteLock) { + mSharedMemory.close(); + } + } + + void write(int reportToken, int sensorType, long eventCounter, VirtualSensorEvent event) { + synchronized (mWriteLock) { + mEventBuffer.position(0); + mEventBuffer.putInt(SENSOR_EVENT_SIZE); + mEventBuffer.putInt(reportToken); + mEventBuffer.putInt(sensorType); + mEventBuffer.putInt((int) (eventCounter & UINT32_MAX)); + mEventBuffer.putLong(event.getTimestampNanos()); + + for (int i = 0; i < MAXIMUM_NUMBER_OF_SENSOR_VALUES; ++i) { + if (i < event.getValues().length) { + mEventBuffer.putFloat(event.getValues()[i]); + } else { + mEventBuffer.putFloat(0f); + } + } + mEventBuffer.putInt(0); + + mMemoryMapping.position(mWriteOffset); + mMemoryMapping.put(mEventBuffer.array(), 0, SENSOR_EVENT_SIZE); + + mWriteOffset += SENSOR_EVENT_SIZE; + if (mWriteOffset + SENSOR_EVENT_SIZE >= mSharedMemory.getSize()) { + mWriteOffset = 0; + } + } + } + } + + private static final class DirectChannelConfiguration { + private final int mReportToken; + private final int mSensorType; + private final AtomicLong mEventCounter; + private final SharedMemoryWrapper mSharedMemoryWrapper; + + DirectChannelConfiguration(int reportToken, int sensorType, + SharedMemoryWrapper sharedMemoryWrapper) { + mReportToken = reportToken; + mSensorType = sensorType; + mEventCounter = new AtomicLong(1); + mSharedMemoryWrapper = sharedMemoryWrapper; + } + + void write(VirtualSensorEvent event) { + long currentCounter = mEventCounter.getAcquire(); + mSharedMemoryWrapper.write(mReportToken, mSensorType, currentCounter++, event); + if (currentCounter == UINT32_MAX + 1) { + currentCounter = 1; + } + mEventCounter.setRelease(currentCounter); + } + } +} |