Merge "Refactor the VirtualSensor API."

This commit is contained in:
Vladimir Komsiyski
2023-02-13 21:08:09 +00:00
committed by Android (Google) Code Review
21 changed files with 449 additions and 362 deletions

View File

@@ -3187,7 +3187,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);
@@ -3242,6 +3242,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);
}
}
@@ -3293,14 +3294,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 {
@@ -3315,7 +3320,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();
}
}
/**
@@ -937,28 +929,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();
}
}