1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.companion.virtual; 18 19 import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; 20 import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE; 21 22 import static java.util.concurrent.TimeUnit.MICROSECONDS; 23 24 import android.annotation.CallbackExecutor; 25 import android.annotation.IntDef; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.RequiresPermission; 29 import android.annotation.SuppressLint; 30 import android.annotation.SystemApi; 31 import android.companion.virtual.sensor.IVirtualSensorCallback; 32 import android.companion.virtual.sensor.VirtualSensor; 33 import android.companion.virtual.sensor.VirtualSensorCallback; 34 import android.companion.virtual.sensor.VirtualSensorConfig; 35 import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.os.Parcel; 39 import android.os.Parcelable; 40 import android.os.SharedMemory; 41 import android.os.UserHandle; 42 import android.util.ArraySet; 43 import android.util.SparseArray; 44 import android.util.SparseIntArray; 45 46 import java.lang.annotation.ElementType; 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.lang.annotation.Target; 50 import java.time.Duration; 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.Objects; 55 import java.util.Set; 56 import java.util.concurrent.Executor; 57 58 /** 59 * Params that can be configured when creating virtual devices. 60 * 61 * @hide 62 */ 63 @SystemApi 64 public final class VirtualDeviceParams implements Parcelable { 65 66 /** @hide */ 67 @IntDef(prefix = "LOCK_STATE_", 68 value = {LOCK_STATE_DEFAULT, LOCK_STATE_ALWAYS_UNLOCKED}) 69 @Retention(RetentionPolicy.SOURCE) 70 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 71 public @interface LockState {} 72 73 /** 74 * Indicates that the lock state of the virtual device will be the same as the default physical 75 * display. 76 */ 77 public static final int LOCK_STATE_DEFAULT = 0; 78 79 /** 80 * Indicates that the lock state of the virtual device should be always unlocked. 81 */ 82 public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; 83 84 /** @hide */ 85 @IntDef(prefix = "ACTIVITY_POLICY_", 86 value = {ACTIVITY_POLICY_DEFAULT_ALLOWED, ACTIVITY_POLICY_DEFAULT_BLOCKED}) 87 @Retention(RetentionPolicy.SOURCE) 88 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 89 public @interface ActivityPolicy {} 90 91 /** 92 * Indicates that activities are allowed by default on this virtual device, unless they are 93 * explicitly blocked by {@link Builder#setBlockedActivities}. 94 */ 95 public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; 96 97 /** 98 * Indicates that activities are blocked by default on this virtual device, unless they are 99 * allowed by {@link Builder#setAllowedActivities}. 100 */ 101 public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; 102 103 /** @hide */ 104 @IntDef(prefix = "NAVIGATION_POLICY_", 105 value = {NAVIGATION_POLICY_DEFAULT_ALLOWED, NAVIGATION_POLICY_DEFAULT_BLOCKED}) 106 @Retention(RetentionPolicy.SOURCE) 107 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 108 public @interface NavigationPolicy {} 109 110 /** 111 * Indicates that tasks are allowed to navigate to other tasks on this virtual device, 112 * unless they are explicitly blocked by {@link Builder#setBlockedCrossTaskNavigations}. 113 */ 114 public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; 115 116 /** 117 * Indicates that tasks are blocked from navigating to other tasks by default on this virtual 118 * device, unless allowed by {@link Builder#setAllowedCrossTaskNavigations}. 119 */ 120 public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; 121 122 /** @hide */ 123 @IntDef(prefix = "DEVICE_POLICY_", value = {DEVICE_POLICY_DEFAULT, DEVICE_POLICY_CUSTOM}) 124 @Retention(RetentionPolicy.SOURCE) 125 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 126 public @interface DevicePolicy {} 127 128 /** 129 * Indicates that there is no special logic for this virtual device and it should be treated 130 * the same way as the default device, keeping the default behavior unchanged. 131 */ 132 public static final int DEVICE_POLICY_DEFAULT = 0; 133 134 /** 135 * Indicates that there is custom logic, specific to this virtual device, which should be 136 * triggered instead of the default behavior. 137 */ 138 public static final int DEVICE_POLICY_CUSTOM = 1; 139 140 /** 141 * Any relevant component must be able to interpret the correct meaning of a custom policy for 142 * a given policy type. 143 * @hide 144 */ 145 @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO, 146 POLICY_TYPE_RECENTS}) 147 @Retention(RetentionPolicy.SOURCE) 148 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 149 public @interface PolicyType {} 150 151 /** 152 * Tells the sensor framework how to handle sensor requests from contexts associated with this 153 * virtual device, namely the sensors returned by 154 * {@link android.hardware.SensorManager#getSensorList}: 155 * 156 * <ul> 157 * <li>{@link #DEVICE_POLICY_DEFAULT}: Return the sensors of the default device. 158 * <li>{@link #DEVICE_POLICY_CUSTOM}: Return the sensors of the virtual device. Note that if 159 * the virtual device did not create any virtual sensors, then an empty list is returned. 160 * </ul> 161 */ 162 public static final int POLICY_TYPE_SENSORS = 0; 163 164 /** 165 * Tells the audio framework whether to configure the players ({@link android.media.AudioTrack}, 166 * {@link android.media.MediaPlayer}, {@link android.media.SoundPool} and recorders 167 * {@link android.media.AudioRecord}) to use specific session ids re-routed to 168 * VirtualAudioDevice. 169 * 170 * <ul> 171 * <li>{@link #DEVICE_POLICY_DEFAULT}: fall back to default session id handling. 172 * <li>{@link #DEVICE_POLICY_CUSTOM}: audio framework will assign device specific session 173 * ids to players and recorders constructed within device context. The session ids are 174 * used to re-route corresponding audio streams to VirtualAudioDevice. 175 * </ul> 176 */ 177 public static final int POLICY_TYPE_AUDIO = 1; 178 179 /** 180 * Tells the activity manager how to handle recents entries for activities run on this device. 181 * 182 * <ul> 183 * <li>{@link #DEVICE_POLICY_DEFAULT}: Activities launched on VirtualDisplays owned by this 184 * device will appear in the host device recents. 185 * <li>{@link #DEVICE_POLICY_CUSTOM}: Activities launched on VirtualDisplays owned by this 186 * * device will not appear in recents. 187 * </ul> 188 */ 189 public static final int POLICY_TYPE_RECENTS = 2; 190 191 private final int mLockState; 192 @NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts; 193 @NonNull private final ArraySet<ComponentName> mAllowedCrossTaskNavigations; 194 @NonNull private final ArraySet<ComponentName> mBlockedCrossTaskNavigations; 195 @NavigationPolicy 196 private final int mDefaultNavigationPolicy; 197 @NonNull private final ArraySet<ComponentName> mAllowedActivities; 198 @NonNull private final ArraySet<ComponentName> mBlockedActivities; 199 @ActivityPolicy 200 private final int mDefaultActivityPolicy; 201 @Nullable private final String mName; 202 // Mapping of @PolicyType to @DevicePolicy 203 @NonNull private final SparseIntArray mDevicePolicies; 204 @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs; 205 @Nullable private final IVirtualSensorCallback mVirtualSensorCallback; 206 private final int mAudioPlaybackSessionId; 207 private final int mAudioRecordingSessionId; 208 VirtualDeviceParams( @ockState int lockState, @NonNull Set<UserHandle> usersWithMatchingAccounts, @NonNull Set<ComponentName> allowedCrossTaskNavigations, @NonNull Set<ComponentName> blockedCrossTaskNavigations, @NavigationPolicy int defaultNavigationPolicy, @NonNull Set<ComponentName> allowedActivities, @NonNull Set<ComponentName> blockedActivities, @ActivityPolicy int defaultActivityPolicy, @Nullable String name, @NonNull SparseIntArray devicePolicies, @NonNull List<VirtualSensorConfig> virtualSensorConfigs, @Nullable IVirtualSensorCallback virtualSensorCallback, int audioPlaybackSessionId, int audioRecordingSessionId)209 private VirtualDeviceParams( 210 @LockState int lockState, 211 @NonNull Set<UserHandle> usersWithMatchingAccounts, 212 @NonNull Set<ComponentName> allowedCrossTaskNavigations, 213 @NonNull Set<ComponentName> blockedCrossTaskNavigations, 214 @NavigationPolicy int defaultNavigationPolicy, 215 @NonNull Set<ComponentName> allowedActivities, 216 @NonNull Set<ComponentName> blockedActivities, 217 @ActivityPolicy int defaultActivityPolicy, 218 @Nullable String name, 219 @NonNull SparseIntArray devicePolicies, 220 @NonNull List<VirtualSensorConfig> virtualSensorConfigs, 221 @Nullable IVirtualSensorCallback virtualSensorCallback, 222 int audioPlaybackSessionId, 223 int audioRecordingSessionId) { 224 mLockState = lockState; 225 mUsersWithMatchingAccounts = 226 new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts)); 227 mAllowedCrossTaskNavigations = 228 new ArraySet<>(Objects.requireNonNull(allowedCrossTaskNavigations)); 229 mBlockedCrossTaskNavigations = 230 new ArraySet<>(Objects.requireNonNull(blockedCrossTaskNavigations)); 231 mDefaultNavigationPolicy = defaultNavigationPolicy; 232 mAllowedActivities = new ArraySet<>(Objects.requireNonNull(allowedActivities)); 233 mBlockedActivities = new ArraySet<>(Objects.requireNonNull(blockedActivities)); 234 mDefaultActivityPolicy = defaultActivityPolicy; 235 mName = name; 236 mDevicePolicies = Objects.requireNonNull(devicePolicies); 237 mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); 238 mVirtualSensorCallback = virtualSensorCallback; 239 mAudioPlaybackSessionId = audioPlaybackSessionId; 240 mAudioRecordingSessionId = audioRecordingSessionId; 241 } 242 243 @SuppressWarnings("unchecked") VirtualDeviceParams(Parcel parcel)244 private VirtualDeviceParams(Parcel parcel) { 245 mLockState = parcel.readInt(); 246 mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null); 247 mAllowedCrossTaskNavigations = (ArraySet<ComponentName>) parcel.readArraySet(null); 248 mBlockedCrossTaskNavigations = (ArraySet<ComponentName>) parcel.readArraySet(null); 249 mDefaultNavigationPolicy = parcel.readInt(); 250 mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); 251 mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null); 252 mDefaultActivityPolicy = parcel.readInt(); 253 mName = parcel.readString8(); 254 mDevicePolicies = parcel.readSparseIntArray(); 255 mVirtualSensorConfigs = new ArrayList<>(); 256 parcel.readTypedList(mVirtualSensorConfigs, VirtualSensorConfig.CREATOR); 257 mVirtualSensorCallback = 258 IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder()); 259 mAudioPlaybackSessionId = parcel.readInt(); 260 mAudioRecordingSessionId = parcel.readInt(); 261 } 262 263 /** 264 * Returns the lock state of the virtual device. 265 */ 266 @LockState getLockState()267 public int getLockState() { 268 return mLockState; 269 } 270 271 /** 272 * Returns the user handles with matching managed accounts on the remote device to which 273 * this virtual device is streaming. 274 * 275 * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY 276 */ 277 @NonNull getUsersWithMatchingAccounts()278 public Set<UserHandle> getUsersWithMatchingAccounts() { 279 return Collections.unmodifiableSet(mUsersWithMatchingAccounts); 280 } 281 282 /** 283 * Returns the set of tasks that are allowed to navigate from current task, 284 * or empty set if all tasks are allowed, except the ones explicitly blocked. 285 * If neither allowed or blocked tasks are provided, all task navigations will 286 * be be allowed by default. 287 * 288 * @see Builder#setAllowedCrossTaskNavigations(Set) 289 */ 290 @NonNull getAllowedCrossTaskNavigations()291 public Set<ComponentName> getAllowedCrossTaskNavigations() { 292 return Collections.unmodifiableSet(mAllowedCrossTaskNavigations); 293 } 294 295 /** 296 * Returns the set of tasks that are blocked from navigating from the current task, 297 * or empty set to indicate that all tasks in {@link #getAllowedCrossTaskNavigations} 298 * are allowed. If neither allowed or blocked tasks are provided, all task navigations 299 * will be be allowed by default. 300 * 301 * @see Builder#setBlockedCrossTaskNavigations(Set) 302 */ 303 @NonNull getBlockedCrossTaskNavigations()304 public Set<ComponentName> getBlockedCrossTaskNavigations() { 305 return Collections.unmodifiableSet(mBlockedCrossTaskNavigations); 306 } 307 308 /** 309 * Returns {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED} if tasks are allowed to navigate on 310 * this virtual device by default, or {@link #NAVIGATION_POLICY_DEFAULT_BLOCKED} if tasks 311 * must be allowed by {@link Builder#setAllowedCrossTaskNavigations} to navigate here. 312 * 313 * @see Builder#setAllowedCrossTaskNavigations 314 * @see Builder#setBlockedCrossTaskNavigations 315 */ 316 @NavigationPolicy getDefaultNavigationPolicy()317 public int getDefaultNavigationPolicy() { 318 return mDefaultNavigationPolicy; 319 } 320 321 /** 322 * Returns the set of activities allowed to be streamed, or empty set if all activities are 323 * allowed, except the ones explicitly blocked. 324 * 325 * @see Builder#setAllowedActivities(Set) 326 */ 327 @NonNull getAllowedActivities()328 public Set<ComponentName> getAllowedActivities() { 329 return Collections.unmodifiableSet(mAllowedActivities); 330 } 331 332 /** 333 * Returns the set of activities that are blocked from streaming, or empty set to indicate 334 * that all activities in {@link #getAllowedActivities} are allowed. 335 * 336 * @see Builder#setBlockedActivities(Set) 337 */ 338 @NonNull getBlockedActivities()339 public Set<ComponentName> getBlockedActivities() { 340 return Collections.unmodifiableSet(mBlockedActivities); 341 } 342 343 /** 344 * Returns {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED} if activities are allowed to launch on this 345 * virtual device by default, or {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED} if activities must be 346 * allowed by {@link Builder#setAllowedActivities} to launch here. 347 * 348 * @see Builder#setBlockedActivities 349 * @see Builder#setAllowedActivities 350 */ 351 @ActivityPolicy getDefaultActivityPolicy()352 public int getDefaultActivityPolicy() { 353 return mDefaultActivityPolicy; 354 } 355 356 /** 357 * Returns the (optional) name of the virtual device. 358 * 359 * @see Builder#setName 360 */ 361 @Nullable getName()362 public String getName() { 363 return mName; 364 } 365 366 /** 367 * Returns the policy specified for this policy type, or {@link #DEVICE_POLICY_DEFAULT} if no 368 * policy for this type has been explicitly specified. 369 * 370 * @see Builder#setDevicePolicy 371 */ getDevicePolicy(@olicyType int policyType)372 public @DevicePolicy int getDevicePolicy(@PolicyType int policyType) { 373 return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT); 374 } 375 376 /** 377 * Returns the configurations for all sensors that should be created for this device. 378 * 379 * @see Builder#addVirtualSensorConfig 380 */ getVirtualSensorConfigs()381 public @NonNull List<VirtualSensorConfig> getVirtualSensorConfigs() { 382 return mVirtualSensorConfigs; 383 } 384 385 /** 386 * Returns the callback to get notified about changes in the sensor listeners or sensor direct 387 * channel configuration. 388 * @hide 389 */ 390 @Nullable getVirtualSensorCallback()391 public IVirtualSensorCallback getVirtualSensorCallback() { 392 return mVirtualSensorCallback; 393 } 394 395 /** 396 * Returns device-specific audio session id for playback. 397 * 398 * @see Builder#setAudioPlaybackSessionId(int) 399 */ getAudioPlaybackSessionId()400 public int getAudioPlaybackSessionId() { 401 return mAudioPlaybackSessionId; 402 } 403 404 /** 405 * Returns device-specific audio session id for recording. 406 * 407 * @see Builder#setAudioRecordingSessionId(int) 408 */ getAudioRecordingSessionId()409 public int getAudioRecordingSessionId() { 410 return mAudioRecordingSessionId; 411 } 412 413 @Override describeContents()414 public int describeContents() { 415 return 0; 416 } 417 418 @Override writeToParcel(@onNull Parcel dest, int flags)419 public void writeToParcel(@NonNull Parcel dest, int flags) { 420 dest.writeInt(mLockState); 421 dest.writeArraySet(mUsersWithMatchingAccounts); 422 dest.writeArraySet(mAllowedCrossTaskNavigations); 423 dest.writeArraySet(mBlockedCrossTaskNavigations); 424 dest.writeInt(mDefaultNavigationPolicy); 425 dest.writeArraySet(mAllowedActivities); 426 dest.writeArraySet(mBlockedActivities); 427 dest.writeInt(mDefaultActivityPolicy); 428 dest.writeString8(mName); 429 dest.writeSparseIntArray(mDevicePolicies); 430 dest.writeTypedList(mVirtualSensorConfigs); 431 dest.writeStrongBinder( 432 mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null); 433 dest.writeInt(mAudioPlaybackSessionId); 434 dest.writeInt(mAudioRecordingSessionId); 435 } 436 437 @Override equals(Object o)438 public boolean equals(Object o) { 439 if (this == o) { 440 return true; 441 } 442 if (!(o instanceof VirtualDeviceParams)) { 443 return false; 444 } 445 VirtualDeviceParams that = (VirtualDeviceParams) o; 446 final int devicePoliciesCount = mDevicePolicies.size(); 447 if (devicePoliciesCount != that.mDevicePolicies.size()) { 448 return false; 449 } 450 for (int i = 0; i < devicePoliciesCount; i++) { 451 if (mDevicePolicies.keyAt(i) != that.mDevicePolicies.keyAt(i)) { 452 return false; 453 } 454 if (mDevicePolicies.valueAt(i) != that.mDevicePolicies.valueAt(i)) { 455 return false; 456 } 457 } 458 return mLockState == that.mLockState 459 && mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts) 460 && Objects.equals(mAllowedCrossTaskNavigations, that.mAllowedCrossTaskNavigations) 461 && Objects.equals(mBlockedCrossTaskNavigations, that.mBlockedCrossTaskNavigations) 462 && mDefaultNavigationPolicy == that.mDefaultNavigationPolicy 463 && Objects.equals(mAllowedActivities, that.mAllowedActivities) 464 && Objects.equals(mBlockedActivities, that.mBlockedActivities) 465 && mDefaultActivityPolicy == that.mDefaultActivityPolicy 466 && Objects.equals(mName, that.mName) 467 && mAudioPlaybackSessionId == that.mAudioPlaybackSessionId 468 && mAudioRecordingSessionId == that.mAudioRecordingSessionId; 469 } 470 471 @Override hashCode()472 public int hashCode() { 473 int hashCode = Objects.hash( 474 mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations, 475 mBlockedCrossTaskNavigations, mDefaultNavigationPolicy, mAllowedActivities, 476 mBlockedActivities, mDefaultActivityPolicy, mName, mDevicePolicies, 477 mAudioPlaybackSessionId, mAudioRecordingSessionId); 478 for (int i = 0; i < mDevicePolicies.size(); i++) { 479 hashCode = 31 * hashCode + mDevicePolicies.keyAt(i); 480 hashCode = 31 * hashCode + mDevicePolicies.valueAt(i); 481 } 482 return hashCode; 483 } 484 485 @Override 486 @NonNull toString()487 public String toString() { 488 return "VirtualDeviceParams(" 489 + " mLockState=" + mLockState 490 + " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts 491 + " mAllowedCrossTaskNavigations=" + mAllowedCrossTaskNavigations 492 + " mBlockedCrossTaskNavigations=" + mBlockedCrossTaskNavigations 493 + " mDefaultNavigationPolicy=" + mDefaultNavigationPolicy 494 + " mAllowedActivities=" + mAllowedActivities 495 + " mBlockedActivities=" + mBlockedActivities 496 + " mDefaultActivityPolicy=" + mDefaultActivityPolicy 497 + " mName=" + mName 498 + " mDevicePolicies=" + mDevicePolicies 499 + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId 500 + " mAudioRecordingSessionId=" + mAudioRecordingSessionId 501 + ")"; 502 } 503 504 @NonNull 505 public static final Parcelable.Creator<VirtualDeviceParams> CREATOR = 506 new Parcelable.Creator<VirtualDeviceParams>() { 507 public VirtualDeviceParams createFromParcel(Parcel in) { 508 return new VirtualDeviceParams(in); 509 } 510 511 public VirtualDeviceParams[] newArray(int size) { 512 return new VirtualDeviceParams[size]; 513 } 514 }; 515 516 /** 517 * Builder for {@link VirtualDeviceParams}. 518 */ 519 public static final class Builder { 520 521 private @LockState int mLockState = LOCK_STATE_DEFAULT; 522 @NonNull private Set<UserHandle> mUsersWithMatchingAccounts = Collections.emptySet(); 523 @NonNull private Set<ComponentName> mAllowedCrossTaskNavigations = Collections.emptySet(); 524 @NonNull private Set<ComponentName> mBlockedCrossTaskNavigations = Collections.emptySet(); 525 @NavigationPolicy 526 private int mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_ALLOWED; 527 private boolean mDefaultNavigationPolicyConfigured = false; 528 @NonNull private Set<ComponentName> mBlockedActivities = Collections.emptySet(); 529 @NonNull private Set<ComponentName> mAllowedActivities = Collections.emptySet(); 530 @ActivityPolicy 531 private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; 532 private boolean mDefaultActivityPolicyConfigured = false; 533 @Nullable private String mName; 534 @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray(); 535 private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE; 536 private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE; 537 538 @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>(); 539 @Nullable private Executor mVirtualSensorCallbackExecutor; 540 @Nullable private VirtualSensorCallback mVirtualSensorCallback; 541 @Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor; 542 @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback; 543 544 private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { 545 @NonNull 546 private final Executor mExecutor; 547 @NonNull 548 private final VirtualSensorCallback mCallback; 549 @Nullable 550 private final Executor mDirectChannelExecutor; 551 @Nullable 552 private final VirtualSensorDirectChannelCallback mDirectChannelCallback; 553 VirtualSensorCallbackDelegate(@onNull @allbackExecutor Executor executor, @NonNull VirtualSensorCallback callback, @Nullable @CallbackExecutor Executor directChannelExecutor, @Nullable VirtualSensorDirectChannelCallback directChannelCallback)554 VirtualSensorCallbackDelegate(@NonNull @CallbackExecutor Executor executor, 555 @NonNull VirtualSensorCallback callback, 556 @Nullable @CallbackExecutor Executor directChannelExecutor, 557 @Nullable VirtualSensorDirectChannelCallback directChannelCallback) { 558 mExecutor = executor; 559 mCallback = callback; 560 mDirectChannelExecutor = directChannelExecutor; 561 mDirectChannelCallback = directChannelCallback; 562 } 563 564 @Override onConfigurationChanged(@onNull VirtualSensor sensor, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros)565 public void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, 566 int samplingPeriodMicros, int batchReportLatencyMicros) { 567 final Duration samplingPeriod = 568 Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros)); 569 final Duration batchReportingLatency = 570 Duration.ofNanos(MICROSECONDS.toNanos(batchReportLatencyMicros)); 571 mExecutor.execute(() -> mCallback.onConfigurationChanged( 572 sensor, enabled, samplingPeriod, batchReportingLatency)); 573 } 574 575 @Override onDirectChannelCreated(int channelHandle, @NonNull SharedMemory sharedMemory)576 public void onDirectChannelCreated(int channelHandle, 577 @NonNull SharedMemory sharedMemory) { 578 if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { 579 mDirectChannelExecutor.execute( 580 () -> mDirectChannelCallback.onDirectChannelCreated(channelHandle, 581 sharedMemory)); 582 } 583 } 584 585 @Override onDirectChannelDestroyed(int channelHandle)586 public void onDirectChannelDestroyed(int channelHandle) { 587 if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { 588 mDirectChannelExecutor.execute( 589 () -> mDirectChannelCallback.onDirectChannelDestroyed(channelHandle)); 590 } 591 } 592 593 @Override onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor, int rateLevel, int reportToken)594 public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor, 595 int rateLevel, int reportToken) { 596 if (mDirectChannelCallback != null && mDirectChannelExecutor != null) { 597 mDirectChannelExecutor.execute( 598 () -> mDirectChannelCallback.onDirectChannelConfigured( 599 channelHandle, sensor, rateLevel, reportToken)); 600 } 601 } 602 } 603 604 /** 605 * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY} 606 * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}. 607 * The default is {@link #LOCK_STATE_DEFAULT}. 608 * 609 * @param lockState The lock state, either {@link #LOCK_STATE_DEFAULT} or 610 * {@link #LOCK_STATE_ALWAYS_UNLOCKED}. 611 */ 612 @RequiresPermission(value = ADD_ALWAYS_UNLOCKED_DISPLAY, conditional = true) 613 @NonNull setLockState(@ockState int lockState)614 public Builder setLockState(@LockState int lockState) { 615 mLockState = lockState; 616 return this; 617 } 618 619 /** 620 * Sets the user handles with matching managed accounts on the remote device to which 621 * this virtual device is streaming. The caller is responsible for verifying the presence 622 * and legitimacy of a matching managed account on the remote device. 623 * 624 * <p>If the app streaming policy is 625 * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY 626 * NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY}, activities not in 627 * {@code usersWithMatchingAccounts} will be blocked from starting. 628 * 629 * <p> If {@code usersWithMatchingAccounts} is empty (the default), streaming is allowed 630 * only if there is no device policy, or if the nearby streaming policy is 631 * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_ENABLED 632 * NEARBY_STREAMING_ENABLED}. 633 * 634 * @param usersWithMatchingAccounts A set of user handles with matching managed 635 * accounts on the remote device this is streaming to. 636 * 637 * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY 638 */ 639 @NonNull setUsersWithMatchingAccounts( @onNull Set<UserHandle> usersWithMatchingAccounts)640 public Builder setUsersWithMatchingAccounts( 641 @NonNull Set<UserHandle> usersWithMatchingAccounts) { 642 mUsersWithMatchingAccounts = Objects.requireNonNull(usersWithMatchingAccounts); 643 return this; 644 } 645 646 /** 647 * Sets the tasks allowed to navigate from current task in the virtual device. Tasks 648 * not in {@code allowedCrossTaskNavigations} will be blocked from navigating to a new 649 * task. Calling this method will cause {@link #getDefaultNavigationPolicy()} to be 650 * {@link #NAVIGATION_POLICY_DEFAULT_BLOCKED}, meaning tasks not in 651 * {@code allowedCrossTaskNavigations} will be blocked from navigating here. 652 * 653 * <p>This method must not be called if {@link #setBlockedCrossTaskNavigations(Set)} has 654 * been called. 655 * 656 * @throws IllegalArgumentException if {@link #setBlockedCrossTaskNavigations(Set)} has been 657 * called. 658 * 659 * @param allowedCrossTaskNavigations A set of tasks {@link ComponentName} allowed to 660 * navigate to new tasks in the virtual device. 661 */ 662 @NonNull setAllowedCrossTaskNavigations( @onNull Set<ComponentName> allowedCrossTaskNavigations)663 public Builder setAllowedCrossTaskNavigations( 664 @NonNull Set<ComponentName> allowedCrossTaskNavigations) { 665 if (mDefaultNavigationPolicyConfigured 666 && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_BLOCKED) { 667 throw new IllegalArgumentException( 668 "Allowed cross task navigation and blocked task navigation cannot " 669 + " both be set."); 670 } 671 mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_BLOCKED; 672 mDefaultNavigationPolicyConfigured = true; 673 mAllowedCrossTaskNavigations = Objects.requireNonNull(allowedCrossTaskNavigations); 674 return this; 675 } 676 677 /** 678 * Sets the tasks blocked from navigating from current task in the virtual device. 679 * Tasks are allowed to navigate unless they are in 680 * {@code blockedCrossTaskNavigations}. Calling this method will cause 681 * {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch 682 * unless they are in {@code blockedCrossTaskNavigations}. 683 * 684 * <p>This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has 685 * been called. 686 * 687 * @throws IllegalArgumentException if {@link #setAllowedCrossTaskNavigations(Set)} has 688 * been called. 689 * 690 * @param blockedCrossTaskNavigations A set of tasks {@link ComponentName} to be 691 * blocked from navigating to new tasks in the virtual device. 692 */ 693 @NonNull setBlockedCrossTaskNavigations( @onNull Set<ComponentName> blockedCrossTaskNavigations)694 public Builder setBlockedCrossTaskNavigations( 695 @NonNull Set<ComponentName> blockedCrossTaskNavigations) { 696 if (mDefaultNavigationPolicyConfigured 697 && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_ALLOWED) { 698 throw new IllegalArgumentException( 699 "Allowed cross task navigation and blocked task navigation cannot " 700 + " be set."); 701 } 702 mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_ALLOWED; 703 mDefaultNavigationPolicyConfigured = true; 704 mBlockedCrossTaskNavigations = Objects.requireNonNull(blockedCrossTaskNavigations); 705 return this; 706 } 707 708 /** 709 * Sets the activities allowed to be launched in the virtual device. Calling this method 710 * will cause {@link #getDefaultActivityPolicy()} to be 711 * {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED}, meaning activities not in 712 * {@code allowedActivities} will be blocked from launching here. 713 * 714 * <p>This method must not be called if {@link #setBlockedActivities(Set)} has been called. 715 * 716 * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been called. 717 * 718 * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched 719 * in the virtual device. 720 */ 721 @NonNull setAllowedActivities(@onNull Set<ComponentName> allowedActivities)722 public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) { 723 if (mDefaultActivityPolicyConfigured 724 && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_BLOCKED) { 725 throw new IllegalArgumentException( 726 "Allowed activities and Blocked activities cannot both be set."); 727 } 728 mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_BLOCKED; 729 mDefaultActivityPolicyConfigured = true; 730 mAllowedActivities = Objects.requireNonNull(allowedActivities); 731 return this; 732 } 733 734 /** 735 * Sets the activities blocked from launching in the virtual device. Calling this method 736 * will cause {@link #getDefaultActivityPolicy()} to be 737 * {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch here 738 * unless they are in {@code blockedActivities}. 739 * 740 * <p>This method must not be called if {@link #setAllowedActivities(Set)} has been called. 741 * 742 * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been called. 743 * 744 * @param blockedActivities A set of {@link ComponentName} to be blocked launching from 745 * virtual device. 746 */ 747 @NonNull setBlockedActivities(@onNull Set<ComponentName> blockedActivities)748 public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) { 749 if (mDefaultActivityPolicyConfigured 750 && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_ALLOWED) { 751 throw new IllegalArgumentException( 752 "Allowed activities and Blocked activities cannot both be set."); 753 } 754 mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; 755 mDefaultActivityPolicyConfigured = true; 756 mBlockedActivities = Objects.requireNonNull(blockedActivities); 757 return this; 758 } 759 760 /** 761 * Sets the optional virtual device name. 762 * 763 * <p>This string is not typically intended to be displayed to end users, but rather for 764 * debugging and other developer-facing purposes. 765 * 766 * <p>3rd party applications may be able to see the name (i.e. it's not private to the 767 * device owner) 768 */ 769 @NonNull setName(@onNull String name)770 public Builder setName(@NonNull String name) { 771 mName = name; 772 return this; 773 } 774 775 /** 776 * Specifies a policy for this virtual device. 777 * 778 * <p>Policies define the system behavior that may be specific for this virtual device. A 779 * policy can be defined for each {@code PolicyType}, but they are all optional. 780 * 781 * @param policyType the type of policy, i.e. which behavior to specify a policy for. 782 * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior. 783 */ 784 @NonNull setDevicePolicy(@olicyType int policyType, @DevicePolicy int devicePolicy)785 public Builder setDevicePolicy(@PolicyType int policyType, @DevicePolicy int devicePolicy) { 786 mDevicePolicies.put(policyType, devicePolicy); 787 return this; 788 } 789 790 /** 791 * Adds a configuration for a sensor that should be created for this virtual device. 792 * 793 * <p>Device sensors must remain valid for the entire lifetime of the device, hence they are 794 * created together with the device itself, and removed when the device is removed. 795 * 796 * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_SENSORS}. 797 * 798 * @see android.companion.virtual.sensor.VirtualSensor 799 * @see #setDevicePolicy 800 */ 801 @NonNull addVirtualSensorConfig(@onNull VirtualSensorConfig virtualSensorConfig)802 public Builder addVirtualSensorConfig(@NonNull VirtualSensorConfig virtualSensorConfig) { 803 mVirtualSensorConfigs.add(Objects.requireNonNull(virtualSensorConfig)); 804 return this; 805 } 806 807 /** 808 * Sets the callback to get notified about changes in the sensor configuration. 809 * 810 * @param executor The executor where the callback is executed on. 811 * @param callback The callback to get notified when the state of the sensor 812 * configuration has changed, see {@link VirtualSensorCallback} 813 */ 814 @SuppressLint("MissingGetterMatchingBuilder") 815 @NonNull setVirtualSensorCallback( @onNull @allbackExecutor Executor executor, @NonNull VirtualSensorCallback callback)816 public Builder setVirtualSensorCallback( 817 @NonNull @CallbackExecutor Executor executor, 818 @NonNull VirtualSensorCallback callback) { 819 mVirtualSensorCallbackExecutor = Objects.requireNonNull(executor); 820 mVirtualSensorCallback = Objects.requireNonNull(callback); 821 return this; 822 } 823 824 /** 825 * Sets the callback to get notified about changes in 826 * {@link android.hardware.SensorDirectChannel} configuration. 827 * 828 * @param executor The executor where the callback is executed on. 829 * @param callback The callback to get notified when the state of the sensor 830 * configuration has changed, see {@link VirtualSensorDirectChannelCallback} 831 */ 832 @SuppressLint("MissingGetterMatchingBuilder") 833 @NonNull setVirtualSensorDirectChannelCallback( @onNull @allbackExecutor Executor executor, @NonNull VirtualSensorDirectChannelCallback callback)834 public Builder setVirtualSensorDirectChannelCallback( 835 @NonNull @CallbackExecutor Executor executor, 836 @NonNull VirtualSensorDirectChannelCallback callback) { 837 mVirtualSensorDirectChannelCallbackExecutor = Objects.requireNonNull(executor); 838 mVirtualSensorDirectChannelCallback = Objects.requireNonNull(callback); 839 return this; 840 } 841 842 /** 843 * Sets audio playback session id specific for this virtual device. 844 * 845 * <p>Audio players constructed within context associated with this virtual device 846 * will be automatically assigned provided session id. 847 * 848 * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO}, 849 * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if 850 * the playback session id is set to value other than 851 * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}. 852 * 853 * @param playbackSessionId requested device-specific audio session id for playback 854 * @see android.media.AudioManager#generateAudioSessionId() 855 * @see android.media.AudioTrack.Builder#setContext(Context) 856 */ 857 @NonNull setAudioPlaybackSessionId(int playbackSessionId)858 public Builder setAudioPlaybackSessionId(int playbackSessionId) { 859 if (playbackSessionId < 0) { 860 throw new IllegalArgumentException("Invalid playback audio session id"); 861 } 862 mAudioPlaybackSessionId = playbackSessionId; 863 return this; 864 } 865 866 /** 867 * Sets audio recording session id specific for this virtual device. 868 * 869 * <p>{@link android.media.AudioRecord} constructed within context associated with this 870 * virtual device will be automatically assigned provided session id. 871 * 872 * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO}, 873 * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if 874 * the recording session id is set to value other than 875 * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}. 876 * 877 * @param recordingSessionId requested device-specific audio session id for playback 878 * @see android.media.AudioManager#generateAudioSessionId() 879 * @see android.media.AudioRecord.Builder#setContext(Context) 880 */ 881 @NonNull setAudioRecordingSessionId(int recordingSessionId)882 public Builder setAudioRecordingSessionId(int recordingSessionId) { 883 if (recordingSessionId < 0) { 884 throw new IllegalArgumentException("Invalid recording audio session id"); 885 } 886 mAudioRecordingSessionId = recordingSessionId; 887 return this; 888 } 889 890 /** 891 * Builds the {@link VirtualDeviceParams} instance. 892 * 893 * @throws IllegalArgumentException if there's mismatch between policy definition and 894 * the passed parameters or if there are sensor configs with the same type and name. 895 * 896 */ 897 @NonNull build()898 public VirtualDeviceParams build() { 899 VirtualSensorCallbackDelegate virtualSensorCallbackDelegate = null; 900 if (!mVirtualSensorConfigs.isEmpty()) { 901 if (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT) 902 != DEVICE_POLICY_CUSTOM) { 903 throw new IllegalArgumentException( 904 "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating " 905 + "virtual sensors."); 906 } 907 if (mVirtualSensorCallback == null) { 908 throw new IllegalArgumentException( 909 "VirtualSensorCallback is required for creating virtual sensors."); 910 } 911 912 for (int i = 0; i < mVirtualSensorConfigs.size(); ++i) { 913 if (mVirtualSensorConfigs.get(i).getDirectChannelTypesSupported() > 0) { 914 if (mVirtualSensorDirectChannelCallback == null) { 915 throw new IllegalArgumentException( 916 "VirtualSensorDirectChannelCallback is required for creating " 917 + "virtual sensors that support direct channel."); 918 } 919 break; 920 } 921 } 922 virtualSensorCallbackDelegate = new VirtualSensorCallbackDelegate( 923 mVirtualSensorCallbackExecutor, 924 mVirtualSensorCallback, 925 mVirtualSensorDirectChannelCallbackExecutor, 926 mVirtualSensorDirectChannelCallback); 927 } 928 929 if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE 930 || mAudioRecordingSessionId != AUDIO_SESSION_ID_GENERATE) 931 && mDevicePolicies.get(POLICY_TYPE_AUDIO, DEVICE_POLICY_DEFAULT) 932 != DEVICE_POLICY_CUSTOM) { 933 throw new IllegalArgumentException("DEVICE_POLICY_CUSTOM for POLICY_TYPE_AUDIO is " 934 + "required for configuration of device-specific audio session ids."); 935 } 936 937 SparseArray<Set<String>> sensorNameByType = new SparseArray(); 938 for (int i = 0; i < mVirtualSensorConfigs.size(); ++i) { 939 VirtualSensorConfig config = mVirtualSensorConfigs.get(i); 940 Set<String> sensorNames = sensorNameByType.get(config.getType(), new ArraySet<>()); 941 if (!sensorNames.add(config.getName())) { 942 throw new IllegalArgumentException( 943 "Sensor names must be unique for a particular sensor type."); 944 } 945 sensorNameByType.put(config.getType(), sensorNames); 946 } 947 948 return new VirtualDeviceParams( 949 mLockState, 950 mUsersWithMatchingAccounts, 951 mAllowedCrossTaskNavigations, 952 mBlockedCrossTaskNavigations, 953 mDefaultNavigationPolicy, 954 mAllowedActivities, 955 mBlockedActivities, 956 mDefaultActivityPolicy, 957 mName, 958 mDevicePolicies, 959 mVirtualSensorConfigs, 960 virtualSensorCallbackDelegate, 961 mAudioPlaybackSessionId, 962 mAudioRecordingSessionId); 963 } 964 } 965 } 966