From 9d5084dac25072050ac568a4ecebc6f8e2d14289 Mon Sep 17 00:00:00 2001 From: Vladimir Komsiyski Date: Fri, 10 Feb 2023 09:54:19 +0100 Subject: [PATCH] Refactor the VirtualSensor API. Make the virtual sensor callback VirtualDevice-level instead of VirtualSensor-level. This allows for clients of the API to define a single callback that takes the sensor as an argument instead of defining callback per sensor. Also, this allows for mimicking the HAL APIs for direct channel support, as channel registration and unregistration are at HAL level and not at sensor level. This is a nicer solution because the VirtualDevice essentially acts as the sensor HAL. In more detail what this CL does: - Rename the VirtualSensorCallback and move it to its own file - Rename the callback method to onListenersChanged - Make VirtualSensor Parcelable so it can be passed to the callback - Move the VirtualSensor ownership to VirtualDeviceImpl - Make VirtualDevice id avaiable from VirtualSensor. - Return a boolean from the internal callbacks indicating success - Move the non-CTS VirtualDeviceParamsTest cases to the CTS one Bug: 266042170 Test: atest VirtualSensorConfigTest Test: atest SensorControllerTest Test: atest VirtualDeviceManagerServiceTest Change-Id: I14127f7a000d84ba41ade6ebda41c95a0c350fc9 --- core/api/system-current.txt | 14 +-- .../companion/virtual/IVirtualDevice.aidl | 12 +-- .../virtual/VirtualDeviceManager.java | 48 ++-------- .../virtual/VirtualDeviceParams.java | 90 +++++++++++++++++-- ...lback.aidl => IVirtualSensorCallback.aidl} | 12 ++- .../virtual/sensor/VirtualSensor.aidl | 19 ++++ .../virtual/sensor/VirtualSensor.java | 83 ++++++++++++----- .../virtual/sensor/VirtualSensorCallback.java | 53 +++++++++++ .../virtual/sensor/VirtualSensorConfig.java | 70 +-------------- .../sensor/VirtualSensorConfigTest.java | 46 ---------- .../companion/virtual/SensorController.java | 90 +++++++++++++------ .../companion/virtual/VirtualDeviceImpl.java | 60 +++++++++---- .../virtual/VirtualDeviceManagerService.java | 12 +++ .../virtual/VirtualDeviceManagerInternal.java | 10 +++ .../server/sensors/SensorManagerInternal.java | 7 +- .../android/server/sensors/SensorService.java | 5 +- ...om_android_server_sensor_SensorService.cpp | 25 +++--- services/proguard.flags | 2 +- .../virtual/SensorControllerTest.java | 9 +- .../VirtualDeviceManagerServiceTest.java | 59 +++++++++--- .../virtual/VirtualDeviceParamsTest.java | 85 ------------------ 21 files changed, 449 insertions(+), 362 deletions(-) rename core/java/android/companion/virtual/sensor/{IVirtualSensorStateChangeCallback.aidl => IVirtualSensorCallback.aidl} (66%) create mode 100644 core/java/android/companion/virtual/sensor/VirtualSensor.aidl create mode 100644 core/java/android/companion/virtual/sensor/VirtualSensorCallback.java delete mode 100644 services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index bbf56a9167644..7d474de77a9d5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3176,7 +3176,7 @@ package android.companion.virtual { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method public int getDeviceId(); - method @Nullable public android.companion.virtual.sensor.VirtualSensor getVirtualSensor(int, @NonNull String); + method @NonNull public java.util.List getVirtualSensorList(); method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); @@ -3231,6 +3231,7 @@ package android.companion.virtual { method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback); } } @@ -3282,14 +3283,18 @@ package android.companion.virtual.audio { package android.companion.virtual.sensor { - public class VirtualSensor { + public final class VirtualSensor implements android.os.Parcelable { + method public int describeContents(); + method public int getDeviceId(); method @NonNull public String getName(); method public int getType(); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 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 CREATOR; } - public static interface VirtualSensor.SensorStateChangeCallback { - method public void onStateChanged(boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); + public interface VirtualSensorCallback { + method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); } public final class VirtualSensorConfig implements android.os.Parcelable { @@ -3304,7 +3309,6 @@ 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 setStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensor.SensorStateChangeCallback); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String); } diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 9ab7cf9a8fc63..12882a2f47c9e 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -20,7 +20,7 @@ import android.app.PendingIntent; import android.companion.virtual.IVirtualDeviceIntentInterceptor; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; -import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback; +import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.content.IntentFilter; @@ -112,16 +112,10 @@ interface IVirtualDevice { boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event); /** - * Creates a virtual sensor, capable of injecting sensor events into the system. + * Returns all virtual sensors for this device. */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") - void createVirtualSensor(IBinder tokenm, in VirtualSensorConfig config); - - /** - * Removes the sensor corresponding to the given token from the system. - */ - @EnforcePermission("CREATE_VIRTUAL_DEVICE") - void unregisterSensor(IBinder token); + List getVirtualSensorList(); /** * Sends an event to the virtual sensor corresponding to the given token. diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 3e6b380be1f4d..b927666b7741c 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -35,7 +35,6 @@ import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChan import android.companion.virtual.camera.VirtualCameraDevice; import android.companion.virtual.camera.VirtualCameraInput; import android.companion.virtual.sensor.VirtualSensor; -import android.companion.virtual.sensor.VirtualSensorConfig; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -428,8 +427,6 @@ public final class VirtualDeviceManager { }; @Nullable private VirtualCameraDevice mVirtualCameraDevice; - @NonNull - private final List mVirtualSensors = new ArrayList<>(); @Nullable private VirtualAudioDevice mVirtualAudioDevice; @@ -448,10 +445,6 @@ public final class VirtualDeviceManager { params, mActivityListenerBinder, mSoundEffectListener); - final List virtualSensorConfigs = params.getVirtualSensorConfigs(); - for (int i = 0; i < virtualSensorConfigs.size(); ++i) { - mVirtualSensors.add(createVirtualSensor(virtualSensorConfigs.get(i))); - } } /** @@ -478,20 +471,19 @@ public final class VirtualDeviceManager { } /** - * Returns this device's sensor with the given type and name, if any. + * Returns this device's sensors. * * @see VirtualDeviceParams.Builder#addVirtualSensorConfig * - * @param type The type of the sensor. - * @param name The name of the sensor. - * @return The matching sensor if found, {@code null} otherwise. + * @return A list of all sensors for this device, or an empty list if no sensors exist. */ - @Nullable - public VirtualSensor getVirtualSensor(int type, @NonNull String name) { - return mVirtualSensors.stream() - .filter(sensor -> sensor.getType() == type && sensor.getName().equals(name)) - .findAny() - .orElse(null); + @NonNull + public List getVirtualSensorList() { + try { + return mVirtualDevice.getVirtualSensorList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -940,28 +932,6 @@ public final class VirtualDeviceManager { } } - /** - * Creates a virtual sensor, capable of injecting sensor events into the system. Only for - * internal use, since device sensors must remain valid for the entire lifetime of the - * device. - * - * @param config The configuration of the sensor. - * @hide - */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) - @NonNull - public VirtualSensor createVirtualSensor(@NonNull VirtualSensorConfig config) { - Objects.requireNonNull(config); - try { - final IBinder token = new Binder( - "android.hardware.sensor.VirtualSensor:" + config.getName()); - mVirtualDevice.createVirtualSensor(token, config); - return new VirtualSensor(config.getType(), config.getName(), mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Adds an activity listener to listen for events such as top activity change or virtual * display task stack became empty. diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index d4a0a08717136..d8076b5c0fd75 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -19,11 +19,18 @@ package android.companion.virtual; import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE; +import static java.util.concurrent.TimeUnit.MICROSECONDS; + +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.companion.virtual.sensor.IVirtualSensorCallback; +import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensorConfig; import android.content.ComponentName; import android.os.Parcel; @@ -37,11 +44,13 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; /** * Params that can be configured when creating virtual devices. @@ -190,6 +199,7 @@ public final class VirtualDeviceParams implements Parcelable { // Mapping of @PolicyType to @DevicePolicy @NonNull private final SparseIntArray mDevicePolicies; @NonNull private final List mVirtualSensorConfigs; + @Nullable private final IVirtualSensorCallback mVirtualSensorCallback; @RecentsPolicy private final int mDefaultRecentsPolicy; private final int mAudioPlaybackSessionId; @@ -207,6 +217,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable String name, @NonNull SparseIntArray devicePolicies, @NonNull List virtualSensorConfigs, + @Nullable IVirtualSensorCallback virtualSensorCallback, @RecentsPolicy int defaultRecentsPolicy, int audioPlaybackSessionId, int audioRecordingSessionId) { @@ -224,6 +235,7 @@ public final class VirtualDeviceParams implements Parcelable { mName = name; mDevicePolicies = Objects.requireNonNull(devicePolicies); mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); + mVirtualSensorCallback = virtualSensorCallback; mDefaultRecentsPolicy = defaultRecentsPolicy; mAudioPlaybackSessionId = audioPlaybackSessionId; mAudioRecordingSessionId = audioRecordingSessionId; @@ -244,6 +256,8 @@ public final class VirtualDeviceParams implements Parcelable { mDevicePolicies = parcel.readSparseIntArray(); mVirtualSensorConfigs = new ArrayList<>(); parcel.readTypedList(mVirtualSensorConfigs, VirtualSensorConfig.CREATOR); + mVirtualSensorCallback = + IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder()); mDefaultRecentsPolicy = parcel.readInt(); mAudioPlaybackSessionId = parcel.readInt(); mAudioRecordingSessionId = parcel.readInt(); @@ -371,6 +385,15 @@ public final class VirtualDeviceParams implements Parcelable { return mVirtualSensorConfigs; } + /** + * Returns the callback to get notified about changes in the sensor listeners. + * @hide + */ + @Nullable + public IVirtualSensorCallback getVirtualSensorCallback() { + return mVirtualSensorCallback; + } + /** * Returns the policy of how to handle activities in recents. * @@ -417,6 +440,8 @@ public final class VirtualDeviceParams implements Parcelable { dest.writeString8(mName); dest.writeSparseIntArray(mDevicePolicies); dest.writeTypedList(mVirtualSensorConfigs); + dest.writeStrongBinder( + mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null); dest.writeInt(mDefaultRecentsPolicy); dest.writeInt(mAudioPlaybackSessionId); dest.writeInt(mAudioRecordingSessionId); @@ -522,11 +547,38 @@ public final class VirtualDeviceParams implements Parcelable { private boolean mDefaultActivityPolicyConfigured = false; @Nullable private String mName; @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray(); - @NonNull private List mVirtualSensorConfigs = new ArrayList<>(); private int mDefaultRecentsPolicy; private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE; private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE; + @NonNull private List mVirtualSensorConfigs = new ArrayList<>(); + @Nullable + private IVirtualSensorCallback mVirtualSensorCallback; + + private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { + @NonNull + private final Executor mExecutor; + @NonNull + private final VirtualSensorCallback mCallback; + + VirtualSensorCallbackDelegate(@NonNull @CallbackExecutor Executor executor, + @NonNull VirtualSensorCallback callback) { + mCallback = callback; + mExecutor = executor; + } + + @Override + public void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, + int samplingPeriodMicros, int batchReportLatencyMicros) { + final Duration samplingPeriod = + Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros)); + final Duration batchReportingLatency = + Duration.ofNanos(MICROSECONDS.toNanos(batchReportLatencyMicros)); + mExecutor.execute(() -> mCallback.onConfigurationChanged( + sensor, enabled, samplingPeriod, batchReportingLatency)); + } + } + /** * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY} * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}. @@ -730,6 +782,24 @@ public final class VirtualDeviceParams implements Parcelable { return this; } + /** + * Sets the callback to get notified about changes in the sensor listeners. + * + * @param executor The executor where the callback is executed on. + * @param callback The callback to get notified when the state of the sensor + * listeners has changed, see {@link VirtualSensorCallback} + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setVirtualSensorCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull VirtualSensorCallback callback) { + mVirtualSensorCallback = new VirtualSensorCallbackDelegate( + Objects.requireNonNull(executor), + Objects.requireNonNull(callback)); + return this; + } + /** * Sets the policy to indicate how activities are handled in recents. * @@ -798,12 +868,17 @@ public final class VirtualDeviceParams implements Parcelable { */ @NonNull public VirtualDeviceParams build() { - if (!mVirtualSensorConfigs.isEmpty() - && (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT) - != DEVICE_POLICY_CUSTOM)) { - throw new IllegalArgumentException( - "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating " - + "virtual sensors."); + if (!mVirtualSensorConfigs.isEmpty()) { + if (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT) + != DEVICE_POLICY_CUSTOM) { + throw new IllegalArgumentException( + "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating " + + "virtual sensors."); + } + if (mVirtualSensorCallback == null) { + throw new IllegalArgumentException( + "VirtualSensorCallback is required for creating virtual sensors."); + } } if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE @@ -837,6 +912,7 @@ public final class VirtualDeviceParams implements Parcelable { mName, mDevicePolicies, mVirtualSensorConfigs, + mVirtualSensorCallback, mDefaultRecentsPolicy, mAudioPlaybackSessionId, mAudioRecordingSessionId); diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl similarity index 66% rename from core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl rename to core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl index b99cc7eb67a53..7da9c3224400a 100644 --- a/core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl +++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl @@ -16,20 +16,24 @@ package android.companion.virtual.sensor; +import android.companion.virtual.sensor.VirtualSensor; + /** - * Interface for notification of listener registration changes for a virtual sensor. + * Interface for notifying the sensor owner about whether and how sensor events should be injected. * * @hide */ -oneway interface IVirtualSensorStateChangeCallback { +oneway interface IVirtualSensorCallback { /** - * Called when the registered listeners to a virtual sensor have changed. + * Called when the requested sensor event injection parameters have changed. * + * @param sensor The sensor whose requested injection parameters have changed. * @param enabled Whether the sensor is enabled. * @param samplingPeriodMicros The requested sensor's sampling period in microseconds. * @param batchReportingLatencyMicros The requested maximum time interval in microseconds * between the delivery of two batches of sensor events. */ - void onStateChanged(boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); + void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros, + int batchReportLatencyMicros); } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.aidl b/core/java/android/companion/virtual/sensor/VirtualSensor.aidl new file mode 100644 index 0000000000000..ccb597aca0ac4 --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensor.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + +parcelable VirtualSensor; diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java index 58a5387c56517..bda44d4028233 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensor.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java @@ -22,10 +22,10 @@ import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.hardware.Sensor; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; -import java.time.Duration; - /** * Representation of a sensor on a remote device, capable of sending events, such as an * accelerometer or a gyroscope. @@ -35,24 +35,8 @@ import java.time.Duration; * @hide */ @SystemApi -public class VirtualSensor { - - /** - * Interface for notification of listener registration changes for a virtual sensor. - */ - public interface SensorStateChangeCallback { - /** - * Called when the registered listeners to a virtual sensor have changed. - * - * @param enabled Whether the sensor is enabled. - * @param samplingPeriod The requested sampling period of the sensor. - * @param batchReportLatency The requested maximum time interval between the delivery of two - * batches of sensor events. - */ - void onStateChanged(boolean enabled, @NonNull Duration samplingPeriod, - @NonNull Duration batchReportLatency); - } - +public final class VirtualSensor implements Parcelable { + private final int mHandle; private final int mType; private final String mName; private final IVirtualDevice mVirtualDevice; @@ -61,13 +45,32 @@ public class VirtualSensor { /** * @hide */ - public VirtualSensor(int type, String name, IVirtualDevice virtualDevice, IBinder token) { + public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice, + IBinder token) { + mHandle = handle; mType = type; mName = name; mVirtualDevice = virtualDevice; mToken = token; } + private VirtualSensor(Parcel parcel) { + mHandle = parcel.readInt(); + mType = parcel.readInt(); + mName = parcel.readString8(); + mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder()); + mToken = parcel.readStrongBinder(); + } + + /** + * Returns the unique handle of the sensor. + * + * @hide + */ + public int getHandle() { + return mHandle; + } + /** * Returns the type of the sensor. * @@ -86,6 +89,32 @@ public class VirtualSensor { return mName; } + /** + * Returns the identifier of the + * {@link android.companion.virtual.VirtualDeviceManager.VirtualDevice} this sensor belongs to. + */ + public int getDeviceId() { + try { + return mVirtualDevice.getDeviceId(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeInt(mHandle); + parcel.writeInt(mType); + parcel.writeString8(mName); + parcel.writeStrongBinder(mVirtualDevice.asBinder()); + parcel.writeStrongBinder(mToken); + } + /** * Send a sensor event to the system. */ @@ -97,4 +126,16 @@ public class VirtualSensor { throw e.rethrowFromSystemServer(); } } + + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public VirtualSensor createFromParcel(Parcel in) { + return new VirtualSensor(in); + } + + public VirtualSensor[] newArray(int size) { + return new VirtualSensor[size]; + } + }; } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java new file mode 100644 index 0000000000000..e097189413027 --- /dev/null +++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual.sensor; + + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.time.Duration; + +/** + * Interface for notifying the sensor owner about whether and how sensor events should be injected. + * + *

This callback can be used for controlling the sensor event injection - e.g. if the sensor is + * not enabled, then no events should be injected. Similarly, the rate and delay of the injected + * events that the registered listeners expect are specified here. + * + *

The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when + * the device is created and destroyed when the device is destroyed. + * + * @hide + */ +@SystemApi +public interface VirtualSensorCallback { + /** + * Called when the requested sensor event injection parameters have changed. + * + *

This is effectively called when the registered listeners to a virtual sensor have changed. + * + * @param sensor The sensor whose requested injection parameters have changed. + * @param enabled Whether the sensor is enabled. True if any listeners are currently registered, + * and false otherwise. + * @param samplingPeriod The requested sampling period of the sensor. + * @param batchReportLatency The requested maximum time interval between the delivery of two + * batches of sensor events. + */ + void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, + @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency); +} diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index eb2f9dde48dbf..6d45365ebbd44 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -16,20 +16,15 @@ package android.companion.virtual.sensor; -import static java.util.concurrent.TimeUnit.MICROSECONDS; -import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.hardware.Sensor; import android.os.Parcel; import android.os.Parcelable; -import java.time.Duration; import java.util.Objects; -import java.util.concurrent.Executor; /** * Configuration for creation of a virtual sensor. @@ -44,23 +39,17 @@ public final class VirtualSensorConfig implements Parcelable { private final String mName; @Nullable private final String mVendor; - @Nullable - private final IVirtualSensorStateChangeCallback mStateChangeCallback; - private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor, - @Nullable IVirtualSensorStateChangeCallback stateChangeCallback) { + private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) { mType = type; mName = name; mVendor = vendor; - mStateChangeCallback = stateChangeCallback; } private VirtualSensorConfig(@NonNull Parcel parcel) { mType = parcel.readInt(); mName = parcel.readString8(); mVendor = parcel.readString8(); - mStateChangeCallback = - IVirtualSensorStateChangeCallback.Stub.asInterface(parcel.readStrongBinder()); } @Override @@ -73,8 +62,6 @@ public final class VirtualSensorConfig implements Parcelable { parcel.writeInt(mType); parcel.writeString8(mName); parcel.writeString8(mVendor); - parcel.writeStrongBinder( - mStateChangeCallback != null ? mStateChangeCallback.asBinder() : null); } /** @@ -104,15 +91,6 @@ public final class VirtualSensorConfig implements Parcelable { return mVendor; } - /** - * Returns the callback to get notified about changes in the sensor listeners. - * @hide - */ - @Nullable - public IVirtualSensorStateChangeCallback getStateChangeCallback() { - return mStateChangeCallback; - } - /** * Builder for {@link VirtualSensorConfig}. */ @@ -123,32 +101,6 @@ public final class VirtualSensorConfig implements Parcelable { private final String mName; @Nullable private String mVendor; - @Nullable - private IVirtualSensorStateChangeCallback mStateChangeCallback; - - private static class SensorStateChangeCallbackDelegate - extends IVirtualSensorStateChangeCallback.Stub { - @NonNull - private final Executor mExecutor; - @NonNull - private final VirtualSensor.SensorStateChangeCallback mCallback; - - SensorStateChangeCallbackDelegate(@NonNull @CallbackExecutor Executor executor, - @NonNull VirtualSensor.SensorStateChangeCallback callback) { - mCallback = callback; - mExecutor = executor; - } - @Override - public void onStateChanged(boolean enabled, int samplingPeriodMicros, - int batchReportLatencyMicros) { - final Duration samplingPeriod = - Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros)); - final Duration batchReportingLatency = - Duration.ofNanos(MICROSECONDS.toNanos(batchReportLatencyMicros)); - mExecutor.execute(() -> mCallback.onStateChanged( - enabled, samplingPeriod, batchReportingLatency)); - } - } /** * Creates a new builder. @@ -167,7 +119,7 @@ public final class VirtualSensorConfig implements Parcelable { */ @NonNull public VirtualSensorConfig build() { - return new VirtualSensorConfig(mType, mName, mVendor, mStateChangeCallback); + return new VirtualSensorConfig(mType, mName, mVendor); } /** @@ -178,24 +130,6 @@ public final class VirtualSensorConfig implements Parcelable { mVendor = vendor; return this; } - - /** - * Sets the callback to get notified about changes in the sensor listeners. - * - * @param executor The executor where the callback is executed on. - * @param callback The callback to get notified when the state of the sensor - * listeners has changed, see {@link VirtualSensor.SensorStateChangeCallback} - */ - @SuppressLint("MissingGetterMatchingBuilder") - @NonNull - public VirtualSensorConfig.Builder setStateChangeCallback( - @NonNull @CallbackExecutor Executor executor, - @NonNull VirtualSensor.SensorStateChangeCallback callback) { - mStateChangeCallback = new SensorStateChangeCallbackDelegate( - Objects.requireNonNull(executor), - Objects.requireNonNull(callback)); - return this; - } } @NonNull 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 2a1881e56a6d3..f97099d045722 100644 --- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java +++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java @@ -20,28 +20,13 @@ import static android.hardware.Sensor.TYPE_ACCELEROMETER; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; - import android.os.Parcel; import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.os.BackgroundThread; - -import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.time.Duration; @Presubmit @RunWith(AndroidJUnit4.class) @@ -50,23 +35,11 @@ public class VirtualSensorConfigTest { private static final String SENSOR_NAME = "VirtualSensorName"; private static final String SENSOR_VENDOR = "VirtualSensorVendor"; - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - private VirtualSensor.SensorStateChangeCallback mSensorCallback; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - @Test public void parcelAndUnparcel_matches() { final VirtualSensorConfig originalConfig = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) .setVendor(SENSOR_VENDOR) - .setStateChangeCallback(BackgroundThread.getExecutor(), mSensorCallback) .build(); final Parcel parcel = Parcel.obtain(); originalConfig.writeToParcel(parcel, /* flags= */ 0); @@ -76,7 +49,6 @@ public class VirtualSensorConfigTest { assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType()); assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName()); assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor()); - assertThat(recreatedConfig.getStateChangeCallback()).isNotNull(); } @Test @@ -84,23 +56,5 @@ public class VirtualSensorConfigTest { final VirtualSensorConfig config = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build(); assertThat(config.getVendor()).isNull(); - assertThat(config.getStateChangeCallback()).isNull(); - } - - @Test - public void sensorConfig_sensorCallbackInvocation() throws Exception { - final VirtualSensorConfig config = - new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) - .setStateChangeCallback(BackgroundThread.getExecutor(), mSensorCallback) - .build(); - - final Duration samplingPeriod = Duration.ofMillis(123); - final Duration batchLatency = Duration.ofMillis(456); - - config.getStateChangeCallback().onStateChanged(true, - (int) MILLISECONDS.toMicros(samplingPeriod.toMillis()), - (int) MILLISECONDS.toMicros(batchLatency.toMillis())); - - verify(mSensorCallback, timeout(1000)).onStateChanged(true, samplingPeriod, batchLatency); } } 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 ec7e993ec30ed..7804ebf1583d9 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -17,7 +17,9 @@ package com.android.server.companion.virtual; import android.annotation.NonNull; -import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback; +import android.annotation.Nullable; +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.os.IBinder; @@ -40,17 +42,30 @@ public class SensorController { private static final String TAG = "SensorController"; + // See system/core/libutils/include/utils/Errors.h + private static final int OK = 0; + private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value + private static final int BAD_VALUE = -22; + private final Object mLock; private final int mVirtualDeviceId; @GuardedBy("mLock") private final Map mSensorDescriptors = new ArrayMap<>(); + @NonNull + private final SensorManagerInternal.RuntimeSensorCallback mRuntimeSensorCallback; private final SensorManagerInternal mSensorManagerInternal; + private final VirtualDeviceManagerInternal mVdmInternal; - public SensorController(@NonNull Object lock, int virtualDeviceId) { + + + public SensorController(@NonNull Object lock, int virtualDeviceId, + @Nullable IVirtualSensorCallback virtualSensorCallback) { mLock = lock; mVirtualDeviceId = virtualDeviceId; + mRuntimeSensorCallback = new RuntimeSensorCallbackWrapper(virtualSensorCallback); mSensorManagerInternal = LocalServices.getService(SensorManagerInternal.class); + mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class); } void close() { @@ -67,36 +82,23 @@ public class SensorController { } } - void createSensor(@NonNull IBinder deviceToken, @NonNull VirtualSensorConfig config) { - Objects.requireNonNull(deviceToken); + int createSensor(@NonNull IBinder sensorToken, @NonNull VirtualSensorConfig config) { + Objects.requireNonNull(sensorToken); Objects.requireNonNull(config); try { - createSensorInternal(deviceToken, config); + return createSensorInternal(sensorToken, config); } catch (SensorCreationException e) { throw new RuntimeException( "Failed to create virtual sensor '" + config.getName() + "'.", e); } } - private void createSensorInternal(IBinder deviceToken, VirtualSensorConfig config) + private int createSensorInternal(IBinder sensorToken, VirtualSensorConfig config) throws SensorCreationException { - final SensorManagerInternal.RuntimeSensorStateChangeCallback runtimeSensorCallback = - (enabled, samplingPeriodMicros, batchReportLatencyMicros) -> { - IVirtualSensorStateChangeCallback callback = config.getStateChangeCallback(); - if (callback != null) { - try { - callback.onStateChanged( - enabled, samplingPeriodMicros, batchReportLatencyMicros); - } catch (RemoteException e) { - throw new RuntimeException("Failed to call sensor callback.", e); - } - } - }; - final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId, config.getType(), config.getName(), config.getVendor() == null ? "" : config.getVendor(), - runtimeSensorCallback); + mRuntimeSensorCallback); if (handle <= 0) { throw new SensorCreationException("Received an invalid virtual sensor handle."); } @@ -104,8 +106,8 @@ public class SensorController { // The handle is valid from here, so ensure that all failures clean it up. final BinderDeathRecipient binderDeathRecipient; try { - binderDeathRecipient = new BinderDeathRecipient(deviceToken); - deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); + binderDeathRecipient = new BinderDeathRecipient(sensorToken); + sensorToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); } catch (RemoteException e) { mSensorManagerInternal.removeRuntimeSensor(handle); throw new SensorCreationException("Client died before sensor could be created.", e); @@ -114,8 +116,9 @@ public class SensorController { synchronized (mLock) { SensorDescriptor sensorDescriptor = new SensorDescriptor( handle, config.getType(), config.getName(), binderDeathRecipient); - mSensorDescriptors.put(deviceToken, sensorDescriptor); + mSensorDescriptors.put(sensorToken, sensorDescriptor); } + return handle; } boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) { @@ -178,6 +181,39 @@ public class SensorController { } } + private final class RuntimeSensorCallbackWrapper + implements SensorManagerInternal.RuntimeSensorCallback { + @Nullable + private IVirtualSensorCallback mCallback; + + RuntimeSensorCallbackWrapper(@Nullable IVirtualSensorCallback callback) { + mCallback = callback; + } + + @Override + public int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros, + int batchReportLatencyMicros) { + if (mCallback == null) { + Slog.e(TAG, "No sensor callback configured for sensor handle " + handle); + return BAD_VALUE; + } + VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, handle); + if (sensor == null) { + Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId + + " and sensor handle=" + handle); + return BAD_VALUE; + } + try { + mCallback.onConfigurationChanged(sensor, enabled, samplingPeriodMicros, + batchReportLatencyMicros); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + return UNKNOWN_ERROR; + } + return OK; + } + } + @VisibleForTesting static final class SensorDescriptor { @@ -207,10 +243,10 @@ public class SensorController { } private final class BinderDeathRecipient implements IBinder.DeathRecipient { - private final IBinder mDeviceToken; + private final IBinder mSensorToken; - BinderDeathRecipient(IBinder deviceToken) { - mDeviceToken = deviceToken; + BinderDeathRecipient(IBinder sensorToken) { + mSensorToken = sensorToken; } @Override @@ -219,7 +255,7 @@ public class SensorController { // quitting, which removes this death recipient. If this is invoked, the remote end // died, or they disposed of the object without properly unregistering. Slog.e(TAG, "Virtual sensor controller binder died"); - unregisterSensor(mDeviceToken); + unregisterSensor(mSensorToken); } } 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 7fce442377208..b4dcf43c2e1a3 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -41,6 +41,7 @@ import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; +import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.content.ComponentName; @@ -86,6 +87,8 @@ import com.android.server.companion.virtual.audio.VirtualAudioController; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -131,6 +134,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @GuardedBy("mVirtualDeviceLock") @Nullable private LocaleList mLocaleList = null; + // This device's sensors, keyed by sensor handle. + @GuardedBy("mVirtualDeviceLock") + private SparseArray mVirtualSensors = new SparseArray<>(); + @GuardedBy("mVirtualDeviceLock") + private List mVirtualSensorList = null; private ActivityListener createListenerAdapter() { return new ActivityListener() { @@ -240,10 +248,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mInputController = inputController; } if (sensorController == null) { - mSensorController = new SensorController(mVirtualDeviceLock, mDeviceId); + mSensorController = new SensorController( + mVirtualDeviceLock, mDeviceId, mParams.getVirtualSensorCallback()); } else { mSensorController = sensorController; } + final List virtualSensorConfigs = mParams.getVirtualSensorConfigs(); + for (int i = 0; i < virtualSensorConfigs.size(); ++i) { + createVirtualSensor(virtualSensorConfigs.get(i)); + } mCameraAccessController = cameraAccessController; mCameraAccessController.startObservingIfNeeded(); mOnDeviceCloseListener = onDeviceCloseListener; @@ -384,6 +397,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mVirtualAudioController = null; } mLocaleList = null; + mVirtualSensorList = null; + mVirtualSensors.clear(); } mOnDeviceCloseListener.onClose(mDeviceId); mAppToken.unlinkToDeath(this, 0); @@ -698,17 +713,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } - @Override // Binder call - @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) - public void createVirtualSensor( - @NonNull IBinder deviceToken, - @NonNull VirtualSensorConfig config) { - super.createVirtualSensor_enforcePermission(); - Objects.requireNonNull(config); - Objects.requireNonNull(deviceToken); + private void createVirtualSensor(@NonNull VirtualSensorConfig config) { + final IBinder sensorToken = + new Binder("android.hardware.sensor.VirtualSensor:" + config.getName()); final long ident = Binder.clearCallingIdentity(); try { - mSensorController.createSensor(deviceToken, config); + int handle = mSensorController.createSensor(sensorToken, config); + VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(), + this, sensorToken); + synchronized (mVirtualDeviceLock) { + mVirtualSensors.put(handle, sensor); + } } finally { Binder.restoreCallingIdentity(ident); } @@ -716,13 +731,24 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) - public void unregisterSensor(@NonNull IBinder token) { - super.unregisterSensor_enforcePermission(); - final long ident = Binder.clearCallingIdentity(); - try { - mSensorController.unregisterSensor(token); - } finally { - Binder.restoreCallingIdentity(ident); + @Nullable + public List getVirtualSensorList() { + super.getVirtualSensorList_enforcePermission(); + synchronized (mVirtualDeviceLock) { + if (mVirtualSensorList == null) { + mVirtualSensorList = new ArrayList<>(); + for (int i = 0; i < mVirtualSensors.size(); ++i) { + mVirtualSensorList.add(mVirtualSensors.valueAt(i)); + } + mVirtualSensorList = Collections.unmodifiableList(mVirtualSensorList); + } + return mVirtualSensorList; + } + } + + VirtualSensor getVirtualSensorByHandle(int handle) { + synchronized (mVirtualDeviceLock) { + return mVirtualSensors.get(handle); } } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index f39c32df18441..9bb05a6bfe5dc 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -34,6 +34,7 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; +import android.companion.virtual.sensor.VirtualSensor; import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManagerInternal; @@ -469,6 +470,17 @@ public class VirtualDeviceManagerService extends SystemService { } } + @Override + public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) { + synchronized (mVirtualDeviceManagerLock) { + VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); + if (virtualDevice != null) { + return virtualDevice.getVirtualSensorByHandle(handle); + } + } + return null; + } + @Override public @NonNull ArraySet getDeviceIdsForUid(int uid) { ArraySet result = new ArraySet<>(); diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index 001cb10e1b295..a33533841e89e 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.companion.virtual; import android.annotation.NonNull; import android.annotation.Nullable; import android.companion.virtual.IVirtualDevice; +import android.companion.virtual.sensor.VirtualSensor; import android.os.LocaleList; import android.util.ArraySet; @@ -77,6 +78,15 @@ public abstract class VirtualDeviceManagerInternal { */ public abstract int getDeviceOwnerUid(int deviceId); + /** + * Returns the VirtualSensor for the given deviceId and sensor handle, if any. + * + * @param deviceId the virtual device that owns the sensor + * @param handle the sensor handle + * @return the VirtualSensor with the given handle, or {@code null} if no such sensor exists. + */ + public abstract @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle); + /** * Finds VirtualDevices where an app is running. * diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java index f17e5e73ceb01..41c2fbfd33142 100644 --- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java +++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java @@ -58,7 +58,7 @@ public abstract class SensorManagerInternal { * @return The sensor handle. */ public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorStateChangeCallback callback); + @NonNull String vendor, @NonNull RuntimeSensorCallback callback); /** * Unregisters the sensor with the given handle from the framework. @@ -95,11 +95,12 @@ public abstract class SensorManagerInternal { * {@link #createRuntimeSensor}, i.e. the dynamic sensors created via the dynamic sensor HAL are * not covered. */ - public interface RuntimeSensorStateChangeCallback { + public interface RuntimeSensorCallback { /** * Invoked when the listeners of the runtime sensor have changed. + * Returns an error code if the invocation was unsuccessful, zero otherwise. */ - void onStateChanged(boolean enabled, int samplingPeriodMicros, + int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); } } diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java index d8e3bddd64320..979065950dc4f 100644 --- a/services/core/java/com/android/server/sensors/SensorService.java +++ b/services/core/java/com/android/server/sensors/SensorService.java @@ -56,8 +56,7 @@ 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.RuntimeSensorStateChangeCallback callback); + String name, String vendor, 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); @@ -96,7 +95,7 @@ public class SensorService extends SystemService { class LocalService extends SensorManagerInternal { @Override public int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorStateChangeCallback callback) { + @NonNull String vendor, @NonNull RuntimeSensorCallback callback) { synchronized (mLock) { int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, callback); diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index 10d8b42c2979c..356e9a95e311f 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -32,13 +32,13 @@ "com/android/server/sensors/SensorManagerInternal$ProximityActiveListener" #define RUNTIME_SENSOR_CALLBACK_CLASS \ - "com/android/server/sensors/SensorManagerInternal$RuntimeSensorStateChangeCallback" + "com/android/server/sensors/SensorManagerInternal$RuntimeSensorCallback" namespace android { static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnProximityActive; -static jmethodID sMethodIdOnStateChanged; +static jmethodID sMethodIdOnConfigurationChanged; class NativeSensorService { public: @@ -67,13 +67,13 @@ private: }; sp mProximityActiveListenerDelegate; - class RuntimeSensorCallbackDelegate : public SensorService::RuntimeSensorStateChangeCallback { + class RuntimeSensorCallbackDelegate : public SensorService::RuntimeSensorCallback { public: RuntimeSensorCallbackDelegate(JNIEnv* env, jobject callback); ~RuntimeSensorCallbackDelegate(); - void onStateChanged(bool enabled, int64_t samplingPeriodNs, - int64_t batchReportLatencyNs) override; + status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs, + int64_t batchReportLatencyNs) override; private: jobject mCallback; @@ -231,12 +231,13 @@ NativeSensorService::RuntimeSensorCallbackDelegate::~RuntimeSensorCallbackDelega AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mCallback); } -void NativeSensorService::RuntimeSensorCallbackDelegate::onStateChanged( - bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) { +status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged( + int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) { auto jniEnv = GetOrAttachJNIEnvironment(sJvm); - jniEnv->CallVoidMethod(mCallback, sMethodIdOnStateChanged, static_cast(enabled), - static_cast(ns2us(samplingPeriodNs)), - static_cast(ns2us(batchReportLatencyNs))); + return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged, + static_cast(handle), static_cast(enabled), + static_cast(ns2us(samplingPeriodNs)), + static_cast(ns2us(batchReportLatencyNs))); } static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) { @@ -292,8 +293,8 @@ 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); - sMethodIdOnStateChanged = - GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onStateChanged", "(ZII)V"); + sMethodIdOnConfigurationChanged = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I"); return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods, NELEM(methods)); } diff --git a/services/proguard.flags b/services/proguard.flags index c13304430d98a..c31abbb5c0d6b 100644 --- a/services/proguard.flags +++ b/services/proguard.flags @@ -95,7 +95,7 @@ -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; } --keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$RuntimeSensorStateChangeCallback { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$RuntimeSensorCallback { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; } 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 ef8a49f95a496..6431e88b1acb7 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 @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; import android.hardware.Sensor; @@ -54,6 +55,8 @@ public class SensorControllerTest { @Mock private SensorManagerInternal mSensorManagerInternalMock; + @Mock + private IVirtualSensorCallback mVirtualSensorCallback; private SensorController mSensorController; private VirtualSensorEvent mSensorEvent; private VirtualSensorConfig mVirtualSensorConfig; @@ -66,7 +69,8 @@ public class SensorControllerTest { LocalServices.removeServiceForTest(SensorManagerInternal.class); LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); - mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID); + mSensorController = + new SensorController(new Object(), VIRTUAL_DEVICE_ID, mVirtualSensorCallback); mSensorEvent = new VirtualSensorEvent.Builder(new float[] { 1f, 2f, 3f}).build(); mVirtualSensorConfig = new VirtualSensorConfig.Builder(Sensor.TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME) @@ -135,6 +139,7 @@ public class SensorControllerTest { private void doCreateSensorSuccessfully() { doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( anyInt(), anyInt(), anyString(), anyString(), any()); - mSensorController.createSensor(mSensorToken, mVirtualSensorConfig); + 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 ce5ddb060a29e..0cd50f03fbfa2 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 @@ -55,6 +55,9 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; +import android.companion.virtual.sensor.IVirtualSensorCallback; +import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtual.sensor.VirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensorConfig; import android.content.ComponentName; import android.content.Context; @@ -102,6 +105,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import com.android.internal.app.BlockedAppStreamingActivity; +import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; import com.android.server.sensors.SensorManagerInternal; @@ -225,6 +229,10 @@ public class VirtualDeviceManagerServiceTest { @Mock private SensorManagerInternal mSensorManagerInternalMock; @Mock + private IVirtualSensorCallback mVirtualSensorCallback; + @Mock + private VirtualSensorCallback mSensorCallback; + @Mock private IVirtualDeviceActivityListener mActivityListener; @Mock private IVirtualDeviceSoundEffectListener mSoundEffectListener; @@ -333,7 +341,8 @@ public class VirtualDeviceManagerServiceTest { mInputController = new InputController(new Object(), mNativeWrapperMock, new Handler(TestableLooper.get(this).getLooper()), mContext.getSystemService(WindowManager.class), threadVerifier); - mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID_1); + mSensorController = + new SensorController(new Object(), VIRTUAL_DEVICE_ID_1, mVirtualSensorCallback); mCameraAccessController = new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback); @@ -450,6 +459,42 @@ public class VirtualDeviceManagerServiceTest { assertThat(ownerUid).isEqualTo(Process.INVALID_UID); } + @Test + public void getVirtualSensor_defaultDeviceId_returnsNull() { + assertThat(mLocalService.getVirtualSensor(DEVICE_ID_DEFAULT, SENSOR_HANDLE)).isNull(); + } + + @Test + public void getVirtualSensor_invalidDeviceId_returnsNull() { + assertThat(mLocalService.getVirtualSensor(DEVICE_ID_INVALID, SENSOR_HANDLE)).isNull(); + } + + @Test + public void getVirtualSensor_noSensors_returnsNull() { + assertThat(mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE)).isNull(); + } + + @Test + public void getVirtualSensor_returnsCorrectSensor() { + VirtualDeviceParams params = new VirtualDeviceParams.Builder() + .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) + .addVirtualSensorConfig( + new VirtualSensorConfig.Builder(Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1) + .build()) + .setVirtualSensorCallback(BackgroundThread.getExecutor(), mSensorCallback) + .build(); + + doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( + anyInt(), anyInt(), anyString(), anyString(), any()); + mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); + + VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE); + assertThat(sensor).isNotNull(); + assertThat(sensor.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID_1); + assertThat(sensor.getHandle()).isEqualTo(SENSOR_HANDLE); + assertThat(sensor.getType()).isEqualTo(Sensor.TYPE_ACCELEROMETER); + } + @Test public void getDeviceIdsForUid_noRunningApps_returnsNull() { Set deviceIds = mLocalService.getDeviceIdsForUid(UID_1); @@ -887,18 +932,6 @@ public class VirtualDeviceManagerServiceTest { } } - @Test - public void createVirtualSensor_noPermission_failsSecurityException() { - try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { - assertThrows( - SecurityException.class, - () -> mDeviceImpl.createVirtualSensor( - BINDER, - new VirtualSensorConfig.Builder( - Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1).build())); - } - } - @Test public void onAudioSessionStarting_noPermission_failsSecurityException() { mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java deleted file mode 100644 index 798650dcc9cef..0000000000000 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.companion.virtual; - -import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; -import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; -import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS; -import static android.hardware.Sensor.TYPE_ACCELEROMETER; - -import static com.google.common.truth.Truth.assertThat; - -import android.companion.virtual.VirtualDeviceParams; -import android.companion.virtual.sensor.VirtualSensorConfig; -import android.os.Parcel; -import android.os.UserHandle; -import android.platform.test.annotations.Presubmit; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.List; -import java.util.Set; - -@Presubmit -@RunWith(AndroidJUnit4.class) -public class VirtualDeviceParamsTest { - - private static final String SENSOR_NAME = "VirtualSensorName"; - private static final String SENSOR_VENDOR = "VirtualSensorVendor"; - private static final int PLAYBACK_SESSION_ID = 42; - private static final int RECORDING_SESSION_ID = 77; - - @Test - public void parcelable_shouldRecreateSuccessfully() { - VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder() - .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) - .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456))) - .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) - .setDevicePolicy(POLICY_TYPE_AUDIO, DEVICE_POLICY_CUSTOM) - .setAudioPlaybackSessionId(PLAYBACK_SESSION_ID) - .setAudioRecordingSessionId(RECORDING_SESSION_ID) - .addVirtualSensorConfig( - new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) - .setVendor(SENSOR_VENDOR) - .build()) - .build(); - Parcel parcel = Parcel.obtain(); - originalParams.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - - VirtualDeviceParams params = VirtualDeviceParams.CREATOR.createFromParcel(parcel); - assertThat(params).isEqualTo(originalParams); - assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED); - assertThat(params.getUsersWithMatchingAccounts()) - .containsExactly(UserHandle.of(123), UserHandle.of(456)); - assertThat(params.getDevicePolicy(POLICY_TYPE_SENSORS)).isEqualTo(DEVICE_POLICY_CUSTOM); - assertThat(params.getDevicePolicy(POLICY_TYPE_AUDIO)).isEqualTo(DEVICE_POLICY_CUSTOM); - assertThat(params.getAudioPlaybackSessionId()).isEqualTo(PLAYBACK_SESSION_ID); - assertThat(params.getAudioRecordingSessionId()).isEqualTo(RECORDING_SESSION_ID); - - List sensorConfigs = params.getVirtualSensorConfigs(); - assertThat(sensorConfigs).hasSize(1); - VirtualSensorConfig sensorConfig = sensorConfigs.get(0); - assertThat(sensorConfig.getType()).isEqualTo(TYPE_ACCELEROMETER); - assertThat(sensorConfig.getName()).isEqualTo(SENSOR_NAME); - assertThat(sensorConfig.getVendor()).isEqualTo(SENSOR_VENDOR); - assertThat(sensorConfig.getStateChangeCallback()).isNull(); - } -}