diff options
5 files changed, 121 insertions, 59 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2c0e6416b455..7d814d60063e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2804,11 +2804,14 @@ package android.companion.virtual { public final class VirtualDeviceParams implements android.os.Parcelable { method public int describeContents(); - method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities(); - method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities(); + method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities(); + method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities(); + method public int getDefaultActivityPolicy(); method public int getLockState(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); method public void writeToParcel(@NonNull android.os.Parcel, int); + 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 LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1 field public static final int LOCK_STATE_DEFAULT = 0; // 0x0 @@ -2817,8 +2820,8 @@ package android.companion.virtual { public static final class VirtualDeviceParams.Builder { ctor public VirtualDeviceParams.Builder(); method @NonNull public android.companion.virtual.VirtualDeviceParams build(); - method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>); - method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); } diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 45d0ad51da04..41b1a1feae80 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -20,9 +20,7 @@ import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.ComponentName; import android.os.Parcel; @@ -64,20 +62,43 @@ public final class VirtualDeviceParams implements Parcelable { */ public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; + /** @hide */ + @IntDef(prefix = "ACTIVITY_POLICY_", + value = {ACTIVITY_POLICY_DEFAULT_ALLOWED, ACTIVITY_POLICY_DEFAULT_BLOCKED}) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface ActivityPolicy {} + + /** + * Indicates that activities are allowed by default on this virtual device, unless they are + * explicitly blocked by {@link Builder#setBlockedActivities}. + */ + public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; + + /** + * Indicates that activities are blocked by default on this virtual device, unless they are + * allowed by {@link Builder#setAllowedActivities}. + */ + public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; + private final int mLockState; private final ArraySet<UserHandle> mUsersWithMatchingAccounts; - @Nullable private final ArraySet<ComponentName> mAllowedActivities; - @Nullable private final ArraySet<ComponentName> mBlockedActivities; + @NonNull private final ArraySet<ComponentName> mAllowedActivities; + @NonNull private final ArraySet<ComponentName> mBlockedActivities; + @ActivityPolicy + private final int mDefaultActivityPolicy; private VirtualDeviceParams( @LockState int lockState, @NonNull Set<UserHandle> usersWithMatchingAccounts, - @Nullable Set<ComponentName> allowedActivities, - @Nullable Set<ComponentName> blockedActivities) { + @NonNull Set<ComponentName> allowedActivities, + @NonNull Set<ComponentName> blockedActivities, + @ActivityPolicy int defaultActivityPolicy) { mLockState = lockState; mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts); mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities); mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities); + mDefaultActivityPolicy = defaultActivityPolicy; } @SuppressWarnings("unchecked") @@ -86,6 +107,7 @@ public final class VirtualDeviceParams implements Parcelable { mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null); mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); + mDefaultActivityPolicy = parcel.readInt(); } /** @@ -113,12 +135,10 @@ public final class VirtualDeviceParams implements Parcelable { * * @see Builder#setAllowedActivities(Set) */ - // Null and empty have different semantics - Null allows all activities to be streamed - @SuppressLint("NullableCollection") - @Nullable + @NonNull public Set<ComponentName> getAllowedActivities() { if (mAllowedActivities == null) { - return null; + return Collections.emptySet(); } return Collections.unmodifiableSet(mAllowedActivities); } @@ -129,16 +149,27 @@ public final class VirtualDeviceParams implements Parcelable { * * @see Builder#setBlockedActivities(Set) */ - // Allowing null to enforce that at most one of allowed / blocked activities can be non-null - @SuppressLint("NullableCollection") - @Nullable + @NonNull public Set<ComponentName> getBlockedActivities() { if (mBlockedActivities == null) { - return null; + return Collections.emptySet(); } return Collections.unmodifiableSet(mBlockedActivities); } + /** + * Returns {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED} if activities are allowed to launch on this + * virtual device by default, or {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED} if activities must be + * allowed by {@link Builder#setAllowedActivities} to launch here. + * + * @see Builder#setBlockedActivities + * @see Builder#setAllowedActivities + */ + @ActivityPolicy + public int getDefaultActivityPolicy() { + return mDefaultActivityPolicy; + } + @Override public int describeContents() { return 0; @@ -150,6 +181,7 @@ public final class VirtualDeviceParams implements Parcelable { dest.writeArraySet(mUsersWithMatchingAccounts); dest.writeArraySet(mAllowedActivities); dest.writeArraySet(mBlockedActivities); + dest.writeInt(mDefaultActivityPolicy); } @Override @@ -164,12 +196,15 @@ public final class VirtualDeviceParams implements Parcelable { return mLockState == that.mLockState && mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts) && Objects.equals(mAllowedActivities, that.mAllowedActivities) - && Objects.equals(mBlockedActivities, that.mBlockedActivities); + && Objects.equals(mBlockedActivities, that.mBlockedActivities) + && mDefaultActivityPolicy == that.mDefaultActivityPolicy; } @Override public int hashCode() { - return Objects.hash(mLockState, mUsersWithMatchingAccounts); + return Objects.hash( + mLockState, mUsersWithMatchingAccounts, mAllowedActivities, mBlockedActivities, + mDefaultActivityPolicy); } @Override @@ -180,6 +215,7 @@ public final class VirtualDeviceParams implements Parcelable { + " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts + " mAllowedActivities=" + mAllowedActivities + " mBlockedActivities=" + mBlockedActivities + + " mDefaultActivityPolicy=" + mDefaultActivityPolicy + ")"; } @@ -202,8 +238,11 @@ public final class VirtualDeviceParams implements Parcelable { private @LockState int mLockState = LOCK_STATE_DEFAULT; private Set<UserHandle> mUsersWithMatchingAccounts; - @Nullable private Set<ComponentName> mBlockedActivities; - @Nullable private Set<ComponentName> mAllowedActivities; + @NonNull private Set<ComponentName> mBlockedActivities = Collections.emptySet(); + @NonNull private Set<ComponentName> mAllowedActivities = Collections.emptySet(); + @ActivityPolicy + private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; + private boolean mDefaultActivityPolicyConfigured = false; /** * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY} @@ -248,53 +287,53 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Sets the activities allowed to be launched in the virtual device. If - * {@code allowedActivities} is non-null, all activities other than the ones in the set will - * be blocked from launching. + * Sets the activities allowed to be launched in the virtual device. Calling this method + * will cause {@link #getDefaultActivityPolicy()} to be + * {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED}, meaning activities not in + * {@code allowedActivities} will be blocked from launching here. * - * <p>{@code allowedActivities} and the set in {@link #setBlockedActivities(Set)} cannot - * both be non-null at the same time. + * <p>This method must not be called if {@link #setBlockedActivities(Set)} has been called. * - * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been set to a - * non-null value. + * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been called. * * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched * in the virtual device. */ - // Null and empty have different semantics - Null allows all activities to be streamed - @SuppressLint("NullableCollection") @NonNull - public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) { - if (mBlockedActivities != null && allowedActivities != null) { + public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) { + if (mDefaultActivityPolicyConfigured + && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_BLOCKED) { throw new IllegalArgumentException( "Allowed activities and Blocked activities cannot both be set."); } + mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_BLOCKED; + mDefaultActivityPolicyConfigured = true; mAllowedActivities = allowedActivities; return this; } /** - * Sets the activities blocked from launching in the virtual device. If the {@code - * blockedActivities} is non-null, activities in the set are blocked from launching in the - * virtual device. + * Sets the activities blocked from launching in the virtual device. Calling this method + * will cause {@link #getDefaultActivityPolicy()} to be + * {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch here + * unless they are in {@code blockedActivities}. * - * {@code blockedActivities} and the set in {@link #setAllowedActivities(Set)} cannot both - * be non-null at the same time. + * <p>This method must not be called if {@link #setAllowedActivities(Set)} has been called. * - * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been set to a - * non-null value. + * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been called. * * @param blockedActivities A set of {@link ComponentName} to be blocked launching from * virtual device. */ - // Allowing null to enforce that at most one of allowed / blocked activities can be non-null - @SuppressLint("NullableCollection") @NonNull - public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) { - if (mAllowedActivities != null && blockedActivities != null) { + public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) { + if (mDefaultActivityPolicyConfigured + && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_ALLOWED) { throw new IllegalArgumentException( "Allowed activities and Blocked activities cannot both be set."); } + mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; + mDefaultActivityPolicyConfigured = true; mBlockedActivities = blockedActivities; return this; } @@ -307,13 +346,12 @@ public final class VirtualDeviceParams implements Parcelable { if (mUsersWithMatchingAccounts == null) { mUsersWithMatchingAccounts = Collections.emptySet(); } - if (mAllowedActivities != null && mBlockedActivities != null) { - // Should never reach here because the setters block this as well. - throw new IllegalStateException( - "Allowed activities and Blocked activities cannot both be set."); - } - return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts, - mAllowedActivities, mBlockedActivities); + return new VirtualDeviceParams( + mLockState, + mUsersWithMatchingAccounts, + mAllowedActivities, + mBlockedActivities, + mDefaultActivityPolicy); } } } diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index bc1f28d1c373..b991ba87eef4 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -24,6 +24,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.compat.CompatChanges; import android.companion.virtual.VirtualDeviceManager.ActivityListener; +import android.companion.virtual.VirtualDeviceParams; +import android.companion.virtual.VirtualDeviceParams.ActivityPolicy; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.ComponentName; @@ -77,7 +79,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Nullable private final ArraySet<ComponentName> mBlockedActivities; private final Object mGenericWindowPolicyControllerLock = new Object(); - private Consumer<ActivityInfo> mActivityBlockedCallback; + @ActivityPolicy + private final int mDefaultActivityPolicy; + private final Consumer<ActivityInfo> mActivityBlockedCallback; @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") @@ -95,18 +99,30 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param windowFlags The window flags that this controller is interested in. * @param systemWindowFlags The system window flags that this controller is interested in. * @param allowedUsers The set of users that are allowed to stream in this display. + * @param allowedActivities The set of activities explicitly allowed to stream on this device. + * Used only if the {@code activityPolicy} is + * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_BLOCKED}. + * @param blockedActivities The set of activities explicitly blocked from streaming on this + * device. Used only if the {@code activityPolicy} is + * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED} + * @param defaultActivityPolicy Whether activities are default allowed to be displayed or + * blocked. * @param activityListener Activity listener to listen for activity changes. The display ID * is not populated in this callback and is always {@link Display#INVALID_DISPLAY}. + * @param activityBlockedCallback Callback that is called when an activity is blocked from + * launching. */ public GenericWindowPolicyController(int windowFlags, int systemWindowFlags, @NonNull ArraySet<UserHandle> allowedUsers, - @Nullable Set<ComponentName> allowedActivities, - @Nullable Set<ComponentName> blockedActivities, + @NonNull Set<ComponentName> allowedActivities, + @NonNull Set<ComponentName> blockedActivities, + @ActivityPolicy int defaultActivityPolicy, @NonNull ActivityListener activityListener, @NonNull Consumer<ActivityInfo> activityBlockedCallback) { mAllowedUsers = allowedUsers; - mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities); - mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities); + mAllowedActivities = new ArraySet<>(allowedActivities); + mBlockedActivities = new ArraySet<>(blockedActivities); + mDefaultActivityPolicy = defaultActivityPolicy; mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; @@ -191,11 +207,13 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser); return false; } - if (mBlockedActivities != null && mBlockedActivities.contains(activityComponent)) { + if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED + && mBlockedActivities.contains(activityComponent)) { Slog.d(TAG, "Virtual device blocking launch of " + activityComponent); return false; } - if (mAllowedActivities != null && !mAllowedActivities.contains(activityComponent)) { + if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_BLOCKED + && !mAllowedActivities.contains(activityComponent)) { Slog.d(TAG, activityComponent + " is not in the allowed list."); return false; } 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 c0a904fe3d9a..dbb48aea94ff 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -505,6 +505,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub getAllowedUserHandles(), mParams.getAllowedActivities(), mParams.getBlockedActivities(), + mParams.getDefaultActivityPolicy(), createListenerAdapter(displayId), activityInfo -> onActivityBlocked(displayId, activityInfo)); mWindowPolicyControllers.put(displayId, dwpc); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index 3160272ef9b1..f0c907d49a46 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.audio.IAudioSessionCallback; import android.content.Context; import android.content.ContextWrapper; @@ -72,7 +73,8 @@ public class VirtualAudioControllerTest { /* allowedUsers= */ new ArraySet<>(), /* allowedActivities= */ new ArraySet<>(), /* blockedActivities= */ new ArraySet<>(), - /* activityListener= */null, + VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED, + /* activityListener= */ null, /* activityBlockedCallback= */ null); } |