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
This commit is contained in:
Maurice Lam
2021-11-17 20:16:08 -08:00
parent 48ef85a789
commit 57cd80185e
10 changed files with 279 additions and 18 deletions

View File

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

View File

@@ -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) {

View File

@@ -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;

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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)) {

View File

@@ -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

View File

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