diff options
10 files changed, 311 insertions, 4 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7a412d8e2a81..0c98ec3466e2 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9069,6 +9069,18 @@ package android.companion { } +package android.companion.virtual { + + public final class VirtualDevice implements android.os.Parcelable { + method public int describeContents(); + method public int getDeviceId(); + method @Nullable public String getName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR; + } + +} + package android.content { public abstract class AbstractThreadedSyncAdapter { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index dd85daaf65c2..aa138d7e7bdb 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2789,6 +2789,7 @@ package android.companion.virtual { public final class VirtualDeviceManager { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams); + method @NonNull public java.util.List<android.companion.virtual.VirtualDevice> getVirtualDevices(); field public static final int DEFAULT_DEVICE_ID = 0; // 0x0 field public static final int INVALID_DEVICE_ID = -1; // 0xffffffff field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2 @@ -2825,6 +2826,7 @@ package android.companion.virtual { method public int getDefaultActivityPolicy(); method public int getDefaultNavigationPolicy(); method public int getLockState(); + method @Nullable public String getName(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0 @@ -2844,6 +2846,7 @@ package android.companion.virtual { method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>); 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>); } diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index 5418f7e93dd9..82d7534c84d9 100644 --- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl +++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl @@ -18,6 +18,7 @@ package android.companion.virtual; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; +import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceParams; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; @@ -46,6 +47,11 @@ interface IVirtualDeviceManager { in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener); /** + * Returns the details of all available virtual devices. + */ + List<VirtualDevice> getVirtualDevices(); + + /** * Creates a virtual display owned by a particular virtual device. * * @param virtualDisplayConfig The configuration used in creating the display diff --git a/core/java/android/companion/virtual/VirtualDevice.aidl b/core/java/android/companion/virtual/VirtualDevice.aidl new file mode 100644 index 000000000000..4bbe05f585e8 --- /dev/null +++ b/core/java/android/companion/virtual/VirtualDevice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 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; + +parcelable VirtualDevice; diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java new file mode 100644 index 000000000000..9e95d472f48b --- /dev/null +++ b/core/java/android/companion/virtual/VirtualDevice.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Details of a particular virtual device. + */ +public final class VirtualDevice implements Parcelable { + + private final int mId; + private final @Nullable String mName; + + /** + * Creates a new instance of {@link VirtualDevice}. + * Only to be used by the VirtualDeviceManagerService. + * + * @hide + */ + public VirtualDevice(int id, @Nullable String name) { + if (id <= VirtualDeviceManager.DEFAULT_DEVICE_ID) { + throw new IllegalArgumentException("VirtualDevice ID mist be greater than " + + VirtualDeviceManager.DEFAULT_DEVICE_ID); + } + mId = id; + mName = name; + } + + private VirtualDevice(@NonNull Parcel parcel) { + mId = parcel.readInt(); + mName = parcel.readString8(); + } + + /** + * Returns the unique ID of the virtual device. + */ + public int getDeviceId() { + return mId; + } + + /** + * Returns the name of the virtual device (optionally) provided during its creation. + * + * @see VirtualDeviceParams.Builder#setName(String) + */ + public @Nullable String getName() { + return mName; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeString8(mName); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VirtualDevice)) { + return false; + } + VirtualDevice that = (VirtualDevice) o; + return mId == that.mId + && Objects.equals(mName, that.mName); + } + + @Override + public int hashCode() { + return Objects.hash(mId, mName); + } + + @Override + @NonNull + public String toString() { + return "VirtualDevice(" + + " mId=" + mId + + " mName=" + mName + + ")"; + } + + @NonNull + public static final Parcelable.Creator<VirtualDevice> CREATOR = + new Parcelable.Creator<VirtualDevice>() { + public VirtualDevice createFromParcel(Parcel in) { + return new VirtualDevice(in); + } + + public VirtualDevice[] newArray(int size) { + return new VirtualDevice[size]; + } + }; +} diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 08bee2552a1f..7ef92e3edb0d 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -49,12 +49,15 @@ import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.ArrayMap; +import android.util.Log; import android.view.Surface; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executor; import java.util.function.IntConsumer; @@ -154,6 +157,22 @@ public final class VirtualDeviceManager { } /** + * Returns the details of all available virtual devices. + */ + @NonNull + public List<android.companion.virtual.VirtualDevice> getVirtualDevices() { + if (mService == null) { + Log.w(TAG, "Failed to retrieve virtual devices; no virtual device manager service."); + return new ArrayList<>(); + } + try { + return mService.getVirtualDevices(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * A virtual device has its own virtual display, audio output, microphone, and camera etc. The * creator of a virtual device can take the output from the virtual display and stream it over * to another device, and inject input events that are received from the remote device. diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 3b1ff3fa65b5..d40c9d63039d 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.ComponentName; @@ -112,6 +113,7 @@ public final class VirtualDeviceParams implements Parcelable { @NonNull private final ArraySet<ComponentName> mBlockedActivities; @ActivityPolicy private final int mDefaultActivityPolicy; + @Nullable private final String mName; private VirtualDeviceParams( @LockState int lockState, @@ -121,7 +123,8 @@ public final class VirtualDeviceParams implements Parcelable { @NavigationPolicy int defaultNavigationPolicy, @NonNull Set<ComponentName> allowedActivities, @NonNull Set<ComponentName> blockedActivities, - @ActivityPolicy int defaultActivityPolicy) { + @ActivityPolicy int defaultActivityPolicy, + @Nullable String name) { Preconditions.checkNotNull(usersWithMatchingAccounts); Preconditions.checkNotNull(allowedCrossTaskNavigations); Preconditions.checkNotNull(blockedCrossTaskNavigations); @@ -136,6 +139,7 @@ public final class VirtualDeviceParams implements Parcelable { mAllowedActivities = new ArraySet<>(allowedActivities); mBlockedActivities = new ArraySet<>(blockedActivities); mDefaultActivityPolicy = defaultActivityPolicy; + mName = name; } @SuppressWarnings("unchecked") @@ -148,6 +152,7 @@ public final class VirtualDeviceParams implements Parcelable { mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); mDefaultActivityPolicy = parcel.readInt(); + mName = parcel.readString8(); } /** @@ -243,6 +248,16 @@ public final class VirtualDeviceParams implements Parcelable { return mDefaultActivityPolicy; } + /** + * Returns the (optional) name of the virtual device. + * + * @see Builder#setName + */ + @Nullable + public String getName() { + return mName; + } + @Override public int describeContents() { return 0; @@ -258,6 +273,7 @@ public final class VirtualDeviceParams implements Parcelable { dest.writeArraySet(mAllowedActivities); dest.writeArraySet(mBlockedActivities); dest.writeInt(mDefaultActivityPolicy); + dest.writeString8(mName); } @Override @@ -276,7 +292,8 @@ public final class VirtualDeviceParams implements Parcelable { && mDefaultNavigationPolicy == that.mDefaultNavigationPolicy && Objects.equals(mAllowedActivities, that.mAllowedActivities) && Objects.equals(mBlockedActivities, that.mBlockedActivities) - && mDefaultActivityPolicy == that.mDefaultActivityPolicy; + && mDefaultActivityPolicy == that.mDefaultActivityPolicy + && Objects.equals(mName, that.mName); } @Override @@ -284,7 +301,7 @@ public final class VirtualDeviceParams implements Parcelable { return Objects.hash( mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations, mBlockedCrossTaskNavigations, mDefaultNavigationPolicy, mAllowedActivities, - mBlockedActivities, mDefaultActivityPolicy); + mBlockedActivities, mDefaultActivityPolicy, mName); } @Override @@ -299,6 +316,7 @@ public final class VirtualDeviceParams implements Parcelable { + " mAllowedActivities=" + mAllowedActivities + " mBlockedActivities=" + mBlockedActivities + " mDefaultActivityPolicy=" + mDefaultActivityPolicy + + " mName=" + mName + ")"; } @@ -331,6 +349,7 @@ public final class VirtualDeviceParams implements Parcelable { @ActivityPolicy private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; private boolean mDefaultActivityPolicyConfigured = false; + @Nullable private String mName; /** * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY} @@ -494,6 +513,21 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Sets the optional virtual device name. + * + * <p>This string is not typically intended to be displayed to end users, but rather for + * debugging and other developer-facing purposes. + * + * <p>3rd party applications may be able to see the name (i.e. it's not private to the + * device owner) + */ + @NonNull + public Builder setName(@NonNull String name) { + mName = name; + return this; + } + + /** * Builds the {@link VirtualDeviceParams} instance. */ @NonNull @@ -506,7 +540,8 @@ public final class VirtualDeviceParams implements Parcelable { mDefaultNavigationPolicy, mAllowedActivities, mBlockedActivities, - mDefaultActivityPolicy); + mDefaultActivityPolicy, + mName); } } } 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 5f27f598ee4e..2835b69b3039 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -222,6 +222,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mAssociationInfo.getDisplayName(); } + /** Returns the optional name of the device. */ + String getDeviceName() { + return mParams.getName(); + } + /** Returns the unique device ID of this device. */ @Override // Binder call public int getDeviceId() { 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 06dfeabfe832..c400a74da4ce 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -28,6 +28,7 @@ import android.companion.CompanionDeviceManager.OnAssociationsChangedListener; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceManager; +import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.content.Context; @@ -344,6 +345,19 @@ public class VirtualDeviceManagerService extends SystemService { return displayId; } + @Override // Binder call + public List<VirtualDevice> getVirtualDevices() { + List<VirtualDevice> virtualDevices = new ArrayList<>(); + synchronized (mVirtualDeviceManagerLock) { + for (int i = 0; i < mVirtualDevices.size(); i++) { + final VirtualDeviceImpl device = mVirtualDevices.valueAt(i); + virtualDevices.add( + new VirtualDevice(device.getDeviceId(), device.getDeviceName())); + } + } + return virtualDevices; + } + @Nullable private AssociationInfo getAssociationInfo(String packageName, int associationId) { final int callingUserId = getCallingUserHandle().getIdentifier(); 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 new file mode 100644 index 000000000000..f6f13392ce3a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 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.VirtualDeviceManager.DEFAULT_DEVICE_ID; +import static android.companion.virtual.VirtualDeviceManager.INVALID_DEVICE_ID; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import android.companion.virtual.VirtualDevice; +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class VirtualDeviceTest { + + private static final int VIRTUAL_DEVICE_ID = 42; + private static final String VIRTUAL_DEVICE_NAME = "VirtualDeviceName"; + + @Test + public void build_invalidId_shouldThrowIllegalArgumentException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualDevice(INVALID_DEVICE_ID, VIRTUAL_DEVICE_NAME)); + } + + @Test + public void build_defaultId_shouldThrowIllegalArgumentException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualDevice(DEFAULT_DEVICE_ID, VIRTUAL_DEVICE_NAME)); + } + + @Test + public void build_nameIsOptional() { + VirtualDevice virtualDevice = + new VirtualDevice(VIRTUAL_DEVICE_ID, /* name= */ null); + assertThat(virtualDevice.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID); + assertThat(virtualDevice.getName()).isNull(); + } + + @Test + public void parcelable_shouldRecreateSuccessfully() { + VirtualDevice originalDevice = + new VirtualDevice(VIRTUAL_DEVICE_ID, VIRTUAL_DEVICE_NAME); + Parcel parcel = Parcel.obtain(); + originalDevice.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + VirtualDevice device = VirtualDevice.CREATOR.createFromParcel(parcel); + assertThat(device).isEqualTo(originalDevice); + assertThat(device.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID); + assertThat(device.getName()).isEqualTo(VIRTUAL_DEVICE_NAME); + } +} |