Allow virtual device owners to specify custom policies per policy type.

The policy per device and per type is available to the system components via both VirtualDeviceManager and VirtualDeviceManagerInternal. Currently only adding SENSORS polcy type.

Bug: 239152561
Bug: 237278244
Test: atest VirtualDeviceParamsTest, atest VirtualDeviceManagerServiceTest
Change-Id: Id32997d716653306abbff5e9dacf600a0ee9f3e4
This commit is contained in:
Vladimir Komsiyski
2022-10-21 13:37:41 +02:00
parent dc919d1d84
commit 877f52c27a
9 changed files with 228 additions and 4 deletions

View File

@@ -2832,6 +2832,7 @@ package android.companion.virtual {
method @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
method public int getDefaultActivityPolicy();
method public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
method public int getLockState();
method @Nullable public String getName();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -2839,14 +2840,18 @@ package android.companion.virtual {
field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int DEVICE_POLICY_CUSTOM = 1; // 0x1
field public static final int DEVICE_POLICY_DEFAULT = 0; // 0x0
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
}
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addDevicePolicy(int, int);
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);

View File

@@ -51,6 +51,11 @@ interface IVirtualDeviceManager {
*/
List<VirtualDevice> getVirtualDevices();
/**
* Returns the device policy for the given virtual device and policy type.
*/
int getDevicePolicy(int deviceId, int policyType);
/**
* Creates a virtual display owned by a particular virtual device.
*

View File

@@ -181,6 +181,28 @@ public final class VirtualDeviceManager {
}
}
/**
* 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
* policy for that device and policy type, then
* {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT} is returned.
*
* @hide
*/
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
if (mService == null) {
Log.w(TAG, "Failed to retrieve device policy; no virtual device manager service.");
return VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
}
try {
return mService.getDevicePolicy(deviceId, policyType);
} 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

View File

@@ -28,6 +28,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
@@ -103,6 +104,47 @@ public final class VirtualDeviceParams implements Parcelable {
*/
public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1;
/** @hide */
@IntDef(prefix = "DEVICE_POLICY_", value = {DEVICE_POLICY_DEFAULT, DEVICE_POLICY_CUSTOM})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface DevicePolicy {}
/**
* Indicates that there is no special logic for this virtual device and it should be treated
* the same way as the default device, keeping the default behavior unchanged.
*/
public static final int DEVICE_POLICY_DEFAULT = 0;
/**
* Indicates that there is custom logic, specific to this virtual device, which should be
* triggered instead of the default behavior.
*/
public static final int DEVICE_POLICY_CUSTOM = 1;
/**
* Any relevant component must be able to interpret the correct meaning of a custom policy for
* a given policy type.
* @hide
*/
@IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface PolicyType {}
/**
* Tells the sensor framework how to handle sensor requests from contexts associated with this
* virtual device, namely the sensors returned by
* {@link android.hardware.SensorManager#getSensorList}:
*
* <ul>
* <li>{@link #DEVICE_POLICY_DEFAULT}: Return the sensors of the default device.
* <li>{@link #DEVICE_POLICY_CUSTOM}: Return the sensors of the virtual device. Note that if
* the virtual device did not create any virtual sensors, then an empty list is returned.
* </ul>
*/
public static final int POLICY_TYPE_SENSORS = 0;
private final int mLockState;
@NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
@NonNull private final ArraySet<ComponentName> mAllowedCrossTaskNavigations;
@@ -114,6 +156,8 @@ public final class VirtualDeviceParams implements Parcelable {
@ActivityPolicy
private final int mDefaultActivityPolicy;
@Nullable private final String mName;
// Mapping of @PolicyType to @DevicePolicy
@NonNull private final SparseIntArray mDevicePolicies;
private VirtualDeviceParams(
@LockState int lockState,
@@ -124,12 +168,14 @@ public final class VirtualDeviceParams implements Parcelable {
@NonNull Set<ComponentName> allowedActivities,
@NonNull Set<ComponentName> blockedActivities,
@ActivityPolicy int defaultActivityPolicy,
@Nullable String name) {
@Nullable String name,
@NonNull SparseIntArray devicePolicies) {
Preconditions.checkNotNull(usersWithMatchingAccounts);
Preconditions.checkNotNull(allowedCrossTaskNavigations);
Preconditions.checkNotNull(blockedCrossTaskNavigations);
Preconditions.checkNotNull(allowedActivities);
Preconditions.checkNotNull(blockedActivities);
Preconditions.checkNotNull(devicePolicies);
mLockState = lockState;
mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
@@ -140,6 +186,7 @@ public final class VirtualDeviceParams implements Parcelable {
mBlockedActivities = new ArraySet<>(blockedActivities);
mDefaultActivityPolicy = defaultActivityPolicy;
mName = name;
mDevicePolicies = devicePolicies;
}
@SuppressWarnings("unchecked")
@@ -153,6 +200,7 @@ public final class VirtualDeviceParams implements Parcelable {
mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
mDefaultActivityPolicy = parcel.readInt();
mName = parcel.readString8();
mDevicePolicies = parcel.readSparseIntArray();
}
/**
@@ -258,6 +306,16 @@ public final class VirtualDeviceParams implements Parcelable {
return mName;
}
/**
* Returns the policy specified for this policy type, or {@link #DEVICE_POLICY_DEFAULT} if no
* policy for this type has been explicitly specified.
*
* @see Builder#addDevicePolicy
*/
public @DevicePolicy int getDevicePolicy(@PolicyType int policyType) {
return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
}
@Override
public int describeContents() {
return 0;
@@ -274,6 +332,7 @@ public final class VirtualDeviceParams implements Parcelable {
dest.writeArraySet(mBlockedActivities);
dest.writeInt(mDefaultActivityPolicy);
dest.writeString8(mName);
dest.writeSparseIntArray(mDevicePolicies);
}
@Override
@@ -285,6 +344,18 @@ public final class VirtualDeviceParams implements Parcelable {
return false;
}
VirtualDeviceParams that = (VirtualDeviceParams) o;
final int devicePoliciesCount = mDevicePolicies.size();
if (devicePoliciesCount != that.mDevicePolicies.size()) {
return false;
}
for (int i = 0; i < devicePoliciesCount; i++) {
if (mDevicePolicies.keyAt(i) != that.mDevicePolicies.keyAt(i)) {
return false;
}
if (mDevicePolicies.valueAt(i) != that.mDevicePolicies.valueAt(i)) {
return false;
}
}
return mLockState == that.mLockState
&& mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
&& Objects.equals(mAllowedCrossTaskNavigations, that.mAllowedCrossTaskNavigations)
@@ -298,10 +369,15 @@ public final class VirtualDeviceParams implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(
int hashCode = Objects.hash(
mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations,
mBlockedCrossTaskNavigations, mDefaultNavigationPolicy, mAllowedActivities,
mBlockedActivities, mDefaultActivityPolicy, mName);
mBlockedActivities, mDefaultActivityPolicy, mName, mDevicePolicies);
for (int i = 0; i < mDevicePolicies.size(); i++) {
hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
}
return hashCode;
}
@Override
@@ -317,6 +393,7 @@ public final class VirtualDeviceParams implements Parcelable {
+ " mBlockedActivities=" + mBlockedActivities
+ " mDefaultActivityPolicy=" + mDefaultActivityPolicy
+ " mName=" + mName
+ " mDevicePolicies=" + mDevicePolicies
+ ")";
}
@@ -350,6 +427,7 @@ public final class VirtualDeviceParams implements Parcelable {
private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
private boolean mDefaultActivityPolicyConfigured = false;
@Nullable private String mName;
@NonNull private SparseIntArray mDevicePolicies = new SparseIntArray();
/**
* Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -527,6 +605,18 @@ public final class VirtualDeviceParams implements Parcelable {
return this;
}
/**
* Specifies a policy for this virtual device.
*
* @param policyType the type of policy, i.e. which behavior to specify a policy for.
* @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
*/
@NonNull
public Builder addDevicePolicy(@PolicyType int policyType, @DevicePolicy int devicePolicy) {
mDevicePolicies.put(policyType, devicePolicy);
return this;
}
/**
* Builds the {@link VirtualDeviceParams} instance.
*/
@@ -541,7 +631,8 @@ public final class VirtualDeviceParams implements Parcelable {
mAllowedActivities,
mBlockedActivities,
mDefaultActivityPolicy,
mName);
mName,
mDevicePolicies);
}
}
}

View File

@@ -227,6 +227,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return mParams.getName();
}
/** Returns the policy specified for this policy type */
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
@VirtualDeviceParams.PolicyType int policyType) {
return mParams.getDevicePolicy(policyType);
}
/** Returns the unique device ID of this device. */
@Override // Binder call
public int getDeviceId() {

View File

@@ -233,6 +233,13 @@ public class VirtualDeviceManagerService extends SystemService {
mLocalService.onAppsOnVirtualDeviceChanged();
}
@VisibleForTesting
void addVirtualDevice(VirtualDeviceImpl virtualDevice) {
synchronized (mVirtualDeviceManagerLock) {
mVirtualDevices.put(virtualDevice.getAssociationId(), virtualDevice);
}
}
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -358,6 +365,12 @@ public class VirtualDeviceManagerService extends SystemService {
return virtualDevices;
}
@Override // BinderCall
@VirtualDeviceParams.DevicePolicy
public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
return mLocalService.getDevicePolicy(deviceId, policyType);
}
@Nullable
private AssociationInfo getAssociationInfo(String packageName, int associationId) {
final int callingUserId = getCallingUserHandle().getIdentifier();
@@ -438,6 +451,20 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
@Override
@VirtualDeviceParams.DevicePolicy
public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mVirtualDevices.size(); i++) {
final VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
if (device.getDeviceId() == deviceId) {
return device.getDevicePolicy(policyType);
}
}
}
return VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
}
@Override
public void onVirtualDisplayCreated(int displayId) {
final VirtualDisplayListener[] listeners;

View File

@@ -18,6 +18,7 @@ package com.android.server.companion.virtual;
import android.annotation.NonNull;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.VirtualDeviceParams;
import java.util.Set;
@@ -109,4 +110,14 @@ public abstract class VirtualDeviceManagerInternal {
* Returns true if the {@code displayId} is owned by any virtual device
*/
public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
/**
* 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
* policy for that device and policy type, then
* {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT} is returned.
*/
public abstract @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
int deviceId, @VirtualDeviceParams.PolicyType int policyType);
}

View File

@@ -16,6 +16,9 @@
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.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
import static com.google.common.truth.Truth.assertThat;
@@ -44,6 +47,7 @@ import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -240,6 +244,55 @@ public class VirtualDeviceManagerServiceTest {
mAssociationInfo, new Binder(), /* ownerUid */ 0, /* uniqueId */ 1,
mInputController, (int associationId) -> {}, mPendingTrampolineCallback,
mActivityListener, mRunningAppsChangedCallback, params);
mVdms.addVirtualDevice(mDeviceImpl);
}
@Test
public void getDevicePolicy_invalidDeviceId_returnsDefault() {
assertThat(
mLocalService.getDevicePolicy(
VirtualDeviceManager.INVALID_DEVICE_ID, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_defaultDeviceId_returnsDefault() {
assertThat(
mLocalService.getDevicePolicy(
VirtualDeviceManager.DEFAULT_DEVICE_ID, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_nonExistentDeviceId_returnsDefault() {
assertThat(
mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_unspecifiedPolicy_returnsDefault() {
assertThat(
mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_returnsCustom() {
VirtualDeviceParams params = new VirtualDeviceParams
.Builder()
.setBlockedActivities(getBlockedActivities())
.addDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
.build();
mDeviceImpl = new VirtualDeviceImpl(mContext,
mAssociationInfo, new Binder(), /* ownerUid */ 0, /* uniqueId */ 1,
mInputController, (int associationId) -> {}, mPendingTrampolineCallback,
mActivityListener, mRunningAppsChangedCallback, params);
mVdms.addVirtualDevice(mDeviceImpl);
assertThat(
mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_CUSTOM);
}
@Test

View File

@@ -37,6 +37,8 @@ public class VirtualDeviceParamsTest {
VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
.setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
.setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
.addDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS,
VirtualDeviceParams.DEVICE_POLICY_CUSTOM)
.build();
Parcel parcel = Parcel.obtain();
originalParams.writeToParcel(parcel, 0);
@@ -47,5 +49,7 @@ public class VirtualDeviceParamsTest {
assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
assertThat(params.getUsersWithMatchingAccounts())
.containsExactly(UserHandle.of(123), UserHandle.of(456));
assertThat(params.getDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS))
.isEqualTo(VirtualDeviceParams.DEVICE_POLICY_CUSTOM);
}
}