diff options
author | 2025-03-07 12:54:09 -0800 | |
---|---|---|
committer | 2025-03-07 12:54:09 -0800 | |
commit | d6103f03a6d983a6f49ecad4f073357139566016 (patch) | |
tree | 5bdf8921568e789b8cac290c5371a38a268f4e15 | |
parent | 0d1e54610f14ade81c1e0a87d9d86fca59aa439a (diff) |
Support virtual sensor additional info
Higher level API compared to the sensors HAL
Fix: 393517834
Test: CTS
Flag: android.companion.virtualdevice.flags.virtual_sensor_additional_info
Change-Id: Ib59871d6f8e4e63d15ddf507c202b543e236d970
13 files changed, 520 insertions, 4 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 32b170a6286b..90f6aebb8b02 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3642,11 +3642,26 @@ package android.companion.virtual.sensor { method public int getDeviceId(); method @NonNull public String getName(); method public int getType(); + method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo); method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR; } + @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getType(); + method @NonNull public java.util.List<float[]> getValues(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR; + } + + public static final class VirtualSensorAdditionalInfo.Builder { + ctor public VirtualSensorAdditionalInfo.Builder(int); + method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]); + method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build(); + } + public interface VirtualSensorCallback { method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); } @@ -3664,6 +3679,7 @@ package android.companion.virtual.sensor { method public float getResolution(); method public int getType(); method @Nullable public String getVendor(); + method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported(); method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR; @@ -3672,6 +3688,7 @@ package android.companion.virtual.sensor { public static final class VirtualSensorConfig.Builder { ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build(); + method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean); 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 setMaxDelay(int); diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index f8ac27de1754..db77adeb5a3d 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorAdditionalInfo; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.companion.virtual.camera.VirtualCameraConfig; @@ -251,6 +252,11 @@ interface IVirtualDevice { boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event); /** + * Sends additional information about the virtual sensor corresponding to the given token. + */ + boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info); + + /** * Launches a pending intent on the given display that is owned by this virtual device. */ void launchPendingIntent(int displayId, in PendingIntent pendingIntent, diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index 4fb3982c3754..615a6dffdf99 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -158,3 +158,11 @@ flag { bug: "370720522" is_exported: true } + +flag { + name: "virtual_sensor_additional_info" + namespace: "virtual_devices" + description: "API for injecting SensorAdditionalInfo for VirtualSensor" + bug: "393517834" + is_exported: true +} diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java index 934a1a8ffcbd..8d4acfcb30d7 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensor.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java @@ -16,12 +16,15 @@ package android.companion.virtual.sensor; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.companion.virtual.IVirtualDevice; +import android.companion.virtualdevice.flags.Flags; import android.hardware.Sensor; +import android.hardware.SensorAdditionalInfo; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -37,20 +40,33 @@ import android.os.RemoteException; */ @SystemApi public final class VirtualSensor implements Parcelable { + private final int mHandle; private final int mType; private final String mName; + private final int mFlags; private final IVirtualDevice mVirtualDevice; private final IBinder mToken; + // Only one additional info frame set at a time. + private final Object mAdditionalInfoLock = new Object(); /** * @hide */ public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice, IBinder token) { + this(handle, type, name, /*flags=*/0, virtualDevice, token); + } + + /** + * @hide + */ + public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice, + IBinder token) { mHandle = handle; mType = type; mName = name; + mFlags = flags; mVirtualDevice = virtualDevice; mToken = token; } @@ -61,13 +77,14 @@ public final class VirtualSensor implements Parcelable { @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi public VirtualSensor(int handle, int type, @NonNull String name) { - this(handle, type, name, /*virtualDevice=*/null, /*token=*/null); + this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null); } private VirtualSensor(Parcel parcel) { mHandle = parcel.readInt(); mType = parcel.readInt(); mName = parcel.readString8(); + mFlags = parcel.readInt(); mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder()); mToken = parcel.readStrongBinder(); } @@ -123,6 +140,7 @@ public final class VirtualSensor implements Parcelable { parcel.writeInt(mHandle); parcel.writeInt(mType); parcel.writeString8(mName); + parcel.writeInt(mFlags); parcel.writeStrongBinder(mVirtualDevice.asBinder()); parcel.writeStrongBinder(mToken); } @@ -143,6 +161,33 @@ public final class VirtualSensor implements Parcelable { } } + /** + * Send additional information about the sensor to the system. + * + * @param info the additional sensor information to send. + * @throws UnsupportedOperationException if the sensor does not support sending additional info. + * @see Sensor#isAdditionalInfoSupported() + * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean) + * @see SensorAdditionalInfo + * @see VirtualSensorAdditionalInfo + */ + @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO) + public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) { + if (!Flags.virtualSensorAdditionalInfo()) { + return; + } + if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) { + throw new UnsupportedOperationException("Sensor additional info not supported."); + } + try { + synchronized (mAdditionalInfoLock) { + mVirtualDevice.sendSensorAdditionalInfo(mToken, info); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @NonNull public static final Parcelable.Creator<VirtualSensor> CREATOR = new Parcelable.Creator<VirtualSensor>() { diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl new file mode 100644 index 000000000000..7267be88ca75 --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + +parcelable VirtualSensorAdditionalInfo;
\ No newline at end of file diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java new file mode 100644 index 000000000000..a4fca507b1d5 --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.companion.virtualdevice.flags.Flags; +import android.hardware.SensorAdditionalInfo; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * An additional information frame for a {@link VirtualSensor}, which is reported through listener + * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}. + * + * @see SensorAdditionalInfo + * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean) + * @hide + */ +@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO) +@SystemApi +public final class VirtualSensorAdditionalInfo implements Parcelable { + + private final int mType; + @NonNull + private final List<float[]> mValues; + + /** @hide */ + @IntDef(prefix = "TYPE_", value = { + SensorAdditionalInfo.TYPE_UNTRACKED_DELAY, + SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE, + SensorAdditionalInfo.TYPE_VEC3_CALIBRATION, + SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT, + SensorAdditionalInfo.TYPE_SAMPLING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) { + mType = type; + mValues = values; + } + + private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) { + mType = parcel.readInt(); + final int valuesLength = parcel.readInt(); + mValues = new ArrayList<>(valuesLength); + for (int i = 0; i < valuesLength; ++i) { + mValues.add(parcel.createFloatArray()); + } + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) { + parcel.writeInt(mType); + parcel.writeInt(mValues.size()); + for (int i = 0; i < mValues.size(); ++i) { + parcel.writeFloatArray(mValues.get(i)); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the type of this information frame. + * + * @see SensorAdditionalInfo#type + */ + public int getType() { + return mType; + } + + /** + * Returns the float values of this information frame, if any. + * + * @see SensorAdditionalInfo#floatValues + */ + @NonNull + public List<float[]> getValues() { + return mValues; + } + + /** + * Builder for {@link VirtualSensorAdditionalInfo}. + */ + public static final class Builder { + + @VirtualSensorAdditionalInfo.Type + private final int mType; + @NonNull + private final ArrayList<float[]> mValues = new ArrayList<>(); + + /** + * Creates a new builder. + * + * @param type type of this additional info frame. + * @see SensorAdditionalInfo + */ + public Builder(@VirtualSensorAdditionalInfo.Type int type) { + switch (type) { + case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY: + case SensorAdditionalInfo.TYPE_SAMPLING: + case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE: + case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION: + case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT: + break; + default: + throw new IllegalArgumentException("Unsupported type " + type); + } + mType = type; + } + + /** + * Additional info payload data represented in float values. Depending on the type of + * information, this may be null. + * + * @see SensorAdditionalInfo#floatValues + */ + @NonNull + public Builder addValues(@NonNull float[] values) { + if (values.length > 14) { + throw new IllegalArgumentException("Maximum payload value size is 14."); + } + if (mValues.isEmpty()) { + switch (mType) { + case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY: + case SensorAdditionalInfo.TYPE_SAMPLING: + assertValuesLength(values, 2); + break; + case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE: + assertValuesLength(values, 1); + break; + case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION: + case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT: + assertValuesLength(values, 11); + break; + } + } else if (values.length != mValues.getFirst().length) { + throw new IllegalArgumentException("All payload values must have the same length"); + } + + mValues.add(values); + return this; + } + + private void assertValuesLength(float[] values, int expected) { + if (values.length != expected) { + throw new IllegalArgumentException( + "Payload values must have size " + expected + " for type " + mType); + } + } + + /** + * Creates a new {@link VirtualSensorAdditionalInfo}. + * + * @throws IllegalArgumentException if the payload doesn't match the expectation for the + * given type, as documented in {@link SensorAdditionalInfo}. + */ + @NonNull + public VirtualSensorAdditionalInfo build() { + if (mValues.isEmpty()) { + throw new IllegalArgumentException("Payload is required"); + } + return new VirtualSensorAdditionalInfo(mType, mValues); + } + } + + public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR = + new Creator<>() { + public VirtualSensorAdditionalInfo createFromParcel(Parcel source) { + return new VirtualSensorAdditionalInfo(source); + } + + public VirtualSensorAdditionalInfo[] newArray(int size) { + return new VirtualSensorAdditionalInfo[size]; + } + }; +} diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index 68bc9bce28d2..be8974ec29ad 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -59,10 +59,14 @@ public final class VirtualSensorConfig implements Parcelable { private static final int REPORTING_MODE_MASK = 0xE; private static final int REPORTING_MODE_SHIFT = 1; + // Mask for indication bit of sensor additional information support, bit 6. + static final int ADDITIONAL_INFO_MASK = 0x40; + // 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; @@ -253,6 +257,18 @@ public final class VirtualSensorConfig implements Parcelable { } /** + * Returns whether the sensor supports additional information. + * + * @see Builder#setAdditionalInfoSupported(boolean) + * @see Sensor#isAdditionalInfoSupported() + * @see android.hardware.SensorAdditionalInfo + */ + @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO) + public boolean isAdditionalInfoSupported() { + return (mFlags & ADDITIONAL_INFO_MASK) > 0; + } + + /** * Returns the reporting mode of this sensor. * * @see Builder#setReportingMode(int) @@ -450,6 +466,25 @@ public final class VirtualSensorConfig implements Parcelable { } /** + * Sets whether this sensor supports sensor additional information. + * + * @see Sensor#isAdditionalInfoSupported() + * @see android.hardware.SensorAdditionalInfo + * @see VirtualSensorAdditionalInfo + */ + @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO) + @NonNull + public VirtualSensorConfig.Builder setAdditionalInfoSupported( + boolean additionalInfoSupported) { + if (additionalInfoSupported) { + mFlags |= ADDITIONAL_INFO_MASK; + } else { + mFlags &= ~ADDITIONAL_INFO_MASK; + } + return this; + } + + /** * Sets the reporting mode of this sensor. * * @throws IllegalArgumentException if the reporting mode is not one of 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 88b791046c87..6e098d014ee3 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -21,15 +21,18 @@ import android.annotation.Nullable; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorAdditionalInfo; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.content.AttributionSource; +import android.hardware.SensorAdditionalInfo; import android.hardware.SensorDirectChannel; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SharedMemory; +import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; @@ -140,7 +143,7 @@ public class SensorController { final IBinder sensorToken = new Binder("android.hardware.sensor.VirtualSensor:" + config.getName()); VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(), - virtualDevice, sensorToken); + config.getFlags(), virtualDevice, sensorToken); synchronized (mLock) { mSensorDescriptors.put(sensorToken, sensorDescriptor); mVirtualSensors.put(handle, sensor); @@ -164,6 +167,37 @@ public class SensorController { } } + boolean sendSensorAdditionalInfo(@NonNull IBinder token, + @NonNull VirtualSensorAdditionalInfo info) { + Objects.requireNonNull(token); + Objects.requireNonNull(info); + synchronized (mLock) { + final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token); + long timestamp = SystemClock.elapsedRealtimeNanos(); + if (sensorDescriptor == null) { + throw new IllegalArgumentException("Could not send sensor event for given token"); + } + if (!mSensorManagerInternal.sendSensorAdditionalInfo( + sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN, + /* serial= */ 0, timestamp++, /* values= */ null)) { + return false; + } + for (int i = 0; i < info.getValues().size(); ++i) { + if (!mSensorManagerInternal.sendSensorAdditionalInfo( + sensorDescriptor.getHandle(), info.getType(), /* serial= */ i, + timestamp++, info.getValues().get(i))) { + return false; + } + } + if (!mSensorManagerInternal.sendSensorAdditionalInfo( + sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END, + /* serial= */ 0, timestamp, /* values= */ null)) { + return false; + } + } + return true; + } + @Nullable VirtualSensor getSensorByHandle(int handle) { synchronized (mLock) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 28efdfc01ee2..0023b6d53837 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -55,6 +55,7 @@ import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; import android.companion.virtual.camera.VirtualCameraConfig; import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorAdditionalInfo; import android.companion.virtual.sensor.VirtualSensorEvent; import android.companion.virtualdevice.flags.Flags; import android.compat.annotation.ChangeId; @@ -1294,6 +1295,18 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } @Override // Binder call + public boolean sendSensorAdditionalInfo(@NonNull IBinder token, + @NonNull VirtualSensorAdditionalInfo info) { + checkCallerIsDeviceOwner(); + final long ident = Binder.clearCallingIdentity(); + try { + return mSensorController.sendSensorAdditionalInfo(token, info); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor, IntentFilter filter) { checkCallerIsDeviceOwner(); diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java index 7ff4ade1101c..9636cc6c77a7 100644 --- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java +++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java @@ -17,6 +17,7 @@ package com.android.server.sensors; import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.SensorDirectChannel; import android.os.ParcelFileDescriptor; @@ -71,7 +72,7 @@ public abstract class SensorManagerInternal { /** * Sends an event for the runtime sensor with the given handle to the framework. * - * Only relevant for sending runtime sensor events. @see #createRuntimeSensor. + * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p> * * @param handle The sensor handle. * @param type The type of the sensor. @@ -83,6 +84,21 @@ public abstract class SensorManagerInternal { @NonNull float[] values); /** + * Sends an additional info event for the runtime sensor with the given handle to the framework. + * + * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p> + * + * @param handle The sensor handle. + * @param type The type of payload data. + * @param serial The sequence number of this frame for this type. + * @param timestampNanos Timestamp of the event. + * @param values The payload data represented in float values. + * @return Whether the event injection was successful. + */ + public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial, + long timestampNanos, @Nullable float[] values); + + /** * Listener for proximity sensor state changes. */ public interface ProximityActiveListener { diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java index 3de191030d71..0d31b22e2020 100644 --- a/services/core/java/com/android/server/sensors/SensorService.java +++ b/services/core/java/com/android/server/sensors/SensorService.java @@ -19,6 +19,7 @@ package com.android.server.sensors; import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.util.ArrayMap; @@ -62,6 +63,9 @@ public class SensorService extends SystemService { private static native void unregisterRuntimeSensorNative(long ptr, int handle); private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type, long timestampNanos, float[] values); + private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle, + int type, int serial, long timestampNanos, float[] values); + public SensorService(Context ctx) { super(ctx); @@ -129,6 +133,18 @@ public class SensorService extends SystemService { } @Override + public boolean sendSensorAdditionalInfo(int handle, int type, int serial, + long timestampNanos, @Nullable float[] values) { + synchronized (mLock) { + if (!mRuntimeSensorHandles.contains(handle)) { + return false; + } + return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial, + timestampNanos, values); + } + } + + @Override public void addProximityActiveListener(@NonNull Executor executor, @NonNull ProximityActiveListener listener) { Objects.requireNonNull(executor, "executor must not be null"); diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index eb729de6afd4..0bee7181c2d5 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -60,6 +60,8 @@ public: void unregisterRuntimeSensor(jint handle); jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp, jfloatArray values); + jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial, + jlong timestamp, jfloatArray values); private: sp<SensorService> mService; @@ -172,9 +174,9 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j sensors_event_t event{ .version = sizeof(sensors_event_t), - .timestamp = timestamp, .sensor = handle, .type = type, + .timestamp = timestamp, }; int valuesLength = env->GetArrayLength(values); @@ -234,6 +236,42 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j return err == OK; } +jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, + jint serial, jlong timestamp, + jfloatArray values) { + if (mService == nullptr) { + ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available."); + return false; + } + + sensors_event_t event{ + .version = sizeof(sensors_event_t), + .sensor = handle, + .type = SENSOR_TYPE_ADDITIONAL_INFO, + .timestamp = timestamp, + .additional_info = + (additional_info_event_t){ + .type = type, + .serial = serial, + }, + }; + + if (values != nullptr) { + int valuesLength = env->GetArrayLength(values); + if (valuesLength > 14) { + ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum."); + return false; + } + if (valuesLength > 0) { + jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr); + memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float)); + } + } + + status_t err = mService->sendRuntimeSensorEvent(event); + return err == OK; +} + NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate( JNIEnv* env, jobject listener) : mListener(env->NewGlobalRef(listener)) {} @@ -326,6 +364,13 @@ static jboolean sendRuntimeSensorEventNative(JNIEnv* env, jclass, jlong ptr, jin return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values); } +static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle, + jint type, jint serial, jlong timestamp, + jfloatArray values) { + auto* service = reinterpret_cast<NativeSensorService*>(ptr); + return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values); +} + static const JNINativeMethod methods[] = { {"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J", reinterpret_cast<void*>(startSensorServiceNative)}, @@ -340,6 +385,8 @@ static const JNINativeMethod methods[] = { reinterpret_cast<void*>(unregisterRuntimeSensorNative)}, {"sendRuntimeSensorEventNative", "(JIIJ[F)Z", reinterpret_cast<void*>(sendRuntimeSensorEventNative)}, + {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z", + reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)}, }; int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) { 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 67fc564fa778..2e07cd8ae698 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 @@ -22,18 +22,25 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorAdditionalInfo; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.content.AttributionSource; import android.hardware.Sensor; +import android.hardware.SensorAdditionalInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -49,6 +56,7 @@ import com.google.common.collect.Iterables; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -65,6 +73,9 @@ public class SensorControllerTest { private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER; + private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f}; + private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f}; + @Mock private SensorManagerInternal mSensorManagerInternalMock; @Mock @@ -155,6 +166,53 @@ public class SensorControllerTest { } @Test + public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception { + SensorController sensorController = doCreateSensorSuccessfully(); + + final VirtualSensorAdditionalInfo info = + new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY) + .addValues(ADDITIONAL_INFO_VALUES_1) + .addValues(ADDITIONAL_INFO_VALUES_2) + .build(); + assertThrows( + IllegalArgumentException.class, + () -> sensorController.sendSensorAdditionalInfo( + new Binder("invalidSensorToken"), info)); + } + + @Test + public void sendSensorAdditionalInfo_success() throws Exception { + SensorController sensorController = doCreateSensorSuccessfully(); + + clearInvocations(mSensorManagerInternalMock); + when(mSensorManagerInternalMock.sendSensorAdditionalInfo( + anyInt(), anyInt(), anyInt(), anyLong(), any())) + .thenReturn(true); + IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet()); + + final VirtualSensorAdditionalInfo info = + new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY) + .addValues(ADDITIONAL_INFO_VALUES_1) + .addValues(ADDITIONAL_INFO_VALUES_2) + .build(); + sensorController.sendSensorAdditionalInfo(token, info); + + InOrder inOrder = inOrder(mSensorManagerInternalMock); + inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo( + eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN), + /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull()); + inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo( + eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY), + /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1)); + inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo( + eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY), + /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2)); + inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo( + eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END), + /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull()); + } + + @Test public void close_unregistersSensors() throws Exception { SensorController sensorController = doCreateSensorSuccessfully(); |