Add params object to createVirtualDevice
The VirtualDeviceParams are passed into createVirtualDevice to provide
additional configuration options to the method.
Bug: 194949534
Test: To be added in ag/15781910
Change-Id: Ief9c013a2f1c70a63e3dc3eb8fbc0af60ea65c44
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 2dfa202..d80bee6 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -17,6 +17,7 @@
package android.companion.virtual;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceParams;
/**
* Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
@@ -33,6 +34,10 @@
* that this belongs to the calling UID.
* @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
* CDM. Virtual devices must have a corresponding association with CDM in order to be created.
+ * @param params The parameters for creating this virtual device. See {@link
+ * VirtualDeviceManager.VirtualDeviceParams}.
*/
- IVirtualDevice createVirtualDevice(in IBinder token, String packageName, int associationId);
+ IVirtualDevice createVirtualDevice(
+ in IBinder token, String packageName, int associationId,
+ in VirtualDeviceParams params);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index bace45b..858e4daa1 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -100,11 +100,11 @@
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@Nullable
- public VirtualDevice createVirtualDevice(int associationId) {
+ public VirtualDevice createVirtualDevice(int associationId, VirtualDeviceParams params) {
// TODO(b/194949534): Unhide this API
try {
IVirtualDevice virtualDevice = mService.createVirtualDevice(
- new Binder(), mContext.getPackageName(), associationId);
+ new Binder(), mContext.getPackageName(), associationId, params);
return new VirtualDevice(mContext, virtualDevice);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -273,6 +273,12 @@
}
}
+ /**
+ * Returns the display flags that should be added to a particular virtual display.
+ * Additional device-level flags from {@link
+ * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
+ * be added by DisplayManagerService.
+ */
private int getVirtualDisplayFlags(@DisplayFlags int flags) {
int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.aidl b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
new file mode 100644
index 0000000..9b3974a
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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 VirtualDeviceParams;
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
new file mode 100644
index 0000000..ad2dea9
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 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 static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Objects;
+
+/**
+ * Params that can be configured when creating virtual devices.
+ *
+ * @hide
+ */
+// TODO(b/194949534): Unhide this API
+public final class VirtualDeviceParams implements Parcelable {
+
+ /** @hide */
+ @IntDef(prefix = "LOCK_STATE_",
+ value = {LOCK_STATE_ALWAYS_LOCKED, LOCK_STATE_ALWAYS_UNLOCKED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface LockState {}
+
+ /**
+ * Indicates that the lock state of the virtual device should be always locked.
+ *
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
+
+ /**
+ * Indicates that the lock state of the virtual device should be always unlocked.
+ *
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
+
+ private final int mLockState;
+
+ private VirtualDeviceParams(@LockState int lockState) {
+ mLockState = lockState;
+ }
+
+ private VirtualDeviceParams(Parcel parcel) {
+ mLockState = parcel.readInt();
+ }
+
+ @LockState
+ public int getLockState() {
+ return mLockState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mLockState);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof VirtualDeviceParams)) {
+ return false;
+ }
+ VirtualDeviceParams that = (VirtualDeviceParams) o;
+ return mLockState == that.mLockState;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLockState);
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualDeviceParams("
+ + " mLockState=" + mLockState
+ + ")";
+ }
+
+ public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
+ new Parcelable.Creator<VirtualDeviceParams>() {
+ public VirtualDeviceParams createFromParcel(Parcel in) {
+ return new VirtualDeviceParams(in);
+ }
+
+ public VirtualDeviceParams[] newArray(int size) {
+ return new VirtualDeviceParams[size];
+ }
+ };
+
+ /**
+ * Builder for {@link VirtualDeviceParams}.
+ */
+ public static final class Builder {
+
+ private @LockState int mLockState = LOCK_STATE_ALWAYS_LOCKED;
+
+ /**
+ * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
+ * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+ * The default is {@link #LOCK_STATE_ALWAYS_LOCKED}.
+ *
+ * @param lockState The lock state, either {@link #LOCK_STATE_ALWAYS_LOCKED} or
+ * {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+ */
+ @RequiresPermission(value = ADD_ALWAYS_UNLOCKED_DISPLAY, conditional = true)
+ @NonNull
+ public Builder setLockState(@LockState int lockState) {
+ mLockState = lockState;
+ return this;
+ }
+
+ /**
+ * Builds the {@link VirtualDeviceParams} instance.
+ */
+ @NonNull
+ public VirtualDeviceParams build() {
+ return new VirtualDeviceParams(mLockState);
+ }
+ }
+}
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 ca35e03..01e8d3b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -22,8 +22,10 @@
import android.annotation.NonNull;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
import android.graphics.Point;
+import android.hardware.display.DisplayManager;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -56,6 +58,7 @@
final List<Integer> mVirtualDisplayIds = new ArrayList<>();
private final OnDeviceCloseListener mListener;
private final IBinder mAppToken;
+ private final VirtualDeviceParams mParams;
/**
* A mapping from the virtual display ID to its corresponding
@@ -65,17 +68,21 @@
new SparseArray<>();
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
- IBinder token, int ownerUid, OnDeviceCloseListener listener) {
- this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
+ IBinder token, int ownerUid, OnDeviceCloseListener listener,
+ VirtualDeviceParams params) {
+ this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
+ params);
}
@VisibleForTesting
VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
- int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
+ int ownerUid, InputController inputController, OnDeviceCloseListener listener,
+ VirtualDeviceParams params) {
mContext = context;
mAssociationInfo = associationInfo;
mOwnerUid = ownerUid;
mAppToken = token;
+ mParams = params;
if (inputController == null) {
mInputController = new InputController(mVirtualDeviceLock);
} else {
@@ -89,7 +96,19 @@
}
}
- @Override
+ /**
+ * Returns the flags that should be added to any virtual displays created on this virtual
+ * device.
+ */
+ int getBaseVirtualDisplayFlags() {
+ int flags = 0;
+ if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+ }
+ return flags;
+ }
+
+ @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 0db670e..12df79d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -24,6 +24,7 @@
import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
import android.os.IBinder;
import android.os.Parcel;
@@ -131,7 +132,10 @@
@Override // Binder call
public IVirtualDevice createVirtualDevice(
- IBinder token, String packageName, int associationId) {
+ IBinder token,
+ String packageName,
+ int associationId,
+ @NonNull VirtualDeviceParams params) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"createVirtualDevice");
@@ -160,7 +164,7 @@
mVirtualDevices.remove(associationId);
}
}
- });
+ }, params);
mVirtualDevices.put(associationInfo.getId(), virtualDevice);
return virtualDevice;
}
@@ -238,6 +242,11 @@
}
@Override
+ public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
+ return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
+ }
+
+ @Override
public boolean isAppOwnerOfAnyVirtualDevice(int uid) {
synchronized (mVirtualDeviceManagerLock) {
int size = mVirtualDevices.size();
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 39fa3f2..135276e 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -50,6 +50,12 @@
public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
/**
+ * Returns the flags that should be added to any virtual displays created on this virtual
+ * device.
+ */
+ public abstract int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice);
+
+ /**
* Returns true if the given {@code uid} is the owner of any virtual devices that are
* currently active.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c0a6abf..20f0f16 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1251,6 +1251,14 @@
}
final Surface surface = virtualDisplayConfig.getSurface();
int flags = virtualDisplayConfig.getFlags();
+ if (virtualDevice != null) {
+ final VirtualDeviceManagerInternal vdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ if (!vdm.isValidVirtualDevice(virtualDevice)) {
+ throw new SecurityException("Invalid virtual device");
+ }
+ flags |= vdm.getBaseVirtualDisplayFlags(virtualDevice);
+ }
if (surface != null && surface.isSingleBuffered()) {
throw new IllegalArgumentException("Surface can't be single-buffered");
@@ -1283,14 +1291,6 @@
}
}
- if (virtualDevice != null) {
- final VirtualDeviceManagerInternal vdm =
- getLocalService(VirtualDeviceManagerInternal.class);
- if (!vdm.isValidVirtualDevice(virtualDevice)) {
- throw new SecurityException("Invalid virtual device");
- }
- }
-
if (callingUid != Process.SYSTEM_UID
&& (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c7c0756..d79a833 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -26,6 +26,7 @@
import static org.testng.Assert.assertThrows;
import android.Manifest;
+import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Point;
@@ -84,7 +85,7 @@
mInputController = new InputController(new Object(), mNativeWrapperMock);
mDeviceImpl = new VirtualDeviceImpl(mContext,
/* association info */ null, new Binder(), /* uid */ 0, mInputController,
- (int associationId) -> {});
+ (int associationId) -> {}, new VirtualDeviceParams.Builder().build());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
new file mode 100644
index 0000000..ecaf581
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat;
+
+import android.companion.virtual.VirtualDeviceParams;
+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 VirtualDeviceParamsTest {
+
+ @Test
+ public void parcelable_shouldRecreateSuccessfully() {
+ VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder().setLockState(
+ VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED).build();
+ Parcel parcel = Parcel.obtain();
+ originalParams.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ VirtualDeviceParams params = VirtualDeviceParams.CREATOR.createFromParcel(parcel);
+ assertThat(params).isEqualTo(originalParams);
+ assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
+ }
+}