From 57cd80185e846defabfc710080a19adfb798efb6 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Wed, 17 Nov 2021 20:16:08 -0800 Subject: [PATCH] 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 --- .../virtual/IVirtualDeviceManager.aidl | 7 +- .../virtual/VirtualDeviceManager.java | 10 +- .../virtual/VirtualDeviceParams.aidl | 19 +++ .../virtual/VirtualDeviceParams.java | 152 ++++++++++++++++++ .../companion/virtual/VirtualDeviceImpl.java | 27 +++- .../virtual/VirtualDeviceManagerService.java | 13 +- .../virtual/VirtualDeviceManagerInternal.java | 6 + .../server/display/DisplayManagerService.java | 16 +- .../VirtualDeviceManagerServiceTest.java | 3 +- .../virtual/VirtualDeviceParamsTest.java | 44 +++++ 10 files changed, 279 insertions(+), 18 deletions(-) create mode 100644 core/java/android/companion/virtual/VirtualDeviceParams.aidl create mode 100644 core/java/android/companion/virtual/VirtualDeviceParams.java create mode 100644 services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index 2dfa2021fdfe0..d80bee668f182 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 @@ interface IVirtualDeviceManager { * 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 bace45bccbf44..858e4daa136f0 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -100,11 +100,11 @@ public final class VirtualDeviceManager { */ @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 @@ public final class VirtualDeviceManager { } } + /** + * 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 0000000000000..9b3974a9c904d --- /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 0000000000000..ad2dea9958fd6 --- /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 CREATOR = + new Parcelable.Creator() { + 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 ca35e033ed70c..01e8d3bce211b 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 static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE 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 class VirtualDeviceImpl extends IVirtualDevice.Stub final List 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 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub 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 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } - @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 0db670e469092..12df79d2ddc90 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; 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 @@ public class VirtualDeviceManagerService extends SystemService { @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 @@ public class VirtualDeviceManagerService extends SystemService { mVirtualDevices.remove(associationId); } } - }); + }, params); mVirtualDevices.put(associationInfo.getId(), virtualDevice); return virtualDevice; } @@ -237,6 +241,11 @@ public class VirtualDeviceManagerService extends SystemService { } } + @Override + public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) { + return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags(); + } + @Override public boolean isAppOwnerOfAnyVirtualDevice(int uid) { synchronized (mVirtualDeviceManagerLock) { 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 39fa3f200aab0..135276e789c1e 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -49,6 +49,12 @@ public abstract class VirtualDeviceManagerInternal { */ 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 c0a6abf3a121a..20f0f16d947fa 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 @@ public final class DisplayManagerService extends SystemService { } 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 @@ public final class DisplayManagerService extends SystemService { } } - 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 c7c0756bc0d08..d79a833d70460 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.mockito.Mockito.verify; 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 @@ public class VirtualDeviceManagerServiceTest { 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 0000000000000..ecaf5814d8fe1 --- /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); + } +}