summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2023-03-24 13:20:06 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-03-24 13:20:06 +0000
commit96bc9ff0b8171c20cbcd191592de2f13bfbf3aae (patch)
tree5b9b411ca07f156ff37d339b22239be8b234a2e6
parent8ea6ef0c25c30f14c54bbd30788b8750f0389684 (diff)
parent01b0fccd7056ad63b273a7fb0c011237073624c8 (diff)
Merge "Split VirtualSensor callback." into udc-dev
-rw-r--r--core/api/system-current.txt19
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java87
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorCallback.java79
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java108
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java261
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);
+ }
+ }
+}