diff options
10 files changed, 398 insertions, 44 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index df41b1ff6066..9f323e2d4d6d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9681,14 +9681,24 @@ package android.companion.virtual { public final class VirtualDevice implements android.os.Parcelable { method public int describeContents(); method public int getDeviceId(); + method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @NonNull public int[] getDisplayIds(); method @Nullable public String getName(); method @Nullable public String getPersistentDeviceId(); + method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public boolean hasCustomSensorSupport(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR; } public final class VirtualDeviceManager { + method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @Nullable public android.companion.virtual.VirtualDevice getVirtualDevice(int); method @NonNull public java.util.List<android.companion.virtual.VirtualDevice> getVirtualDevices(); + method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void registerVirtualDeviceListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener); + method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void unregisterVirtualDeviceListener(@NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener); + } + + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public static interface VirtualDeviceManager.VirtualDeviceListener { + method public default void onVirtualDeviceClosed(int); + method public default void onVirtualDeviceCreated(int); } } diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index be699f4b9fa3..c58561d0bb2e 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -64,6 +64,16 @@ interface IVirtualDevice { String getPersistentDeviceId(); /** + * Returns the IDs of all virtual displays of this device. + */ + int[] getDisplayIds(); + + /** + * Returns the device policy for the given policy type. + */ + int getDevicePolicy(int policyType); + + /** * Closes the virtual device and frees all associated resources. */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") diff --git a/core/java/android/companion/virtual/IVirtualDeviceListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceListener.aidl new file mode 100644 index 000000000000..c6dd227fe782 --- /dev/null +++ b/core/java/android/companion/virtual/IVirtualDeviceListener.aidl @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * Interface to listen for changes in the available virtual devices. + * + * @hide + */ +oneway interface IVirtualDeviceListener { + + /** + * Called whenever a new virtual device has been added to the system. + */ + void onVirtualDeviceCreated(int deviceId); + + /** + * Called whenever a virtual device has been removed from the system. + */ + void onVirtualDeviceClosed(int deviceId); +} diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index ed8484fe7266..b665036c202f 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.IVirtualDeviceListener; import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceParams; @@ -56,12 +57,27 @@ interface IVirtualDeviceManager { */ List<VirtualDevice> getVirtualDevices(); - /** + /** + * Returns the details of the virtual device with the given ID, if any. + */ + VirtualDevice getVirtualDevice(int deviceId); + + /** + * Registers a virtual device listener to receive notifications for virtual device events. + */ + void registerVirtualDeviceListener(in IVirtualDeviceListener listener); + + /** + * Unregisters a previously registered virtual device listener. + */ + void unregisterVirtualDeviceListener(in IVirtualDeviceListener listener); + + /** * Returns the ID of the device which owns the display with the given ID. */ int getDeviceIdForDisplayId(int displayId); - /** + /** * Checks whether the passed {@code deviceId} is a valid virtual device ID or not. * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default * device which is not a virtual device. {@code deviceId} must correspond to a virtual device diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java index ceaf7e4f8317..4692f921beb2 100644 --- a/core/java/android/companion/virtual/VirtualDevice.java +++ b/core/java/android/companion/virtual/VirtualDevice.java @@ -16,13 +16,17 @@ package android.companion.virtual; +import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; +import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.companion.virtual.flags.Flags; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; - -import java.util.Objects; +import android.os.RemoteException; /** * Details of a particular virtual device. @@ -31,9 +35,12 @@ import java.util.Objects; * * <p class="note">Not to be confused with {@link VirtualDeviceManager.VirtualDevice}, which is used * by the virtual device creator and allows them to manage the device. + * + * @see VirtualDeviceManager#registerVirtualDeviceListener */ public final class VirtualDevice implements Parcelable { + private final @NonNull IVirtualDevice mVirtualDevice; private final int mId; private final @Nullable String mPersistentId; private final @Nullable String mName; @@ -44,17 +51,20 @@ public final class VirtualDevice implements Parcelable { * * @hide */ - public VirtualDevice(int id, @Nullable String persistentId, @Nullable String name) { + public VirtualDevice(@NonNull IVirtualDevice virtualDevice, int id, + @Nullable String persistentId, @Nullable String name) { if (id <= Context.DEVICE_ID_DEFAULT) { throw new IllegalArgumentException("VirtualDevice ID must be greater than " + Context.DEVICE_ID_DEFAULT); } + mVirtualDevice = virtualDevice; mId = id; mPersistentId = persistentId; mName = name; } private VirtualDevice(@NonNull Parcel parcel) { + mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder()); mId = parcel.readInt(); mPersistentId = parcel.readString8(); mName = parcel.readString8(); @@ -101,6 +111,40 @@ public final class VirtualDevice implements Parcelable { return mName; } + /** + * Returns the IDs of all virtual displays that belong to this device, if any. + * + * <p>The actual {@link android.view.Display} objects can be obtained by passing the returned + * IDs to {@link android.hardware.display.DisplayManager#getDisplay(int)}.</p> + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + public @NonNull int[] getDisplayIds() { + try { + return mVirtualDevice.getDisplayIds(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns whether this device may have custom sensors. + * + * <p>Returning {@code true} does not necessarily mean that this device has sensors, it only + * means that a {@link android.hardware.SensorManager} instance created from a {@link Context} + * associated with this device will return this device's sensors, if any.</p> + * + * @see Context#getDeviceId() + * @see Context#createDeviceContext(int) + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + public boolean hasCustomSensorSupport() { + try { + return mVirtualDevice.getDevicePolicy(POLICY_TYPE_SENSORS) == DEVICE_POLICY_CUSTOM; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override public int describeContents() { return 0; @@ -108,31 +152,13 @@ public final class VirtualDevice implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mVirtualDevice.asBinder()); dest.writeInt(mId); dest.writeString8(mPersistentId); 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(mPersistentId, that.mPersistentId) - && Objects.equals(mName, that.mName); - } - - @Override - public int hashCode() { - return Objects.hash(mId, mPersistentId, mName); - } - - @Override @NonNull public String toString() { return "VirtualDevice(" diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 923e68951509..29b0ff30f96f 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -55,11 +55,13 @@ import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualTouchscreen; import android.hardware.input.VirtualTouchscreenConfig; import android.media.AudioManager; +import android.os.Binder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.Surface; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.AnnotationValidations; import java.lang.annotation.ElementType; @@ -67,6 +69,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -147,6 +150,9 @@ public final class VirtualDeviceManager { private final IVirtualDeviceManager mService; private final Context mContext; + @GuardedBy("mVirtualDeviceListeners") + private final List<VirtualDeviceListenerDelegate> mVirtualDeviceListeners = new ArrayList<>(); + /** @hide */ public VirtualDeviceManager( @Nullable IVirtualDeviceManager service, @NonNull Context context) { @@ -207,6 +213,88 @@ public final class VirtualDeviceManager { } /** + * Returns the details of the virtual device with the given ID, if any. + * + * <p>The returned object is a read-only representation of the virtual device that expose its + * properties.</p> + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + @Nullable + public android.companion.virtual.VirtualDevice getVirtualDevice(int deviceId) { + if (mService == null) { + Log.w(TAG, "Failed to retrieve virtual devices; no virtual device manager service."); + return null; + } + if (deviceId == Context.DEVICE_ID_INVALID || deviceId == Context.DEVICE_ID_DEFAULT) { + return null; // Don't even bother making a Binder call. + } + try { + return mService.getVirtualDevice(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers a virtual device listener to receive notifications when virtual devices are created + * or closed. + * + * @param executor The executor where the listener is executed on. + * @param listener The listener to add. + * @see #unregisterVirtualDeviceListener + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + public void registerVirtualDeviceListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull VirtualDeviceListener listener) { + if (mService == null) { + Log.w(TAG, "Failed to register listener; no virtual device manager service."); + return; + } + final VirtualDeviceListenerDelegate delegate = + new VirtualDeviceListenerDelegate(Objects.requireNonNull(executor), + Objects.requireNonNull(listener)); + synchronized (mVirtualDeviceListeners) { + try { + mService.registerVirtualDeviceListener(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mVirtualDeviceListeners.add(delegate); + } + } + + /** + * Unregisters a virtual device listener previously registered with + * {@link #registerVirtualDeviceListener}. + * + * @param listener The listener to unregister. + * @see #registerVirtualDeviceListener + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + public void unregisterVirtualDeviceListener(@NonNull VirtualDeviceListener listener) { + if (mService == null) { + Log.w(TAG, "Failed to unregister listener; no virtual device manager service."); + return; + } + Objects.requireNonNull(listener); + synchronized (mVirtualDeviceListeners) { + final Iterator<VirtualDeviceListenerDelegate> it = mVirtualDeviceListeners.iterator(); + while (it.hasNext()) { + final VirtualDeviceListenerDelegate delegate = it.next(); + if (delegate.mListener == listener) { + try { + mService.unregisterVirtualDeviceListener(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + it.remove(); + } + } + } + } + + /** * Returns the device policy for the given virtual device and policy type. * * <p>In case the virtual device identifier is not valid, or there's no explicitly specified @@ -748,7 +836,7 @@ public final class VirtualDeviceManager { * * @param executor The executor where the listener is executed on. * @param soundEffectListener The listener to add. - * @see #removeActivityListener(ActivityListener) + * @see #removeSoundEffectListener(SoundEffectListener) */ public void addSoundEffectListener(@CallbackExecutor @NonNull Executor executor, @NonNull SoundEffectListener soundEffectListener) { @@ -877,4 +965,59 @@ public final class VirtualDeviceManager { */ void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType); } + + /** + * Listener for changes in the available virtual devices. + */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) + public interface VirtualDeviceListener { + /** + * Called whenever a new virtual device has been added to the system. + * Use {@link VirtualDeviceManager#getVirtualDevice(int)} to get more information about + * the device. + * + * @param deviceId The id of the virtual device that was added. + */ + default void onVirtualDeviceCreated(int deviceId) {} + + /** + * Called whenever a virtual device has been removed from the system. + * + * @param deviceId The id of the virtual device that was removed. + */ + default void onVirtualDeviceClosed(int deviceId) {} + } + + /** + * A wrapper for {@link VirtualDeviceListener} that executes callbacks on the given executor. + */ + private static class VirtualDeviceListenerDelegate extends IVirtualDeviceListener.Stub { + private final VirtualDeviceListener mListener; + private final Executor mExecutor; + + private VirtualDeviceListenerDelegate(Executor executor, VirtualDeviceListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onVirtualDeviceCreated(int deviceId) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mListener.onVirtualDeviceCreated(deviceId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onVirtualDeviceClosed(int deviceId) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mListener.onVirtualDeviceClosed(deviceId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } } diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index 057b8565a8bb..9ab3be6863d0 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -13,3 +13,10 @@ flag { description: "Enable dynamic policy API" bug: "298401780" } + +flag { + name: "vdm_public_apis" + namespace: "virtual_devices" + description: "Enable public VDM API for device capabilities" + bug: "297253526" +} 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 56afeb112068..5eefd12941ec 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -39,6 +39,7 @@ import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceIntentInterceptor; import android.companion.virtual.IVirtualDeviceSoundEffectListener; +import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; @@ -168,6 +169,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Nullable private LocaleList mLocaleList = null; + @NonNull + private final VirtualDevice mPublicVirtualDeviceObject; + private ActivityListener createListenerAdapter() { return new ActivityListener() { @@ -286,6 +290,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub throw e.rethrowFromSystemServer(); } mVirtualDeviceLog.logCreated(deviceId, mOwnerUid); + + mPublicVirtualDeviceObject = new VirtualDevice( + this, getDeviceId(), getPersistentDeviceId(), mParams.getName()); } @VisibleForTesting @@ -315,9 +322,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mAssociationInfo.getDisplayName(); } - /** Returns the optional name of the device. */ - String getDeviceName() { - return mParams.getName(); + /** Returns the public representation of the device. */ + VirtualDevice getPublicVirtualDeviceObject() { + return mPublicVirtualDeviceObject; } /** Returns the locale of the device. */ @@ -327,7 +334,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } - /** Returns the policy specified for this policy type */ + @Override // Binder call public @VirtualDeviceParams.DevicePolicy int getDevicePolicy( @VirtualDeviceParams.PolicyType int policyType) { if (Flags.dynamicPolicy()) { @@ -754,8 +761,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub synchronized (mVirtualDeviceLock) { mDefaultShowPointerIcon = showPointerIcon; } - getDisplayIds().forEach( - displayId -> mInputController.setShowPointerIcon(showPointerIcon, displayId)); + final int[] displayIds = getDisplayIds(); + for (int i = 0; i < displayIds.length; ++i) { + mInputController.setShowPointerIcon(showPointerIcon, displayIds[i]); + } } finally { Binder.restoreCallingIdentity(ident); } @@ -1018,14 +1027,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mOwnerUid; } - ArraySet<Integer> getDisplayIds() { + @Override // Binder call + public int[] getDisplayIds() { synchronized (mVirtualDeviceLock) { final int size = mVirtualDisplays.size(); - ArraySet<Integer> arraySet = new ArraySet<>(size); + int[] displayIds = new int[size]; for (int i = 0; i < size; i++) { - arraySet.append(mVirtualDisplays.keyAt(i)); + displayIds[i] = mVirtualDisplays.keyAt(i); } - return arraySet; + return displayIds; } } 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 4da929816d2f..cfe56e910b8a 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -30,6 +30,7 @@ import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; +import android.companion.virtual.IVirtualDeviceListener; import android.companion.virtual.IVirtualDeviceManager; import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; @@ -49,6 +50,7 @@ import android.os.LocaleList; import android.os.Looper; import android.os.Parcel; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; @@ -70,6 +72,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -77,6 +80,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; @SuppressLint("LongLogTag") @@ -103,6 +107,9 @@ public class VirtualDeviceManagerService extends SystemService { } }; + private final RemoteCallbackList<IVirtualDeviceListener> mVirtualDeviceListeners = + new RemoteCallbackList<>(); + /** * Mapping from device IDs to virtual devices. */ @@ -225,6 +232,17 @@ public class VirtualDeviceManagerService extends SystemService { mVirtualDevices.remove(deviceId); } + if (Flags.vdmPublicApis()) { + mVirtualDeviceListeners.broadcast(listener -> { + try { + listener.onVirtualDeviceClosed(deviceId); + } catch (RemoteException e) { + Slog.i(TAG, "Failed to invoke onVirtualDeviceClosed listener: " + + e.getMessage()); + } + }); + } + Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED); i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId); i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -376,6 +394,17 @@ public class VirtualDeviceManagerService extends SystemService { } mVirtualDevices.put(deviceId, virtualDevice); } + + if (Flags.vdmPublicApis()) { + mVirtualDeviceListeners.broadcast(listener -> { + try { + listener.onVirtualDeviceCreated(deviceId); + } catch (RemoteException e) { + Slog.i(TAG, "Failed to invoke onVirtualDeviceCreated listener: " + + e.getMessage()); + } + }); + } return virtualDevice; } @@ -415,14 +444,31 @@ public class VirtualDeviceManagerService extends SystemService { synchronized (mVirtualDeviceManagerLock) { for (int i = 0; i < mVirtualDevices.size(); i++) { final VirtualDeviceImpl device = mVirtualDevices.valueAt(i); - virtualDevices.add( - new VirtualDevice(device.getDeviceId(), device.getPersistentDeviceId(), - device.getDeviceName())); + virtualDevices.add(device.getPublicVirtualDeviceObject()); } } return virtualDevices; } + @Override // Binder call + public VirtualDevice getVirtualDevice(int deviceId) { + VirtualDeviceImpl device; + synchronized (mVirtualDeviceManagerLock) { + device = mVirtualDevices.get(deviceId); + } + return device == null ? null : device.getPublicVirtualDeviceObject(); + } + + @Override // Binder call + public void registerVirtualDeviceListener(IVirtualDeviceListener listener) { + mVirtualDeviceListeners.register(listener); + } + + @Override // Binder call + public void unregisterVirtualDeviceListener(IVirtualDeviceListener listener) { + mVirtualDeviceListeners.unregister(listener); + } + @Override // BinderCall @VirtualDeviceParams.DevicePolicy public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) { @@ -705,7 +751,9 @@ public class VirtualDeviceManagerService extends SystemService { synchronized (mVirtualDeviceManagerLock) { virtualDevice = mVirtualDevices.get(deviceId); } - return virtualDevice == null ? new ArraySet<>() : virtualDevice.getDisplayIds(); + return virtualDevice == null ? new ArraySet<>() + : Arrays.stream(virtualDevice.getDisplayIds()).boxed() + .collect(Collectors.toCollection(ArraySet::new)); } @Override 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 28df24c3866a..c65452aa2fa1 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 @@ -16,21 +16,31 @@ package com.android.server.companion.virtual; +import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; +import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; +import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.content.Context.DEVICE_ID_INVALID; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.when; +import android.companion.virtual.IVirtualDevice; import android.companion.virtual.VirtualDevice; +import android.companion.virtual.flags.Flags; import android.os.Parcel; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @Presubmit @RunWith(AndroidJUnit4.class) @@ -40,24 +50,35 @@ public class VirtualDeviceTest { private static final String PERSISTENT_ID = "persistentId"; private static final String DEVICE_NAME = "VirtualDeviceName"; + @Mock + private IVirtualDevice mVirtualDevice; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + @Test public void build_invalidId_shouldThrowIllegalArgumentException() { assertThrows( IllegalArgumentException.class, - () -> new VirtualDevice(DEVICE_ID_INVALID, PERSISTENT_ID, DEVICE_NAME)); + () -> new VirtualDevice( + mVirtualDevice, DEVICE_ID_INVALID, PERSISTENT_ID, DEVICE_NAME)); } @Test public void build_defaultId_shouldThrowIllegalArgumentException() { assertThrows( IllegalArgumentException.class, - () -> new VirtualDevice(DEVICE_ID_DEFAULT, PERSISTENT_ID, DEVICE_NAME)); + () -> new VirtualDevice( + mVirtualDevice, DEVICE_ID_DEFAULT, PERSISTENT_ID, DEVICE_NAME)); } @Test public void build_onlyRequiredFields() { VirtualDevice virtualDevice = - new VirtualDevice(VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null); + new VirtualDevice( + mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null); assertThat(virtualDevice.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID); assertThat(virtualDevice.getPersistentDeviceId()).isNull(); assertThat(virtualDevice.getName()).isNull(); @@ -66,15 +87,43 @@ public class VirtualDeviceTest { @Test public void parcelable_shouldRecreateSuccessfully() { VirtualDevice originalDevice = - new VirtualDevice(VIRTUAL_DEVICE_ID, PERSISTENT_ID, DEVICE_NAME); + new VirtualDevice(mVirtualDevice, VIRTUAL_DEVICE_ID, PERSISTENT_ID, 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.getPersistentDeviceId()).isEqualTo(PERSISTENT_ID); assertThat(device.getName()).isEqualTo(DEVICE_NAME); } + + @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS) + @Test + public void virtualDevice_getDisplayIds() throws Exception { + VirtualDevice virtualDevice = + new VirtualDevice( + mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null); + + when(mVirtualDevice.getDisplayIds()).thenReturn(new int[0]); + assertThat(virtualDevice.getDisplayIds()).hasLength(0); + + final int[] displayIds = new int[]{7, 18}; + when(mVirtualDevice.getDisplayIds()).thenReturn(displayIds); + assertThat(virtualDevice.getDisplayIds()).isEqualTo(displayIds); + } + + @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS) + @Test + public void virtualDevice_hasCustomSensorSupport() throws Exception { + VirtualDevice virtualDevice = + new VirtualDevice( + mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null); + + when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_SENSORS)).thenReturn(DEVICE_POLICY_DEFAULT); + assertThat(virtualDevice.hasCustomSensorSupport()).isFalse(); + + when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_SENSORS)).thenReturn(DEVICE_POLICY_CUSTOM); + assertThat(virtualDevice.hasCustomSensorSupport()).isTrue(); + } } |