Public API for getting a persistent VD ID.

The VD deviceId is ephemeral and cannot be used for storing device
preferences by apps. Exposing the CDM association ID for the
corresponding physical device solves this (we're not exposing the
semantics of the ID, just calling it "persistent").

Bug: 286363466
Test: atest VirtualDeviceTest
Test: atest VirtualDeviceManagerBasicTest
Change-Id: Iacfcc7d725b9c6c0f364e113efe18ce0a63409a3
This commit is contained in:
Vladimir Komsiyski
2023-06-08 14:27:58 +02:00
parent f5741e2a6e
commit ae903e05b8
9 changed files with 78 additions and 11 deletions

View File

@@ -9609,6 +9609,7 @@ package android.companion.virtual {
method public int describeContents();
method public int getDeviceId();
method @Nullable public String getName();
method @Nullable public String getPersistentDeviceId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR;
}

View File

@@ -3200,6 +3200,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 String getPersistentDeviceId();
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);

View File

@@ -58,6 +58,11 @@ interface IVirtualDevice {
*/
int getDeviceId();
/**
* Returns the persistent ID of this virtual device.
*/
String getPersistentDeviceId();
/**
* Closes the virtual device and frees all associated resources.
*/

View File

@@ -35,6 +35,7 @@ import java.util.Objects;
public final class VirtualDevice implements Parcelable {
private final int mId;
private final @Nullable String mPersistentId;
private final @Nullable String mName;
/**
@@ -43,27 +44,54 @@ public final class VirtualDevice implements Parcelable {
*
* @hide
*/
public VirtualDevice(int id, @Nullable String name) {
public VirtualDevice(int id, @Nullable String persistentId, @Nullable String name) {
if (id <= Context.DEVICE_ID_DEFAULT) {
throw new IllegalArgumentException("VirtualDevice ID mist be greater than "
throw new IllegalArgumentException("VirtualDevice ID must be greater than "
+ Context.DEVICE_ID_DEFAULT);
}
mId = id;
mPersistentId = persistentId;
mName = name;
}
private VirtualDevice(@NonNull Parcel parcel) {
mId = parcel.readInt();
mPersistentId = parcel.readString8();
mName = parcel.readString8();
}
/**
* Returns the unique ID of the virtual device.
*
* <p>This identifier corresponds to {@link Context#getDeviceId()} and can be used to access
* device-specific system capabilities.
*
* <p class="note">This identifier is ephemeral and should not be used for persisting any data
* per device.
*
* @see Context#createDeviceContext
* @see #getPersistentDeviceId
*/
public int getDeviceId() {
return mId;
}
/**
* Returns the persistent identifier of this virtual device, if any.
*
* <p> If there is no stable identifier for this virtual device, then this returns {@code null}.
* <p>This identifier may correspond to a physical device. In that case it remains valid for as
* long as that physical device is associated with the host device and may be used to persist
* data per device.
*
* <p class="note">This identifier may not be unique across virtual devices, in case there are
* more than one virtual devices corresponding to the same physical device.
*/
public @Nullable String getPersistentDeviceId() {
return mPersistentId;
}
/**
* Returns the name of the virtual device (optionally) provided during its creation.
*
@@ -81,6 +109,7 @@ public final class VirtualDevice implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString8(mPersistentId);
dest.writeString8(mName);
}
@@ -94,12 +123,13 @@ public final class VirtualDevice implements Parcelable {
}
VirtualDevice that = (VirtualDevice) o;
return mId == that.mId
&& Objects.equals(mPersistentId, that.mPersistentId)
&& Objects.equals(mName, that.mName);
}
@Override
public int hashCode() {
return Objects.hash(mId, mName);
return Objects.hash(mId, mPersistentId, mName);
}
@Override
@@ -107,6 +137,7 @@ public final class VirtualDevice implements Parcelable {
public String toString() {
return "VirtualDevice("
+ " mId=" + mId
+ " mPersistentId=" + mPersistentId
+ " mName=" + mName
+ ")";
}

View File

@@ -160,6 +160,14 @@ public class VirtualDeviceInternal {
}
}
@Nullable String getPersistentDeviceId() {
try {
return mVirtualDevice.getPersistentDeviceId();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@NonNull Context createContext() {
try {
return mContext.createDeviceContext(mVirtualDevice.getDeviceId());

View File

@@ -366,6 +366,13 @@ public final class VirtualDeviceManager {
return mVirtualDeviceInternal.getDeviceId();
}
/**
* Returns the persistent ID of this virtual device.
*/
public @Nullable String getPersistentDeviceId() {
return mVirtualDeviceInternal.getPersistentDeviceId();
}
/**
* Returns a new context bound to this device.
*

View File

@@ -113,6 +113,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
| DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:";
/**
* Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched.
*/
@@ -126,6 +128,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
private int mDeviceId;
private @Nullable String mPersistentDeviceId;
// Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
// Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
// 1. After display is created the window manager calls into VDM during construction
@@ -240,6 +243,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
mPersistentDeviceId = PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationInfo.getId();
mService = service;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -329,6 +333,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return mDeviceId;
}
/** Returns the unique device ID of this device. */
@Override // Binder call
public @Nullable String getPersistentDeviceId() {
return mPersistentDeviceId;
}
@Override // Binder call
public int getAssociationId() {
return mAssociationInfo.getId();

View File

@@ -396,7 +396,8 @@ public class VirtualDeviceManagerService extends SystemService {
for (int i = 0; i < mVirtualDevices.size(); i++) {
final VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
virtualDevices.add(
new VirtualDevice(device.getDeviceId(), device.getDeviceName()));
new VirtualDevice(device.getDeviceId(), device.getPersistentDeviceId(),
device.getDeviceName()));
}
}
return virtualDevices;

View File

@@ -37,34 +37,36 @@ import org.junit.runner.RunWith;
public class VirtualDeviceTest {
private static final int VIRTUAL_DEVICE_ID = 42;
private static final String VIRTUAL_DEVICE_NAME = "VirtualDeviceName";
private static final String PERSISTENT_ID = "persistentId";
private static final String DEVICE_NAME = "VirtualDeviceName";
@Test
public void build_invalidId_shouldThrowIllegalArgumentException() {
assertThrows(
IllegalArgumentException.class,
() -> new VirtualDevice(DEVICE_ID_INVALID, VIRTUAL_DEVICE_NAME));
() -> new VirtualDevice(DEVICE_ID_INVALID, PERSISTENT_ID, DEVICE_NAME));
}
@Test
public void build_defaultId_shouldThrowIllegalArgumentException() {
assertThrows(
IllegalArgumentException.class,
() -> new VirtualDevice(DEVICE_ID_DEFAULT, VIRTUAL_DEVICE_NAME));
() -> new VirtualDevice(DEVICE_ID_DEFAULT, PERSISTENT_ID, DEVICE_NAME));
}
@Test
public void build_nameIsOptional() {
public void build_onlyRequiredFields() {
VirtualDevice virtualDevice =
new VirtualDevice(VIRTUAL_DEVICE_ID, /* name= */ null);
new VirtualDevice(VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
assertThat(virtualDevice.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID);
assertThat(virtualDevice.getPersistentDeviceId()).isNull();
assertThat(virtualDevice.getName()).isNull();
}
@Test
public void parcelable_shouldRecreateSuccessfully() {
VirtualDevice originalDevice =
new VirtualDevice(VIRTUAL_DEVICE_ID, VIRTUAL_DEVICE_NAME);
new VirtualDevice(VIRTUAL_DEVICE_ID, PERSISTENT_ID, DEVICE_NAME);
Parcel parcel = Parcel.obtain();
originalDevice.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -72,6 +74,7 @@ public class VirtualDeviceTest {
VirtualDevice device = VirtualDevice.CREATOR.createFromParcel(parcel);
assertThat(device).isEqualTo(originalDevice);
assertThat(device.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID);
assertThat(device.getName()).isEqualTo(VIRTUAL_DEVICE_NAME);
assertThat(device.getPersistentDeviceId()).isEqualTo(PERSISTENT_ID);
assertThat(device.getName()).isEqualTo(DEVICE_NAME);
}
}