diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6581c421f9705..476cfe76d398d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3344,10 +3344,15 @@ 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 {
method public int describeContents();
+ method public int getDirectChannelTypesSupported();
+ method public int getHighestDirectReportRateLevel();
method @NonNull public String getName();
method public int getType();
method @Nullable public String getVendor();
@@ -3358,6 +3363,8 @@ package android.companion.virtual.sensor {
public static final class VirtualSensorConfig.Builder {
ctor public VirtualSensorConfig.Builder(int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index d8076b5c0fd75..9f3b60148004b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -35,6 +35,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SharedMemory;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -577,6 +578,25 @@ public final class VirtualDeviceParams implements Parcelable {
mExecutor.execute(() -> mCallback.onConfigurationChanged(
sensor, enabled, samplingPeriod, batchReportingLatency));
}
+
+ @Override
+ public void onDirectChannelCreated(int channelHandle,
+ @NonNull SharedMemory sharedMemory) {
+ mExecutor.execute(
+ () -> mCallback.onDirectChannelCreated(channelHandle, sharedMemory));
+ }
+
+ @Override
+ public void onDirectChannelDestroyed(int channelHandle) {
+ mExecutor.execute(() -> mCallback.onDirectChannelDestroyed(channelHandle));
+ }
+
+ @Override
+ public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor,
+ int rateLevel, int reportToken) {
+ mExecutor.execute(() -> mCallback.onDirectChannelConfigured(
+ channelHandle, sensor, rateLevel, reportToken));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index 7da9c3224400a..3cb0572f3350d 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -17,6 +17,7 @@
package android.companion.virtual.sensor;
import android.companion.virtual.sensor.VirtualSensor;
+import android.os.SharedMemory;
/**
* Interface for notifying the sensor owner about whether and how sensor events should be injected.
@@ -36,4 +37,31 @@ oneway interface IVirtualSensorCallback {
*/
void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
int batchReportLatencyMicros);
+
+ /**
+ * Called when a sensor direct channel is created.
+ *
+ * @param channelHandle Identifier of the channel that was created.
+ * @param sharedMemory The shared memory region for the direct sensor channel.
+ */
+ void onDirectChannelCreated(int channelHandle, in SharedMemory sharedMemory);
+
+ /**
+ * Called when a sensor direct channel is destroyed.
+ *
+ * @param channelHandle Identifier of the channel that was destroyed.
+ */
+ void onDirectChannelDestroyed(int channelHandle);
+
+ /**
+ * Called when a sensor direct channel is configured.
+ *
+ * @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.
+ */
+ void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel,
+ int reportToken);
}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
index e097189413027..f7af283a749b1 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -17,8 +17,13 @@
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;
@@ -50,4 +55,74 @@ 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.
+ *
+ *
The {@link android.hardware.SensorManager} instance used to create the direct channel must
+ * be associated with the virtual device.
+ *
+ *
A typical order of callback invocations is:
+ *
+ * - {@code onDirectChannelCreated} - the channel handle and the associated shared memory
+ * should be stored by the virtual device
+ * - {@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.
+ * - {@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual
+ * device should stop writing to the shared memory for the associated channel.
+ * - {@code onDirectChannelDestroyed} - the shared memory associated with the channel
+ * handle should be closed.
+ *
+ *
+ * @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.
+ *
+ * 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.
+ *
+ *
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}.
+ *
+ *
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/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 6d45365ebbd44..ffbdff8c2e3b6 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -21,11 +21,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.Sensor;
+import android.hardware.SensorDirectChannel;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
+
/**
* Configuration for creation of a virtual sensor.
* @see VirtualSensor
@@ -33,6 +35,14 @@ import java.util.Objects;
*/
@SystemApi
public final class VirtualSensorConfig implements Parcelable {
+ private static final String TAG = "VirtualSensorConfig";
+
+ // Mask for direct mode highest rate level, bit 7, 8, 9.
+ private static final int DIRECT_REPORT_MASK = 0x380;
+ private static final int DIRECT_REPORT_SHIFT = 7;
+
+ // Mask for supported direct channel, bit 10, 11
+ private static final int DIRECT_CHANNEL_SHIFT = 10;
private final int mType;
@NonNull
@@ -40,16 +50,21 @@ public final class VirtualSensorConfig implements Parcelable {
@Nullable
private final String mVendor;
- private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) {
+ private final int mFlags;
+
+ private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor,
+ int flags) {
mType = type;
mName = name;
mVendor = vendor;
+ mFlags = flags;
}
private VirtualSensorConfig(@NonNull Parcel parcel) {
mType = parcel.readInt();
mName = parcel.readString8();
mVendor = parcel.readString8();
+ mFlags = parcel.readInt();
}
@Override
@@ -62,6 +77,7 @@ public final class VirtualSensorConfig implements Parcelable {
parcel.writeInt(mType);
parcel.writeString8(mName);
parcel.writeString8(mVendor);
+ parcel.writeInt(mFlags);
}
/**
@@ -91,23 +107,65 @@ public final class VirtualSensorConfig implements Parcelable {
return mVendor;
}
+ /**
+ * Returns the highest supported direct report mode rate level of the sensor.
+ *
+ * @see Sensor#getHighestDirectReportRateLevel()
+ */
+ @SensorDirectChannel.RateLevel
+ public int getHighestDirectReportRateLevel() {
+ int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
+ return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
+ ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+ }
+
+ /**
+ * Returns a combination of all supported direct channel types.
+ *
+ * @see Builder#setDirectChannelTypesSupported(int)
+ * @see Sensor#isDirectChannelTypeSupported(int)
+ */
+ public @SensorDirectChannel.MemoryType int getDirectChannelTypesSupported() {
+ int memoryTypes = 0;
+ if ((mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0) {
+ memoryTypes |= SensorDirectChannel.TYPE_MEMORY_FILE;
+ }
+ if ((mFlags & (1 << (DIRECT_CHANNEL_SHIFT + 1))) > 0) {
+ memoryTypes |= SensorDirectChannel.TYPE_HARDWARE_BUFFER;
+ }
+ return memoryTypes;
+ }
+
+ /**
+ * Returns the sensor flags.
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
/**
* Builder for {@link VirtualSensorConfig}.
*/
public static final class Builder {
+ private static final int FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED =
+ 1 << DIRECT_CHANNEL_SHIFT;
private final int mType;
@NonNull
private final String mName;
@Nullable
private String mVendor;
+ private int mFlags;
+ @SensorDirectChannel.RateLevel
+ int mHighestDirectReportRateLevel;
/**
* Creates a new builder.
*
* @param type The type of the sensor, matching {@link Sensor#getType}.
* @param name The name of the sensor. Must be unique among all sensors with the same type
- * that belong to the same virtual device.
+ * that belong to the same virtual device.
*/
public Builder(int type, @NonNull String name) {
mType = type;
@@ -119,7 +177,19 @@ public final class VirtualSensorConfig implements Parcelable {
*/
@NonNull
public VirtualSensorConfig build() {
- return new VirtualSensorConfig(mType, mName, mVendor);
+ if (mHighestDirectReportRateLevel > 0) {
+ if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) == 0) {
+ throw new IllegalArgumentException("Setting direct channel type is required "
+ + "for sensors with direct channel support.");
+ }
+ mFlags |= mHighestDirectReportRateLevel << DIRECT_REPORT_SHIFT;
+ }
+ if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) > 0
+ && mHighestDirectReportRateLevel == 0) {
+ throw new IllegalArgumentException("Highest direct report rate level is "
+ + "required for sensors with direct channel support.");
+ }
+ return new VirtualSensorConfig(mType, mName, mVendor, mFlags);
}
/**
@@ -130,6 +200,44 @@ public final class VirtualSensorConfig implements Parcelable {
mVendor = vendor;
return this;
}
+
+ /**
+ * Sets the highest supported rate level for direct sensor report.
+ *
+ * @see VirtualSensorConfig#getHighestDirectReportRateLevel()
+ */
+ @NonNull
+ public VirtualSensorConfig.Builder setHighestDirectReportRateLevel(
+ @SensorDirectChannel.RateLevel int rateLevel) {
+ mHighestDirectReportRateLevel = rateLevel;
+ return this;
+ }
+
+ /**
+ * Sets whether direct sensor channel of the given types is supported.
+ *
+ * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags
+ * indicating the types of shared memory supported for creating direct channels. Only
+ * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual
+ * sensors.
+ * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is
+ * set to be supported.
+ */
+ @NonNull
+ public VirtualSensorConfig.Builder setDirectChannelTypesSupported(
+ @SensorDirectChannel.MemoryType int memoryTypes) {
+ if ((memoryTypes & SensorDirectChannel.TYPE_MEMORY_FILE) > 0) {
+ mFlags |= FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED;
+ } else {
+ mFlags &= ~FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED;
+ }
+ if ((memoryTypes & ~SensorDirectChannel.TYPE_MEMORY_FILE) > 0) {
+ throw new IllegalArgumentException(
+ "Only TYPE_MEMORY_FILE direct channels can be supported for virtual "
+ + "sensors.");
+ }
+ return this;
+ }
}
@NonNull
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 9388ae3fd5e45..965b35f4cc759 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -92,7 +92,8 @@ public class SystemSensorManager extends SensorManager {
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
private static native int nativeCreateDirectChannel(
- long nativeInstance, long size, int channelType, int fd, HardwareBuffer buffer);
+ long nativeInstance, int deviceId, long size, int channelType, int fd,
+ HardwareBuffer buffer);
private static native void nativeDestroyDirectChannel(
long nativeInstance, int channelHandle);
private static native int nativeConfigDirectChannel(
@@ -695,6 +696,10 @@ public class SystemSensorManager extends SensorManager {
/** @hide */
protected SensorDirectChannel createDirectChannelImpl(
MemoryFile memoryFile, HardwareBuffer hardwareBuffer) {
+ int deviceId = mContext.getDeviceId();
+ if (isDeviceSensorPolicyDefault(deviceId)) {
+ deviceId = DEVICE_ID_DEFAULT;
+ }
int id;
int type;
long size;
@@ -713,8 +718,8 @@ public class SystemSensorManager extends SensorManager {
}
size = memoryFile.length();
- id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_MEMORY_FILE, fd, null);
+ id = nativeCreateDirectChannel(mNativeInstance, deviceId, size,
+ SensorDirectChannel.TYPE_MEMORY_FILE, fd, null);
if (id <= 0) {
throw new UncheckedIOException(
new IOException("create MemoryFile direct channel failed " + id));
@@ -738,7 +743,7 @@ public class SystemSensorManager extends SensorManager {
}
size = hardwareBuffer.getWidth();
id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+ mNativeInstance, deviceId, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
-1, hardwareBuffer);
if (id <= 0) {
throw new UncheckedIOException(
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 939a0e4119139..9c6a534c3bbb6 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -266,7 +266,8 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s
}
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
- jlong size, jint channelType, jint fd, jobject hardwareBufferObj) {
+ jint deviceId, jlong size, jint channelType, jint fd,
+ jobject hardwareBufferObj) {
const native_handle_t *nativeHandle = nullptr;
NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0);
@@ -287,7 +288,7 @@ static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa
}
SensorManager* mgr = reinterpret_cast(sensorManager);
- return mgr->createDirectChannel(size, channelType, nativeHandle);
+ return mgr->createDirectChannel(deviceId, size, channelType, nativeHandle);
}
static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
@@ -532,7 +533,7 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled},
- {"nativeCreateDirectChannel", "(JJIILandroid/hardware/HardwareBuffer;)I",
+ {"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I",
(void *)nativeCreateDirectChannel},
{"nativeDestroyDirectChannel", "(JI)V", (void *)nativeDestroyDirectChannel},
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
index f97099d045722..16ed3ef42da32 100644
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
@@ -17,9 +17,15 @@
package android.companion.virtual.sensor;
import static android.hardware.Sensor.TYPE_ACCELEROMETER;
+import static android.hardware.SensorDirectChannel.RATE_STOP;
+import static android.hardware.SensorDirectChannel.RATE_VERY_FAST;
+import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER;
+import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE;
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -40,6 +46,8 @@ public class VirtualSensorConfigTest {
final VirtualSensorConfig originalConfig =
new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
.setVendor(SENSOR_VENDOR)
+ .setHighestDirectReportRateLevel(RATE_VERY_FAST)
+ .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
.build();
final Parcel parcel = Parcel.obtain();
originalConfig.writeToParcel(parcel, /* flags= */ 0);
@@ -49,6 +57,39 @@ public class VirtualSensorConfigTest {
assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType());
assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName());
assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor());
+ assertThat(recreatedConfig.getHighestDirectReportRateLevel()).isEqualTo(RATE_VERY_FAST);
+ assertThat(recreatedConfig.getDirectChannelTypesSupported()).isEqualTo(TYPE_MEMORY_FILE);
+ // From hardware/libhardware/include/hardware/sensors-base.h:
+ // 0x400 is SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM (i.e. TYPE_MEMORY_FILE)
+ // 0x800 is SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC (i.e. TYPE_HARDWARE_BUFFER)
+ // 7 is SENSOR_FLAG_SHIFT_DIRECT_REPORT
+ assertThat(recreatedConfig.getFlags()).isEqualTo(0x400 | RATE_VERY_FAST << 7);
+ }
+
+ @Test
+ public void hardwareBufferDirectChannelTypeSupported_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE));
+ }
+
+ @Test
+ public void directChannelTypeSupported_missingHighestReportRateLevel_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
+ .build());
+ }
+
+ @Test
+ public void directChannelTypeSupported_missingDirectChannelTypeSupported_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setHighestDirectReportRateLevel(RATE_VERY_FAST)
+ .build());
}
@Test
@@ -56,5 +97,8 @@ public class VirtualSensorConfigTest {
final VirtualSensorConfig config =
new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build();
assertThat(config.getVendor()).isNull();
+ assertThat(config.getHighestDirectReportRateLevel()).isEqualTo(RATE_STOP);
+ assertThat(config.getDirectChannelTypesSupported()).isEqualTo(0);
+ assertThat(config.getFlags()).isEqualTo(0);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 7804ebf1583d9..864fe0f5edc12 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -22,8 +22,11 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.hardware.SensorDirectChannel;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SharedMemory;
import android.util.ArrayMap;
import android.util.Slog;
@@ -36,6 +39,7 @@ import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/** Controls virtual sensors, including their lifecycle and sensor event dispatch. */
public class SensorController {
@@ -47,6 +51,8 @@ public class SensorController {
private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value
private static final int BAD_VALUE = -22;
+ private static AtomicInteger sNextDirectChannelHandle = new AtomicInteger(1);
+
private final Object mLock;
private final int mVirtualDeviceId;
@GuardedBy("mLock")
@@ -57,8 +63,6 @@ public class SensorController {
private final SensorManagerInternal mSensorManagerInternal;
private final VirtualDeviceManagerInternal mVdmInternal;
-
-
public SensorController(@NonNull Object lock, int virtualDeviceId,
@Nullable IVirtualSensorCallback virtualSensorCallback) {
mLock = lock;
@@ -97,7 +101,7 @@ public class SensorController {
throws SensorCreationException {
final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId,
config.getType(), config.getName(),
- config.getVendor() == null ? "" : config.getVendor(),
+ config.getVendor() == null ? "" : config.getVendor(), config.getFlags(),
mRuntimeSensorCallback);
if (handle <= 0) {
throw new SensorCreationException("Received an invalid virtual sensor handle.");
@@ -212,6 +216,66 @@ public class SensorController {
}
return OK;
}
+
+ @Override
+ public int onDirectChannelCreated(ParcelFileDescriptor fd) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId);
+ return BAD_VALUE;
+ } else if (fd == null) {
+ Slog.e(TAG, "Received invalid ParcelFileDescriptor");
+ return BAD_VALUE;
+ }
+ final int channelHandle = sNextDirectChannelHandle.getAndIncrement();
+ SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd);
+ try {
+ mCallback.onDirectChannelCreated(channelHandle, sharedMemory);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ return UNKNOWN_ERROR;
+ }
+ return channelHandle;
+ }
+
+ @Override
+ public void onDirectChannelDestroyed(int channelHandle) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId);
+ return;
+ }
+ try {
+ mCallback.onDirectChannelDestroyed(channelHandle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ }
+ }
+
+ @Override
+ public int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ @SensorDirectChannel.RateLevel int rateLevel) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No runtime sensor callback configured.");
+ return BAD_VALUE;
+ }
+ VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, sensorHandle);
+ if (sensor == null) {
+ Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId
+ + " and sensor handle=" + sensorHandle);
+ return BAD_VALUE;
+ }
+ try {
+ mCallback.onDirectChannelConfigured(channelHandle, sensor, rateLevel, sensorHandle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ return UNKNOWN_ERROR;
+ }
+ if (rateLevel == SensorDirectChannel.RATE_STOP) {
+ return OK;
+ } else {
+ // Use the sensor handle as a report token, i.e. a unique identifier of the sensor.
+ return sensorHandle;
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 41c2fbfd33142..6c32ec2e8df88 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,8 @@
package com.android.server.sensors;
import android.annotation.NonNull;
+import android.hardware.SensorDirectChannel;
+import android.os.ParcelFileDescriptor;
import java.util.concurrent.Executor;
@@ -58,7 +60,7 @@ public abstract class SensorManagerInternal {
* @return The sensor handle.
*/
public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name,
- @NonNull String vendor, @NonNull RuntimeSensorCallback callback);
+ @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback);
/**
* Unregisters the sensor with the given handle from the framework.
@@ -98,9 +100,31 @@ public abstract class SensorManagerInternal {
public interface RuntimeSensorCallback {
/**
* Invoked when the listeners of the runtime sensor have changed.
- * Returns an error code if the invocation was unsuccessful, zero otherwise.
+ * Returns zero on success, negative error code otherwise.
*/
int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros,
int batchReportLatencyMicros);
+
+ /**
+ * Invoked when a direct sensor channel has been created.
+ * Wraps the file descriptor in a {@link android.os.SharedMemory} object and passes it to
+ * the client process.
+ * Returns a positive identifier of the channel on success, negative error code otherwise.
+ */
+ int onDirectChannelCreated(ParcelFileDescriptor fd);
+
+ /**
+ * Invoked when a direct sensor channel has been destroyed.
+ */
+ void onDirectChannelDestroyed(int channelHandle);
+
+ /**
+ * Invoked when a direct sensor channel has been configured for a sensor.
+ * If the invocation is unsuccessful, a negative error code is returned.
+ * On success, the return value is zero if the rate level is {@code RATE_STOP}, and a
+ * positive report token otherwise.
+ */
+ int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ @SensorDirectChannel.RateLevel int rateLevel);
}
}
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 979065950dc4f..1baa0a6d79a18 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -56,7 +56,8 @@ public class SensorService extends SystemService {
private static native void unregisterProximityActiveListenerNative(long ptr);
private static native int registerRuntimeSensorNative(long ptr, int deviceId, int type,
- String name, String vendor, SensorManagerInternal.RuntimeSensorCallback callback);
+ String name, String vendor, int flags,
+ SensorManagerInternal.RuntimeSensorCallback callback);
private static native void unregisterRuntimeSensorNative(long ptr, int handle);
private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
long timestampNanos, float[] values);
@@ -95,9 +96,9 @@ public class SensorService extends SystemService {
class LocalService extends SensorManagerInternal {
@Override
public int createRuntimeSensor(int deviceId, int type, @NonNull String name,
- @NonNull String vendor, @NonNull RuntimeSensorCallback callback) {
+ @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback) {
synchronized (mLock) {
- int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor,
+ int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, flags,
callback);
mRuntimeSensorHandles.add(handle);
return handle;
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index 356e9a95e311f..a916b64fc0bd3 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -17,10 +17,13 @@
#define LOG_TAG "NativeSensorService"
#include
+#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -28,6 +31,8 @@
#include
+#include "android_util_Binder.h"
+
#define PROXIMITY_ACTIVE_CLASS \
"com/android/server/sensors/SensorManagerInternal$ProximityActiveListener"
@@ -38,7 +43,10 @@ namespace android {
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnProximityActive;
-static jmethodID sMethodIdOnConfigurationChanged;
+static jmethodID sMethodIdRuntimeSensorOnConfigurationChanged;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelCreated;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelDestroyed;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelConfigured;
class NativeSensorService {
public:
@@ -47,7 +55,7 @@ public:
void registerProximityActiveListener();
void unregisterProximityActiveListener();
jint registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, jstring vendor,
- jobject callback);
+ jint flags, jobject callback);
void unregisterRuntimeSensor(jint handle);
jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
jfloatArray values);
@@ -74,6 +82,9 @@ private:
status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs,
int64_t batchReportLatencyNs) override;
+ int onDirectChannelCreated(int fd) override;
+ void onDirectChannelDestroyed(int channelHandle) override;
+ int onDirectChannelConfigured(int channelHandle, int sensorHandle, int rateLevel) override;
private:
jobject mCallback;
@@ -108,7 +119,7 @@ void NativeSensorService::unregisterProximityActiveListener() {
}
jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name,
- jstring vendor, jobject callback) {
+ jstring vendor, jint flags, jobject callback) {
if (mService == nullptr) {
ALOGD("Dropping registerRuntimeSensor, sensor service not available.");
return -1;
@@ -119,6 +130,11 @@ jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint
.vendor = env->GetStringUTFChars(vendor, 0),
.version = sizeof(sensor_t),
.type = type,
+#ifdef __LP64__
+ .flags = static_cast(flags),
+#else
+ .flags = static_cast(flags),
+#endif
};
sp callbackDelegate(
@@ -234,12 +250,39 @@ NativeSensorService::RuntimeSensorCallbackDelegate::~RuntimeSensorCallbackDelega
status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged(
int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) {
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
- return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged,
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnConfigurationChanged,
static_cast(handle), static_cast(enabled),
static_cast(ns2us(samplingPeriodNs)),
static_cast(ns2us(batchReportLatencyNs)));
}
+int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelCreated(int fd) {
+ if (fd <= 0) {
+ return 0;
+ }
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jobject jfd = jniCreateFileDescriptor(jniEnv, fd);
+ jobject parcelFileDescriptor = newParcelFileDescriptor(jniEnv, jfd);
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelCreated,
+ parcelFileDescriptor);
+}
+
+void NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelDestroyed(
+ int channelHandle) {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ return jniEnv->CallVoidMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelDestroyed,
+ static_cast(channelHandle));
+}
+
+int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelConfigured(int channelHandle,
+ int sensorHandle,
+ int rateLevel) {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelConfigured,
+ static_cast(channelHandle), static_cast(sensorHandle),
+ static_cast(rateLevel));
+}
+
static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) {
NativeSensorService* service = new NativeSensorService(env, listener);
return reinterpret_cast(service);
@@ -256,9 +299,10 @@ static void unregisterProximityActiveListenerNative(JNIEnv* env, jclass, jlong p
}
static jint registerRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint deviceId, jint type,
- jstring name, jstring vendor, jobject callback) {
+ jstring name, jstring vendor, jint flags,
+ jobject callback) {
auto* service = reinterpret_cast(ptr);
- return service->registerRuntimeSensor(env, deviceId, type, name, vendor, callback);
+ return service->registerRuntimeSensor(env, deviceId, type, name, vendor, flags, callback);
}
static void unregisterRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint handle) {
@@ -280,7 +324,7 @@ static const JNINativeMethod methods[] = {
{"unregisterProximityActiveListenerNative", "(J)V",
reinterpret_cast(unregisterProximityActiveListenerNative)},
{"registerRuntimeSensorNative",
- "(JIILjava/lang/String;Ljava/lang/String;L" RUNTIME_SENSOR_CALLBACK_CLASS ";)I",
+ "(JIILjava/lang/String;Ljava/lang/String;IL" RUNTIME_SENSOR_CALLBACK_CLASS ";)I",
reinterpret_cast(registerRuntimeSensorNative)},
{"unregisterRuntimeSensorNative", "(JI)V",
reinterpret_cast(unregisterRuntimeSensorNative)},
@@ -293,8 +337,17 @@ int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS);
sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V");
jclass runtimeSensorCallbackClass = FindClassOrDie(env, RUNTIME_SENSOR_CALLBACK_CLASS);
- sMethodIdOnConfigurationChanged =
+ sMethodIdRuntimeSensorOnConfigurationChanged =
GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I");
+ sMethodIdRuntimeSensorOnDirectChannelCreated =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelCreated",
+ "(Landroid/os/ParcelFileDescriptor;)I");
+ sMethodIdRuntimeSensorOnDirectChannelDestroyed =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelDestroyed", "(I)V");
+ sMethodIdRuntimeSensorOnDirectChannelConfigured =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelConfigured",
+ "(III)I");
+
return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
NELEM(methods));
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 6431e88b1acb7..1259d7189a6d5 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -81,7 +81,7 @@ public class SensorControllerTest {
@Test
public void createSensor_invalidHandle_throwsException() {
doReturn(/* handle= */0).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
Throwable thrown = assertThrows(
RuntimeException.class,
@@ -138,7 +138,7 @@ public class SensorControllerTest {
private void doCreateSensorSuccessfully() {
doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
assertThat(mSensorController.createSensor(mSensorToken, mVirtualSensorConfig))
.isEqualTo(SENSOR_HANDLE);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 0cd50f03fbfa2..1e95d973b1c0f 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -485,7 +485,7 @@ public class VirtualDeviceManagerServiceTest {
.build();
doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE);