diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 27a97de815b6a..d705d68374aa7 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);