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