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 com.android.server.companion.virtual; 18 19 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; 20 import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE; 21 22 import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SuppressLint; 28 import android.app.ActivityOptions; 29 import android.companion.AssociationInfo; 30 import android.companion.AssociationRequest; 31 import android.companion.CompanionDeviceManager; 32 import android.companion.virtual.IVirtualDevice; 33 import android.companion.virtual.IVirtualDeviceActivityListener; 34 import android.companion.virtual.IVirtualDeviceListener; 35 import android.companion.virtual.IVirtualDeviceManager; 36 import android.companion.virtual.IVirtualDeviceSoundEffectListener; 37 import android.companion.virtual.VirtualDevice; 38 import android.companion.virtual.VirtualDeviceManager; 39 import android.companion.virtual.VirtualDeviceParams; 40 import android.companion.virtual.flags.Flags; 41 import android.companion.virtual.sensor.VirtualSensor; 42 import android.companion.virtualnative.IVirtualDeviceManagerNative; 43 import android.content.AttributionSource; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.hardware.display.DisplayManagerInternal; 47 import android.hardware.display.IVirtualDisplayCallback; 48 import android.hardware.display.VirtualDisplayConfig; 49 import android.os.Binder; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.LocaleList; 53 import android.os.Looper; 54 import android.os.Parcel; 55 import android.os.Process; 56 import android.os.RemoteCallbackList; 57 import android.os.RemoteException; 58 import android.os.UserHandle; 59 import android.util.ArrayMap; 60 import android.util.ArraySet; 61 import android.util.ExceptionUtils; 62 import android.util.Slog; 63 import android.util.SparseArray; 64 import android.view.Display; 65 import android.widget.Toast; 66 67 import com.android.internal.R; 68 import com.android.internal.annotations.GuardedBy; 69 import com.android.internal.annotations.VisibleForTesting; 70 import com.android.internal.util.DumpUtils; 71 import com.android.modules.expresslog.Counter; 72 import com.android.server.LocalServices; 73 import com.android.server.SystemService; 74 import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline; 75 import com.android.server.wm.ActivityInterceptorCallback; 76 import com.android.server.wm.ActivityTaskManagerInternal; 77 78 import java.io.FileDescriptor; 79 import java.io.PrintWriter; 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.HashSet; 83 import java.util.List; 84 import java.util.Objects; 85 import java.util.Set; 86 import java.util.concurrent.ConcurrentHashMap; 87 import java.util.concurrent.atomic.AtomicInteger; 88 import java.util.function.Consumer; 89 import java.util.stream.Collectors; 90 91 92 @SuppressLint("LongLogTag") 93 public class VirtualDeviceManagerService extends SystemService { 94 95 private static final String TAG = "VirtualDeviceManagerService"; 96 97 private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native"; 98 99 private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList( 100 AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, 101 AssociationRequest.DEVICE_PROFILE_APP_STREAMING, 102 AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING); 103 104 private final Object mVirtualDeviceManagerLock = new Object(); 105 private final VirtualDeviceManagerImpl mImpl; 106 private final VirtualDeviceManagerNativeImpl mNativeImpl; 107 private final VirtualDeviceManagerInternal mLocalService; 108 private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext()); 109 private final Handler mHandler = new Handler(Looper.getMainLooper()); 110 private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler); 111 112 private static AtomicInteger sNextUniqueIndex = new AtomicInteger( 113 Context.DEVICE_ID_DEFAULT + 1); 114 115 @GuardedBy("mVirtualDeviceManagerLock") 116 private ArrayMap<String, AssociationInfo> mActiveAssociations = new ArrayMap<>(); 117 118 private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener = 119 new CompanionDeviceManager.OnAssociationsChangedListener() { 120 @Override 121 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 122 public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) { 123 syncVirtualDevicesToCdmAssociations(associations); 124 } 125 }; 126 127 private final RemoteCallbackList<IVirtualDeviceListener> mVirtualDeviceListeners = 128 new RemoteCallbackList<>(); 129 130 /** 131 * Mapping from device IDs to virtual devices. 132 */ 133 @GuardedBy("mVirtualDeviceManagerLock") 134 private final SparseArray<VirtualDeviceImpl> mVirtualDevices = new SparseArray<>(); 135 136 /** 137 * Mapping from device IDs to app UIDs running on the corresponding virtual device. 138 */ 139 @GuardedBy("mVirtualDeviceManagerLock") 140 private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>(); 141 VirtualDeviceManagerService(Context context)142 public VirtualDeviceManagerService(Context context) { 143 super(context); 144 mImpl = new VirtualDeviceManagerImpl(); 145 mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null; 146 mLocalService = new LocalService(); 147 } 148 149 private final ActivityInterceptorCallback mActivityInterceptorCallback = 150 new ActivityInterceptorCallback() { 151 152 @Nullable 153 @Override 154 public ActivityInterceptResult onInterceptActivityLaunch(@NonNull 155 ActivityInterceptorInfo info) { 156 if (info.getCallingPackage() == null) { 157 return null; 158 } 159 PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage()); 160 if (pt == null) { 161 return null; 162 } 163 pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); 164 ActivityOptions options = info.getCheckedOptions(); 165 if (options == null) { 166 options = ActivityOptions.makeBasic(); 167 } 168 return new ActivityInterceptResult( 169 info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId)); 170 } 171 }; 172 173 @Override 174 @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) onStart()175 public void onStart() { 176 publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl); 177 if (Flags.enableNativeVdm()) { 178 publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl); 179 } 180 publishLocalService(VirtualDeviceManagerInternal.class, mLocalService); 181 ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService( 182 ActivityTaskManagerInternal.class); 183 activityTaskManagerInternal.registerActivityStartInterceptor( 184 VIRTUAL_DEVICE_SERVICE_ORDERED_ID, 185 mActivityInterceptorCallback); 186 187 if (Flags.persistentDeviceIdApi()) { 188 CompanionDeviceManager cdm = 189 getContext().getSystemService(CompanionDeviceManager.class); 190 if (cdm != null) { 191 onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL)); 192 cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(), 193 this::onCdmAssociationsChanged, UserHandle.USER_ALL); 194 } else { 195 Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info " 196 + " will be available."); 197 } 198 } 199 } 200 onCameraAccessBlocked(int appUid)201 void onCameraAccessBlocked(int appUid) { 202 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 203 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 204 VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i); 205 virtualDevice.showToastWhereUidIsRunning(appUid, 206 getContext().getString( 207 R.string.vdm_camera_access_denied, 208 virtualDevice.getDisplayName()), 209 Toast.LENGTH_LONG, Looper.myLooper()); 210 } 211 } 212 getCameraAccessController(UserHandle userHandle)213 CameraAccessController getCameraAccessController(UserHandle userHandle) { 214 if (Flags.streamCamera()) { 215 return null; 216 } 217 int userId = userHandle.getIdentifier(); 218 synchronized (mVirtualDeviceManagerLock) { 219 for (int i = 0; i < mVirtualDevices.size(); i++) { 220 final CameraAccessController cameraAccessController = 221 mVirtualDevices.valueAt(i).getCameraAccessController(); 222 if (cameraAccessController.getUserId() == userId) { 223 return cameraAccessController; 224 } 225 } 226 } 227 Context userContext = getContext().createContextAsUser(userHandle, 0); 228 return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked); 229 } 230 231 @VisibleForTesting getLocalServiceInstance()232 VirtualDeviceManagerInternal getLocalServiceInstance() { 233 return mLocalService; 234 } 235 236 @VisibleForTesting notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids)237 void notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids) { 238 synchronized (mVirtualDeviceManagerLock) { 239 if (!mVirtualDevices.contains(deviceId)) { 240 Slog.e(TAG, "notifyRunningAppsChanged called for unknown deviceId:" + deviceId 241 + " (maybe it was recently closed?)"); 242 return; 243 } 244 mAppsOnVirtualDevices.put(deviceId, uids); 245 } 246 mLocalService.onAppsOnVirtualDeviceChanged(); 247 } 248 249 @VisibleForTesting addVirtualDevice(VirtualDeviceImpl virtualDevice)250 void addVirtualDevice(VirtualDeviceImpl virtualDevice) { 251 synchronized (mVirtualDeviceManagerLock) { 252 mVirtualDevices.put(virtualDevice.getDeviceId(), virtualDevice); 253 } 254 } 255 256 /** 257 * Remove the virtual device. Sends the 258 * {@link VirtualDeviceManager#ACTION_VIRTUAL_DEVICE_REMOVED} broadcast as a result. 259 * 260 * @param deviceId deviceId to be removed 261 * @return {@code true} if the device was removed, {@code false} if the operation was a no-op 262 */ removeVirtualDevice(int deviceId)263 boolean removeVirtualDevice(int deviceId) { 264 synchronized (mVirtualDeviceManagerLock) { 265 if (!mVirtualDevices.contains(deviceId)) { 266 return false; 267 } 268 269 mAppsOnVirtualDevices.remove(deviceId); 270 mVirtualDevices.remove(deviceId); 271 } 272 273 if (Flags.vdmPublicApis()) { 274 mVirtualDeviceListeners.broadcast(listener -> { 275 try { 276 listener.onVirtualDeviceClosed(deviceId); 277 } catch (RemoteException e) { 278 Slog.i(TAG, "Failed to invoke onVirtualDeviceClosed listener: " 279 + e.getMessage()); 280 } 281 }); 282 } 283 284 Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED); 285 i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId); 286 i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 287 final long identity = Binder.clearCallingIdentity(); 288 try { 289 getContext().sendBroadcastAsUser(i, UserHandle.ALL); 290 291 if (!Flags.persistentDeviceIdApi()) { 292 synchronized (mVirtualDeviceManagerLock) { 293 if (mVirtualDevices.size() == 0) { 294 unregisterCdmAssociationListener(); 295 } 296 } 297 } 298 } finally { 299 Binder.restoreCallingIdentity(identity); 300 } 301 return true; 302 } 303 304 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations)305 private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) { 306 Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>(); 307 synchronized (mVirtualDeviceManagerLock) { 308 if (mVirtualDevices.size() == 0) { 309 return; 310 } 311 312 Set<Integer> activeAssociationIds = new HashSet<>(associations.size()); 313 for (AssociationInfo association : associations) { 314 activeAssociationIds.add(association.getId()); 315 } 316 317 for (int i = 0; i < mVirtualDevices.size(); i++) { 318 VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); 319 if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) { 320 virtualDevicesToRemove.add(virtualDevice); 321 } 322 } 323 } 324 325 for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) { 326 virtualDevice.close(); 327 } 328 } 329 330 @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) registerCdmAssociationListener()331 private void registerCdmAssociationListener() { 332 final CompanionDeviceManager cdm = getContext().getSystemService( 333 CompanionDeviceManager.class); 334 cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(), 335 mCdmAssociationListener); 336 } 337 338 @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) unregisterCdmAssociationListener()339 private void unregisterCdmAssociationListener() { 340 final CompanionDeviceManager cdm = getContext().getSystemService( 341 CompanionDeviceManager.class); 342 cdm.removeOnAssociationsChangedListener(mCdmAssociationListener); 343 } 344 345 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) onCdmAssociationsChanged(List<AssociationInfo> associations)346 void onCdmAssociationsChanged(List<AssociationInfo> associations) { 347 ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>(); 348 for (int i = 0; i < associations.size(); ++i) { 349 AssociationInfo association = associations.get(i); 350 if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(association.getDeviceProfile()) 351 && !association.isRevoked()) { 352 String persistentId = 353 VirtualDeviceImpl.createPersistentDeviceId(association.getId()); 354 vdmAssociations.put(persistentId, association); 355 } 356 } 357 Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>(); 358 Set<String> removedPersistentDeviceIds; 359 synchronized (mVirtualDeviceManagerLock) { 360 removedPersistentDeviceIds = mActiveAssociations.keySet(); 361 removedPersistentDeviceIds.removeAll(vdmAssociations.keySet()); 362 mActiveAssociations = vdmAssociations; 363 364 for (int i = 0; i < mVirtualDevices.size(); i++) { 365 VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); 366 if (removedPersistentDeviceIds.contains(virtualDevice.getPersistentDeviceId())) { 367 virtualDevicesToRemove.add(virtualDevice); 368 } 369 } 370 } 371 372 for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) { 373 virtualDevice.close(); 374 } 375 376 if (!removedPersistentDeviceIds.isEmpty()) { 377 mLocalService.onPersistentDeviceIdsRemoved(removedPersistentDeviceIds); 378 } 379 } 380 getVirtualDevicesSnapshot()381 private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() { 382 synchronized (mVirtualDeviceManagerLock) { 383 ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size()); 384 for (int i = 0; i < mVirtualDevices.size(); i++) { 385 virtualDevices.add(mVirtualDevices.valueAt(i)); 386 } 387 return virtualDevices; 388 } 389 } 390 391 class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { 392 393 private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = 394 new VirtualDeviceImpl.PendingTrampolineCallback() { 395 @Override 396 public void startWaitingForPendingTrampoline( 397 PendingTrampoline pendingTrampoline) { 398 PendingTrampoline existing = mPendingTrampolines.put( 399 pendingTrampoline.mPendingIntent.getCreatorPackage(), 400 pendingTrampoline); 401 if (existing != null) { 402 existing.mResultReceiver.send( 403 VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); 404 } 405 } 406 407 @Override 408 public void stopWaitingForPendingTrampoline( 409 PendingTrampoline pendingTrampoline) { 410 mPendingTrampolines.remove( 411 pendingTrampoline.mPendingIntent.getCreatorPackage()); 412 } 413 }; 414 415 @android.annotation.EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 416 @Override // Binder call createVirtualDevice( IBinder token, AttributionSource attributionSource, int associationId, @NonNull VirtualDeviceParams params, @NonNull IVirtualDeviceActivityListener activityListener, @NonNull IVirtualDeviceSoundEffectListener soundEffectListener)417 public IVirtualDevice createVirtualDevice( 418 IBinder token, 419 AttributionSource attributionSource, 420 int associationId, 421 @NonNull VirtualDeviceParams params, 422 @NonNull IVirtualDeviceActivityListener activityListener, 423 @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { 424 createVirtualDevice_enforcePermission(); 425 attributionSource.enforceCallingUid(); 426 427 final int callingUid = getCallingUid(); 428 final String packageName = attributionSource.getPackageName(); 429 if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { 430 throw new SecurityException( 431 "Package name " + packageName + " does not belong to calling uid " 432 + callingUid); 433 } 434 AssociationInfo associationInfo = getAssociationInfo(packageName, associationId); 435 if (associationInfo == null) { 436 throw new IllegalArgumentException("No association with ID " + associationId); 437 } 438 if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES 439 .contains(associationInfo.getDeviceProfile()) 440 && Flags.persistentDeviceIdApi()) { 441 throw new IllegalArgumentException("Unsupported CDM Association device profile " 442 + associationInfo.getDeviceProfile() + " for virtual device creation."); 443 } 444 Objects.requireNonNull(params); 445 Objects.requireNonNull(activityListener); 446 Objects.requireNonNull(soundEffectListener); 447 448 final UserHandle userHandle = getCallingUserHandle(); 449 final CameraAccessController cameraAccessController = 450 getCameraAccessController(userHandle); 451 final int deviceId = sNextUniqueIndex.getAndIncrement(); 452 final Consumer<ArraySet<Integer>> runningAppsChangedCallback = 453 runningUids -> notifyRunningAppsChanged(deviceId, runningUids); 454 VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo, 455 VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource, 456 deviceId, 457 cameraAccessController, mPendingTrampolineCallback, activityListener, 458 soundEffectListener, runningAppsChangedCallback, params); 459 if (Flags.expressMetrics()) { 460 Counter.logIncrement("virtual_devices.value_virtual_devices_created_count"); 461 } 462 463 synchronized (mVirtualDeviceManagerLock) { 464 if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) { 465 final long callingId = Binder.clearCallingIdentity(); 466 try { 467 registerCdmAssociationListener(); 468 } finally { 469 Binder.restoreCallingIdentity(callingId); 470 } 471 } 472 mVirtualDevices.put(deviceId, virtualDevice); 473 } 474 475 if (Flags.vdmPublicApis()) { 476 mVirtualDeviceListeners.broadcast(listener -> { 477 try { 478 listener.onVirtualDeviceCreated(deviceId); 479 } catch (RemoteException e) { 480 Slog.i(TAG, "Failed to invoke onVirtualDeviceCreated listener: " 481 + e.getMessage()); 482 } 483 }); 484 } 485 if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { 486 Counter.logIncrementWithUid( 487 "virtual_devices.value_virtual_devices_created_with_uid_count", 488 attributionSource.getUid()); 489 } 490 return virtualDevice; 491 } 492 493 @Override // Binder call createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)494 public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, 495 IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName) 496 throws RemoteException { 497 Objects.requireNonNull(virtualDisplayConfig); 498 final int callingUid = getCallingUid(); 499 if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { 500 throw new SecurityException( 501 "Package name " + packageName + " does not belong to calling uid " 502 + callingUid); 503 } 504 VirtualDeviceImpl virtualDeviceImpl; 505 synchronized (mVirtualDeviceManagerLock) { 506 virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId()); 507 if (virtualDeviceImpl == null) { 508 throw new SecurityException( 509 "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId() 510 + ")"); 511 } 512 } 513 if (virtualDeviceImpl.getOwnerUid() != callingUid) { 514 throw new SecurityException( 515 "uid " + callingUid 516 + " is not the owner of the supplied VirtualDevice (deviceId = " 517 + virtualDevice.getDeviceId() + ")"); 518 } 519 520 return virtualDeviceImpl.createVirtualDisplay( 521 virtualDisplayConfig, callback, packageName); 522 } 523 524 @Override // Binder call getVirtualDevices()525 public List<VirtualDevice> getVirtualDevices() { 526 List<VirtualDevice> virtualDevices = new ArrayList<>(); 527 synchronized (mVirtualDeviceManagerLock) { 528 for (int i = 0; i < mVirtualDevices.size(); i++) { 529 final VirtualDeviceImpl device = mVirtualDevices.valueAt(i); 530 virtualDevices.add(device.getPublicVirtualDeviceObject()); 531 } 532 } 533 return virtualDevices; 534 } 535 536 @Override // Binder call getVirtualDevice(int deviceId)537 public VirtualDevice getVirtualDevice(int deviceId) { 538 VirtualDeviceImpl device; 539 synchronized (mVirtualDeviceManagerLock) { 540 device = mVirtualDevices.get(deviceId); 541 } 542 return device == null ? null : device.getPublicVirtualDeviceObject(); 543 } 544 545 @Override // Binder call registerVirtualDeviceListener(IVirtualDeviceListener listener)546 public void registerVirtualDeviceListener(IVirtualDeviceListener listener) { 547 mVirtualDeviceListeners.register(listener); 548 } 549 550 @Override // Binder call unregisterVirtualDeviceListener(IVirtualDeviceListener listener)551 public void unregisterVirtualDeviceListener(IVirtualDeviceListener listener) { 552 mVirtualDeviceListeners.unregister(listener); 553 } 554 555 @Override // BinderCall 556 @VirtualDeviceParams.DevicePolicy getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType)557 public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) { 558 synchronized (mVirtualDeviceManagerLock) { 559 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 560 return virtualDevice != null 561 ? virtualDevice.getDevicePolicy(policyType) : DEVICE_POLICY_DEFAULT; 562 } 563 } 564 565 566 @Override // Binder call getDeviceIdForDisplayId(int displayId)567 public int getDeviceIdForDisplayId(int displayId) { 568 if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) { 569 return Context.DEVICE_ID_DEFAULT; 570 } 571 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 572 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 573 VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i); 574 if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) { 575 return virtualDevice.getDeviceId(); 576 } 577 } 578 return Context.DEVICE_ID_DEFAULT; 579 } 580 581 @Override // Binder call getDisplayNameForPersistentDeviceId( @onNull String persistentDeviceId)582 public @Nullable CharSequence getDisplayNameForPersistentDeviceId( 583 @NonNull String persistentDeviceId) { 584 final AssociationInfo associationInfo; 585 synchronized (mVirtualDeviceManagerLock) { 586 associationInfo = mActiveAssociations.get(persistentDeviceId); 587 } 588 return associationInfo == null ? null : associationInfo.getDisplayName(); 589 } 590 591 @Override // Binder call getAllPersistentDeviceIds()592 public @NonNull List<String> getAllPersistentDeviceIds() { 593 return new ArrayList<>(mLocalService.getAllPersistentDeviceIds()); 594 } 595 596 // Binder call 597 @Override isValidVirtualDeviceId(int deviceId)598 public boolean isValidVirtualDeviceId(int deviceId) { 599 synchronized (mVirtualDeviceManagerLock) { 600 return mVirtualDevices.contains(deviceId); 601 } 602 } 603 604 @Override // Binder call getAudioPlaybackSessionId(int deviceId)605 public int getAudioPlaybackSessionId(int deviceId) { 606 synchronized (mVirtualDeviceManagerLock) { 607 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 608 return virtualDevice != null 609 ? virtualDevice.getAudioPlaybackSessionId() : AUDIO_SESSION_ID_GENERATE; 610 } 611 } 612 613 @Override // Binder call getAudioRecordingSessionId(int deviceId)614 public int getAudioRecordingSessionId(int deviceId) { 615 synchronized (mVirtualDeviceManagerLock) { 616 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 617 return virtualDevice != null 618 ? virtualDevice.getAudioRecordingSessionId() : AUDIO_SESSION_ID_GENERATE; 619 } 620 } 621 622 @Override // Binder call playSoundEffect(int deviceId, int effectType)623 public void playSoundEffect(int deviceId, int effectType) { 624 VirtualDeviceImpl virtualDevice; 625 synchronized (mVirtualDeviceManagerLock) { 626 virtualDevice = mVirtualDevices.get(deviceId); 627 } 628 629 if (virtualDevice != null) { 630 virtualDevice.playSoundEffect(effectType); 631 } 632 } 633 634 @Override // Binder call isVirtualDeviceOwnedMirrorDisplay(int displayId)635 public boolean isVirtualDeviceOwnedMirrorDisplay(int displayId) { 636 if (getDeviceIdForDisplayId(displayId) == Context.DEVICE_ID_DEFAULT) { 637 return false; 638 } 639 640 DisplayManagerInternal displayManager = LocalServices.getService( 641 DisplayManagerInternal.class); 642 return displayManager.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY; 643 } 644 645 @Nullable getAssociationInfo(String packageName, int associationId)646 private AssociationInfo getAssociationInfo(String packageName, int associationId) { 647 final UserHandle userHandle = getCallingUserHandle(); 648 final CompanionDeviceManager cdm = 649 getContext().createContextAsUser(userHandle, 0) 650 .getSystemService(CompanionDeviceManager.class); 651 List<AssociationInfo> associations; 652 final long identity = Binder.clearCallingIdentity(); 653 try { 654 associations = cdm.getAllAssociations(); 655 } finally { 656 Binder.restoreCallingIdentity(identity); 657 } 658 final int callingUserId = userHandle.getIdentifier(); 659 if (associations != null) { 660 final int associationSize = associations.size(); 661 for (int i = 0; i < associationSize; i++) { 662 AssociationInfo associationInfo = associations.get(i); 663 if (associationInfo.belongsToPackage(callingUserId, packageName) 664 && associationId == associationInfo.getId()) { 665 return associationInfo; 666 } 667 } 668 } else { 669 Slog.w(TAG, "No associations for user " + callingUserId); 670 } 671 return null; 672 } 673 674 @Override onTransact(int code, Parcel data, Parcel reply, int flags)675 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 676 throws RemoteException { 677 try { 678 return super.onTransact(code, data, reply, flags); 679 } catch (Throwable e) { 680 Slog.e(TAG, "Error during IPC", e); 681 throw ExceptionUtils.propagate(e, RemoteException.class); 682 } 683 } 684 685 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args)686 public void dump(@NonNull FileDescriptor fd, 687 @NonNull PrintWriter fout, 688 @Nullable String[] args) { 689 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) { 690 return; 691 } 692 fout.println("Created virtual devices: "); 693 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 694 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 695 virtualDevicesSnapshot.get(i).dump(fd, fout, args); 696 } 697 698 mVirtualDeviceLog.dump(fout); 699 } 700 } 701 702 final class VirtualDeviceManagerNativeImpl extends IVirtualDeviceManagerNative.Stub { 703 @Override // Binder call getDeviceIdsForUid(int uid)704 public int[] getDeviceIdsForUid(int uid) { 705 return mLocalService 706 .getDeviceIdsForUid(uid).stream().mapToInt(Integer::intValue).toArray(); 707 } 708 709 @Override // Binder call getDevicePolicy(int deviceId, int policyType)710 public int getDevicePolicy(int deviceId, int policyType) { 711 return mImpl.getDevicePolicy(deviceId, policyType); 712 } 713 } 714 715 private final class LocalService extends VirtualDeviceManagerInternal { 716 @GuardedBy("mVirtualDeviceManagerLock") 717 private final ArrayList<AppsOnVirtualDeviceListener> mAppsOnVirtualDeviceListeners = 718 new ArrayList<>(); 719 @GuardedBy("mVirtualDeviceManagerLock") 720 private final ArrayList<Consumer<String>> mPersistentDeviceIdRemovedListeners = 721 new ArrayList<>(); 722 723 @GuardedBy("mVirtualDeviceManagerLock") 724 private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>(); 725 726 @Override getDeviceOwnerUid(int deviceId)727 public int getDeviceOwnerUid(int deviceId) { 728 VirtualDeviceImpl virtualDevice; 729 synchronized (mVirtualDeviceManagerLock) { 730 virtualDevice = mVirtualDevices.get(deviceId); 731 } 732 return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID; 733 } 734 735 @Override getVirtualSensor(int deviceId, int handle)736 public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) { 737 VirtualDeviceImpl virtualDevice; 738 synchronized (mVirtualDeviceManagerLock) { 739 virtualDevice = mVirtualDevices.get(deviceId); 740 } 741 return virtualDevice != null ? virtualDevice.getVirtualSensorByHandle(handle) : null; 742 } 743 744 @Override getDeviceIdsForUid(int uid)745 public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) { 746 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 747 ArraySet<Integer> result = new ArraySet<>(); 748 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 749 VirtualDeviceImpl device = virtualDevicesSnapshot.get(i); 750 if (device.isAppRunningOnVirtualDevice(uid)) { 751 result.add(device.getDeviceId()); 752 } 753 } 754 return result; 755 } 756 757 @Override onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId)758 public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { 759 VirtualDeviceImpl virtualDeviceImpl; 760 synchronized (mVirtualDeviceManagerLock) { 761 virtualDeviceImpl = mVirtualDevices.get( 762 ((VirtualDeviceImpl) virtualDevice).getDeviceId()); 763 } 764 if (virtualDeviceImpl != null) { 765 virtualDeviceImpl.onVirtualDisplayRemoved(displayId); 766 } 767 } 768 769 @Override onAppsOnVirtualDeviceChanged()770 public void onAppsOnVirtualDeviceChanged() { 771 ArraySet<Integer> latestRunningUids = new ArraySet<>(); 772 final AppsOnVirtualDeviceListener[] listeners; 773 synchronized (mVirtualDeviceManagerLock) { 774 int size = mAppsOnVirtualDevices.size(); 775 for (int i = 0; i < size; i++) { 776 latestRunningUids.addAll(mAppsOnVirtualDevices.valueAt(i)); 777 } 778 if (!mAllUidsOnVirtualDevice.equals(latestRunningUids)) { 779 mAllUidsOnVirtualDevice.clear(); 780 mAllUidsOnVirtualDevice.addAll(latestRunningUids); 781 listeners = 782 mAppsOnVirtualDeviceListeners.toArray( 783 new AppsOnVirtualDeviceListener[0]); 784 } else { 785 listeners = null; 786 } 787 } 788 if (listeners != null) { 789 mHandler.post(() -> { 790 for (AppsOnVirtualDeviceListener listener : listeners) { 791 listener.onAppsOnAnyVirtualDeviceChanged(latestRunningUids); 792 } 793 }); 794 } 795 } 796 797 @Override onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds)798 public void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds) { 799 final List<Consumer<String>> persistentDeviceIdRemovedListeners; 800 synchronized (mVirtualDeviceManagerLock) { 801 persistentDeviceIdRemovedListeners = List.copyOf( 802 mPersistentDeviceIdRemovedListeners); 803 } 804 mHandler.post(() -> { 805 for (String persistentDeviceId : removedPersistentDeviceIds) { 806 for (Consumer<String> listener : persistentDeviceIdRemovedListeners) { 807 listener.accept(persistentDeviceId); 808 } 809 } 810 }); 811 } 812 813 @Override onAuthenticationPrompt(int uid)814 public void onAuthenticationPrompt(int uid) { 815 synchronized (mVirtualDeviceManagerLock) { 816 for (int i = 0; i < mVirtualDevices.size(); i++) { 817 VirtualDeviceImpl device = mVirtualDevices.valueAt(i); 818 device.showToastWhereUidIsRunning(uid, 819 R.string.app_streaming_blocked_message_for_fingerprint_dialog, 820 Toast.LENGTH_LONG, Looper.getMainLooper()); 821 } 822 } 823 } 824 825 @Override getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice)826 public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) { 827 return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags(); 828 } 829 830 @Override 831 @Nullable getPreferredLocaleListForUid(int uid)832 public LocaleList getPreferredLocaleListForUid(int uid) { 833 // TODO: b/263188984 support the case where an app is running on multiple VDs 834 synchronized (mVirtualDeviceManagerLock) { 835 for (int i = 0; i < mAppsOnVirtualDevices.size(); i++) { 836 if (mAppsOnVirtualDevices.valueAt(i).contains(uid)) { 837 int deviceId = mAppsOnVirtualDevices.keyAt(i); 838 return mVirtualDevices.get(deviceId).getDeviceLocaleList(); 839 } 840 } 841 } 842 return null; 843 } 844 845 @Override isAppRunningOnAnyVirtualDevice(int uid)846 public boolean isAppRunningOnAnyVirtualDevice(int uid) { 847 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 848 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 849 if (virtualDevicesSnapshot.get(i).isAppRunningOnVirtualDevice(uid)) { 850 return true; 851 } 852 } 853 return false; 854 } 855 856 @Override isInputDeviceOwnedByVirtualDevice(int inputDeviceId)857 public boolean isInputDeviceOwnedByVirtualDevice(int inputDeviceId) { 858 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 859 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 860 if (virtualDevicesSnapshot.get(i) 861 .isInputDeviceOwnedByVirtualDevice(inputDeviceId)) { 862 return true; 863 } 864 } 865 return false; 866 } 867 868 @Override getDisplayIdsForDevice(int deviceId)869 public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) { 870 VirtualDeviceImpl virtualDevice; 871 synchronized (mVirtualDeviceManagerLock) { 872 virtualDevice = mVirtualDevices.get(deviceId); 873 } 874 return virtualDevice == null ? new ArraySet<>() 875 : Arrays.stream(virtualDevice.getDisplayIds()).boxed() 876 .collect(Collectors.toCollection(ArraySet::new)); 877 } 878 879 @Override getDeviceIdForDisplayId(int displayId)880 public int getDeviceIdForDisplayId(int displayId) { 881 return mImpl.getDeviceIdForDisplayId(displayId); 882 } 883 884 @Override isValidVirtualDeviceId(int deviceId)885 public boolean isValidVirtualDeviceId(int deviceId) { 886 return mImpl.isValidVirtualDeviceId(deviceId); 887 } 888 889 @Override getPersistentIdForDevice(int deviceId)890 public @Nullable String getPersistentIdForDevice(int deviceId) { 891 if (deviceId == Context.DEVICE_ID_DEFAULT) { 892 return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; 893 } 894 895 VirtualDeviceImpl virtualDevice; 896 synchronized (mVirtualDeviceManagerLock) { 897 virtualDevice = mVirtualDevices.get(deviceId); 898 } 899 return virtualDevice == null ? null : virtualDevice.getPersistentDeviceId(); 900 } 901 902 @Override getAllPersistentDeviceIds()903 public @NonNull Set<String> getAllPersistentDeviceIds() { 904 synchronized (mVirtualDeviceManagerLock) { 905 return Set.copyOf(mActiveAssociations.keySet()); 906 } 907 } 908 909 @Override registerAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)910 public void registerAppsOnVirtualDeviceListener( 911 @NonNull AppsOnVirtualDeviceListener listener) { 912 synchronized (mVirtualDeviceManagerLock) { 913 mAppsOnVirtualDeviceListeners.add(listener); 914 } 915 } 916 917 @Override unregisterAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)918 public void unregisterAppsOnVirtualDeviceListener( 919 @NonNull AppsOnVirtualDeviceListener listener) { 920 synchronized (mVirtualDeviceManagerLock) { 921 mAppsOnVirtualDeviceListeners.remove(listener); 922 } 923 } 924 925 @Override registerPersistentDeviceIdRemovedListener( @onNull Consumer<String> persistentDeviceIdRemovedListener)926 public void registerPersistentDeviceIdRemovedListener( 927 @NonNull Consumer<String> persistentDeviceIdRemovedListener) { 928 synchronized (mVirtualDeviceManagerLock) { 929 mPersistentDeviceIdRemovedListeners.add(persistentDeviceIdRemovedListener); 930 } 931 } 932 933 @Override unregisterPersistentDeviceIdRemovedListener( @onNull Consumer<String> persistentDeviceIdRemovedListener)934 public void unregisterPersistentDeviceIdRemovedListener( 935 @NonNull Consumer<String> persistentDeviceIdRemovedListener) { 936 synchronized (mVirtualDeviceManagerLock) { 937 mPersistentDeviceIdRemovedListeners.remove(persistentDeviceIdRemovedListener); 938 } 939 } 940 } 941 942 private static final class PendingTrampolineMap { 943 /** 944 * The maximum duration, in milliseconds, to wait for a trampoline activity launch after 945 * invoking a pending intent. 946 */ 947 private static final int TRAMPOLINE_WAIT_MS = 5000; 948 949 private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>(); 950 private final Handler mHandler; 951 PendingTrampolineMap(Handler handler)952 PendingTrampolineMap(Handler handler) { 953 mHandler = handler; 954 } 955 put( @onNull String packageName, @NonNull PendingTrampoline pendingTrampoline)956 PendingTrampoline put( 957 @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) { 958 PendingTrampoline existing = mMap.put(packageName, pendingTrampoline); 959 mHandler.removeCallbacksAndMessages(existing); 960 mHandler.postDelayed( 961 () -> { 962 final String creatorPackage = 963 pendingTrampoline.mPendingIntent.getCreatorPackage(); 964 if (creatorPackage != null) { 965 remove(creatorPackage); 966 } 967 }, 968 pendingTrampoline, 969 TRAMPOLINE_WAIT_MS); 970 return existing; 971 } 972 remove(@onNull String packageName)973 PendingTrampoline remove(@NonNull String packageName) { 974 PendingTrampoline pendingTrampoline = mMap.remove(packageName); 975 mHandler.removeCallbacksAndMessages(pendingTrampoline); 976 return pendingTrampoline; 977 } 978 } 979 } 980