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(); - } -}