From 5c4d5c86b50f011e38c99fa78ff8720cbbfd3a64 Mon Sep 17 00:00:00 2001 From: Vladimir Komsiyski Date: Thu, 19 Jan 2023 18:22:53 +0100 Subject: [PATCH] Direct connection API for runtime sensors. The type of direct channel and highest supported rate level are reflected in the sensor flags. Only MemoryFile channels are supported for virtual device sensors, HardwareBuffer ones are not. New sensor callback methods inform the VirtualDevice owner when a new direct sensor channel has been (un-)registered or configured for a sensor, along with the writable shared memory region and rate level. Bug: 266042170 Test: atest SensorControllerTest Test: atest VirtualSensorConfigTest Change-Id: I239bdf448ce0ff1412a0bcd0a4654d70a08b3077 --- core/api/system-current.txt | 7 ++ .../virtual/VirtualDeviceParams.java | 20 +++ .../sensor/IVirtualSensorCallback.aidl | 28 +++++ .../virtual/sensor/VirtualSensorCallback.java | 75 ++++++++++++ .../virtual/sensor/VirtualSensorConfig.java | 114 +++++++++++++++++- .../android/hardware/SystemSensorManager.java | 13 +- core/jni/android_hardware_SensorManager.cpp | 7 +- .../sensor/VirtualSensorConfigTest.java | 44 +++++++ .../companion/virtual/SensorController.java | 70 ++++++++++- .../server/sensors/SensorManagerInternal.java | 28 ++++- .../android/server/sensors/SensorService.java | 7 +- ...om_android_server_sensor_SensorService.cpp | 69 +++++++++-- .../virtual/SensorControllerTest.java | 4 +- .../VirtualDeviceManagerServiceTest.java | 2 +- 14 files changed, 459 insertions(+), 29 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0e81da00fa02d..c83f8e8a520a3 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3346,10 +3346,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(); @@ -3360,6 +3365,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: + *

+ * + * @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 cc6f7c27b01d4..5226a3cf0dea4 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 @@ -500,7 +500,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);