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
This commit is contained in:
Vladimir Komsiyski
2023-02-10 09:54:19 +01:00
parent 437eb02ece
commit 9d5084dac2
21 changed files with 449 additions and 362 deletions

View File

@@ -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<android.companion.virtual.sensor.VirtualSensor> 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<android.os.UserHandle>);
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<android.companion.virtual.sensor.VirtualSensor> 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);
}

View File

@@ -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<VirtualSensor> getVirtualSensorList();
/**
* Sends an event to the virtual sensor corresponding to the given token.

View File

@@ -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<VirtualSensor> mVirtualSensors = new ArrayList<>();
@Nullable
private VirtualAudioDevice mVirtualAudioDevice;
@@ -448,10 +445,6 @@ public final class VirtualDeviceManager {
params,
mActivityListenerBinder,
mSoundEffectListener);
final List<VirtualSensorConfig> 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<VirtualSensor> 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.

View File

@@ -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<VirtualSensorConfig> 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<VirtualSensorConfig> 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<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>();
private int mDefaultRecentsPolicy;
private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE;
private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE;
@NonNull private List<VirtualSensorConfig> 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);

View File

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

View File

@@ -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;

View File

@@ -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<VirtualSensor> CREATOR =
new Parcelable.Creator<VirtualSensor>() {
public VirtualSensor createFromParcel(Parcel in) {
return new VirtualSensor(in);
}
public VirtualSensor[] newArray(int size) {
return new VirtualSensor[size];
}
};
}

View File

@@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>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);
}

View File

@@ -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

View File

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

View File

@@ -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<IBinder, SensorDescriptor> 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);
}
}

View File

@@ -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<VirtualSensor> mVirtualSensors = new SparseArray<>();
@GuardedBy("mVirtualDeviceLock")
private List<VirtualSensor> 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<VirtualSensorConfig> 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<VirtualSensor> 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);
}
}

View File

@@ -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<Integer> getDeviceIdsForUid(int uid) {
ArraySet<Integer> result = new ArraySet<>();

View File

@@ -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.
*

View File

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

View File

@@ -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);

View File

@@ -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<ProximityActiveListenerDelegate> 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<jboolean>(enabled),
static_cast<jint>(ns2us(samplingPeriodNs)),
static_cast<jint>(ns2us(batchReportLatencyNs)));
return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged,
static_cast<jint>(handle), static_cast<jboolean>(enabled),
static_cast<jint>(ns2us(samplingPeriodNs)),
static_cast<jint>(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));
}

View File

@@ -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 { *; }

View File

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

View File

@@ -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<Integer> 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);

View File

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