From ae903e05b8b65890b1921df14e9f8e9543c1268d Mon Sep 17 00:00:00 2001 From: Vladimir Komsiyski Date: Thu, 8 Jun 2023 14:27:58 +0200 Subject: [PATCH] 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 --- core/api/current.txt | 1 + core/api/system-current.txt | 1 + .../companion/virtual/IVirtualDevice.aidl | 5 +++ .../companion/virtual/VirtualDevice.java | 37 +++++++++++++++++-- .../virtual/VirtualDeviceInternal.java | 8 ++++ .../virtual/VirtualDeviceManager.java | 7 ++++ .../companion/virtual/VirtualDeviceImpl.java | 10 +++++ .../virtual/VirtualDeviceManagerService.java | 3 +- .../companion/virtual/VirtualDeviceTest.java | 17 +++++---- 9 files changed, 78 insertions(+), 11 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index a2c1b1b61f7a8..6aac750872583 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -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 CREATOR; } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 7a86971ec5fef..928e41da9c3b7 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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 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); diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 9efdf2831b9e3..4801d155eb33c 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -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. */ diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java index 4ee65e0773825..ceaf7e4f8317e 100644 --- a/core/java/android/companion/virtual/VirtualDevice.java +++ b/core/java/android/companion/virtual/VirtualDevice.java @@ -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. + * + *

This identifier corresponds to {@link Context#getDeviceId()} and can be used to access + * device-specific system capabilities. + * + *

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

If there is no stable identifier for this virtual device, then this returns {@code null}. + + *

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

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 + ")"; } diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index 045e4c6c77b18..f68cfff1c053b 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -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()); diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index fba896d8b78ae..060a5c85a713b 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -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. * diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 6b948bd3d3c8a..93557418e10c1 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -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(); diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index bc24cf39c5496..a0c80a50a2e11 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -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; diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java index a2e204d894248..28df24c3866a7 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java @@ -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); } }