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.SuppressLint; 27 import android.app.ActivityOptions; 28 import android.companion.AssociationInfo; 29 import android.companion.CompanionDeviceManager; 30 import android.companion.virtual.IVirtualDevice; 31 import android.companion.virtual.IVirtualDeviceActivityListener; 32 import android.companion.virtual.IVirtualDeviceManager; 33 import android.companion.virtual.IVirtualDeviceSoundEffectListener; 34 import android.companion.virtual.VirtualDevice; 35 import android.companion.virtual.VirtualDeviceManager; 36 import android.companion.virtual.VirtualDeviceParams; 37 import android.companion.virtual.sensor.VirtualSensor; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.hardware.display.IVirtualDisplayCallback; 41 import android.hardware.display.VirtualDisplayConfig; 42 import android.os.Binder; 43 import android.os.Handler; 44 import android.os.IBinder; 45 import android.os.LocaleList; 46 import android.os.Looper; 47 import android.os.Parcel; 48 import android.os.Process; 49 import android.os.RemoteException; 50 import android.os.UserHandle; 51 import android.util.ArraySet; 52 import android.util.ExceptionUtils; 53 import android.util.Slog; 54 import android.util.SparseArray; 55 import android.view.Display; 56 import android.widget.Toast; 57 58 import com.android.internal.R; 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.internal.util.DumpUtils; 62 import com.android.server.SystemService; 63 import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline; 64 import com.android.server.wm.ActivityInterceptorCallback; 65 import com.android.server.wm.ActivityTaskManagerInternal; 66 67 import java.io.FileDescriptor; 68 import java.io.PrintWriter; 69 import java.util.ArrayList; 70 import java.util.HashSet; 71 import java.util.List; 72 import java.util.Objects; 73 import java.util.Set; 74 import java.util.concurrent.ConcurrentHashMap; 75 import java.util.concurrent.atomic.AtomicInteger; 76 import java.util.function.Consumer; 77 78 79 @SuppressLint("LongLogTag") 80 public class VirtualDeviceManagerService extends SystemService { 81 82 private static final String TAG = "VirtualDeviceManagerService"; 83 84 private final Object mVirtualDeviceManagerLock = new Object(); 85 private final VirtualDeviceManagerImpl mImpl; 86 private final VirtualDeviceManagerInternal mLocalService; 87 private final Handler mHandler = new Handler(Looper.getMainLooper()); 88 private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler); 89 90 private static AtomicInteger sNextUniqueIndex = new AtomicInteger( 91 Context.DEVICE_ID_DEFAULT + 1); 92 93 private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener = 94 new CompanionDeviceManager.OnAssociationsChangedListener() { 95 @Override 96 public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) { 97 syncVirtualDevicesToCdmAssociations(associations); 98 } 99 }; 100 101 /** 102 * Mapping from device IDs to virtual devices. 103 */ 104 @GuardedBy("mVirtualDeviceManagerLock") 105 private final SparseArray<VirtualDeviceImpl> mVirtualDevices = new SparseArray<>(); 106 107 /** 108 * Mapping from device IDs to app UIDs running on the corresponding virtual device. 109 */ 110 @GuardedBy("mVirtualDeviceManagerLock") 111 private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>(); 112 VirtualDeviceManagerService(Context context)113 public VirtualDeviceManagerService(Context context) { 114 super(context); 115 mImpl = new VirtualDeviceManagerImpl(); 116 mLocalService = new LocalService(); 117 } 118 119 private final ActivityInterceptorCallback mActivityInterceptorCallback = 120 new ActivityInterceptorCallback() { 121 122 @Nullable 123 @Override 124 public ActivityInterceptResult onInterceptActivityLaunch(@NonNull 125 ActivityInterceptorInfo info) { 126 if (info.getCallingPackage() == null) { 127 return null; 128 } 129 PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage()); 130 if (pt == null) { 131 return null; 132 } 133 pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); 134 ActivityOptions options = info.getCheckedOptions(); 135 if (options == null) { 136 options = ActivityOptions.makeBasic(); 137 } 138 return new ActivityInterceptResult( 139 info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId)); 140 } 141 }; 142 143 @Override onStart()144 public void onStart() { 145 publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl); 146 publishLocalService(VirtualDeviceManagerInternal.class, mLocalService); 147 ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService( 148 ActivityTaskManagerInternal.class); 149 activityTaskManagerInternal.registerActivityStartInterceptor( 150 VIRTUAL_DEVICE_SERVICE_ORDERED_ID, 151 mActivityInterceptorCallback); 152 } 153 onCameraAccessBlocked(int appUid)154 void onCameraAccessBlocked(int appUid) { 155 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 156 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 157 VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i); 158 virtualDevice.showToastWhereUidIsRunning(appUid, 159 getContext().getString( 160 R.string.vdm_camera_access_denied, 161 virtualDevice.getDisplayName()), 162 Toast.LENGTH_LONG, Looper.myLooper()); 163 } 164 } 165 getCameraAccessController(UserHandle userHandle)166 CameraAccessController getCameraAccessController(UserHandle userHandle) { 167 int userId = userHandle.getIdentifier(); 168 synchronized (mVirtualDeviceManagerLock) { 169 for (int i = 0; i < mVirtualDevices.size(); i++) { 170 final CameraAccessController cameraAccessController = 171 mVirtualDevices.valueAt(i).getCameraAccessController(); 172 if (cameraAccessController.getUserId() == userId) { 173 return cameraAccessController; 174 } 175 } 176 } 177 Context userContext = getContext().createContextAsUser(userHandle, 0); 178 return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked); 179 } 180 181 @VisibleForTesting getLocalServiceInstance()182 VirtualDeviceManagerInternal getLocalServiceInstance() { 183 return mLocalService; 184 } 185 186 @VisibleForTesting notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids)187 void notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids) { 188 synchronized (mVirtualDeviceManagerLock) { 189 if (!mVirtualDevices.contains(deviceId)) { 190 Slog.e(TAG, "notifyRunningAppsChanged called for unknown deviceId:" + deviceId 191 + " (maybe it was recently closed?)"); 192 return; 193 } 194 mAppsOnVirtualDevices.put(deviceId, uids); 195 } 196 mLocalService.onAppsOnVirtualDeviceChanged(); 197 } 198 199 @VisibleForTesting addVirtualDevice(VirtualDeviceImpl virtualDevice)200 void addVirtualDevice(VirtualDeviceImpl virtualDevice) { 201 synchronized (mVirtualDeviceManagerLock) { 202 mVirtualDevices.put(virtualDevice.getDeviceId(), virtualDevice); 203 } 204 } 205 206 /** 207 * Remove the virtual device. Sends the 208 * {@link VirtualDeviceManager#ACTION_VIRTUAL_DEVICE_REMOVED} broadcast as a result. 209 * 210 * @param deviceId deviceId to be removed 211 * @return {@code true} if the device was removed, {@code false} if the operation was a no-op 212 */ removeVirtualDevice(int deviceId)213 boolean removeVirtualDevice(int deviceId) { 214 synchronized (mVirtualDeviceManagerLock) { 215 if (!mVirtualDevices.contains(deviceId)) { 216 return false; 217 } 218 219 mAppsOnVirtualDevices.remove(deviceId); 220 mVirtualDevices.remove(deviceId); 221 } 222 223 Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED); 224 i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId); 225 i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 226 final long identity = Binder.clearCallingIdentity(); 227 try { 228 getContext().sendBroadcastAsUser(i, UserHandle.ALL); 229 230 synchronized (mVirtualDeviceManagerLock) { 231 if (mVirtualDevices.size() == 0) { 232 unregisterCdmAssociationListener(); 233 } 234 } 235 } finally { 236 Binder.restoreCallingIdentity(identity); 237 } 238 return true; 239 } 240 syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations)241 private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) { 242 Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>(); 243 synchronized (mVirtualDeviceManagerLock) { 244 if (mVirtualDevices.size() == 0) { 245 return; 246 } 247 248 Set<Integer> activeAssociationIds = new HashSet<>(associations.size()); 249 for (AssociationInfo association : associations) { 250 activeAssociationIds.add(association.getId()); 251 } 252 253 for (int i = 0; i < mVirtualDevices.size(); i++) { 254 VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); 255 if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) { 256 virtualDevicesToRemove.add(virtualDevice); 257 } 258 } 259 } 260 261 for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) { 262 virtualDevice.close(); 263 } 264 } 265 registerCdmAssociationListener()266 private void registerCdmAssociationListener() { 267 final CompanionDeviceManager cdm = getContext().getSystemService( 268 CompanionDeviceManager.class); 269 cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(), 270 mCdmAssociationListener); 271 } 272 unregisterCdmAssociationListener()273 private void unregisterCdmAssociationListener() { 274 final CompanionDeviceManager cdm = getContext().getSystemService( 275 CompanionDeviceManager.class); 276 cdm.removeOnAssociationsChangedListener(mCdmAssociationListener); 277 } 278 getVirtualDevicesSnapshot()279 private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() { 280 synchronized (mVirtualDeviceManagerLock) { 281 ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size()); 282 for (int i = 0; i < mVirtualDevices.size(); i++) { 283 virtualDevices.add(mVirtualDevices.valueAt(i)); 284 } 285 return virtualDevices; 286 } 287 } 288 289 class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { 290 291 private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = 292 new VirtualDeviceImpl.PendingTrampolineCallback() { 293 @Override 294 public void startWaitingForPendingTrampoline( 295 PendingTrampoline pendingTrampoline) { 296 PendingTrampoline existing = mPendingTrampolines.put( 297 pendingTrampoline.mPendingIntent.getCreatorPackage(), 298 pendingTrampoline); 299 if (existing != null) { 300 existing.mResultReceiver.send( 301 VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); 302 } 303 } 304 305 @Override 306 public void stopWaitingForPendingTrampoline( 307 PendingTrampoline pendingTrampoline) { 308 mPendingTrampolines.remove( 309 pendingTrampoline.mPendingIntent.getCreatorPackage()); 310 } 311 }; 312 313 @Override // Binder call createVirtualDevice( IBinder token, String packageName, int associationId, @NonNull VirtualDeviceParams params, @NonNull IVirtualDeviceActivityListener activityListener, @NonNull IVirtualDeviceSoundEffectListener soundEffectListener)314 public IVirtualDevice createVirtualDevice( 315 IBinder token, 316 String packageName, 317 int associationId, 318 @NonNull VirtualDeviceParams params, 319 @NonNull IVirtualDeviceActivityListener activityListener, 320 @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { 321 getContext().enforceCallingOrSelfPermission( 322 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 323 "createVirtualDevice"); 324 final int callingUid = getCallingUid(); 325 if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { 326 throw new SecurityException( 327 "Package name " + packageName + " does not belong to calling uid " 328 + callingUid); 329 } 330 AssociationInfo associationInfo = getAssociationInfo(packageName, associationId); 331 if (associationInfo == null) { 332 throw new IllegalArgumentException("No association with ID " + associationId); 333 } 334 Objects.requireNonNull(params); 335 Objects.requireNonNull(activityListener); 336 Objects.requireNonNull(soundEffectListener); 337 338 final UserHandle userHandle = getCallingUserHandle(); 339 final CameraAccessController cameraAccessController = 340 getCameraAccessController(userHandle); 341 final int deviceId = sNextUniqueIndex.getAndIncrement(); 342 final Consumer<ArraySet<Integer>> runningAppsChangedCallback = 343 runningUids -> notifyRunningAppsChanged(deviceId, runningUids); 344 VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), 345 associationInfo, VirtualDeviceManagerService.this, token, callingUid, 346 deviceId, cameraAccessController, 347 mPendingTrampolineCallback, activityListener, 348 soundEffectListener, runningAppsChangedCallback, params); 349 synchronized (mVirtualDeviceManagerLock) { 350 if (mVirtualDevices.size() == 0) { 351 final long callindId = Binder.clearCallingIdentity(); 352 try { 353 registerCdmAssociationListener(); 354 } finally { 355 Binder.restoreCallingIdentity(callindId); 356 } 357 } 358 mVirtualDevices.put(deviceId, virtualDevice); 359 } 360 return virtualDevice; 361 } 362 363 @Override // Binder call createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)364 public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, 365 IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName) 366 throws RemoteException { 367 Objects.requireNonNull(virtualDisplayConfig); 368 final int callingUid = getCallingUid(); 369 if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { 370 throw new SecurityException( 371 "Package name " + packageName + " does not belong to calling uid " 372 + callingUid); 373 } 374 VirtualDeviceImpl virtualDeviceImpl; 375 synchronized (mVirtualDeviceManagerLock) { 376 virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId()); 377 if (virtualDeviceImpl == null) { 378 throw new SecurityException("Invalid VirtualDevice"); 379 } 380 } 381 if (virtualDeviceImpl.getOwnerUid() != callingUid) { 382 throw new SecurityException( 383 "uid " + callingUid 384 + " is not the owner of the supplied VirtualDevice"); 385 } 386 387 int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback, 388 packageName); 389 mLocalService.onVirtualDisplayCreated(displayId); 390 return displayId; 391 } 392 393 @Override // Binder call getVirtualDevices()394 public List<VirtualDevice> getVirtualDevices() { 395 List<VirtualDevice> virtualDevices = new ArrayList<>(); 396 synchronized (mVirtualDeviceManagerLock) { 397 for (int i = 0; i < mVirtualDevices.size(); i++) { 398 final VirtualDeviceImpl device = mVirtualDevices.valueAt(i); 399 virtualDevices.add( 400 new VirtualDevice(device.getDeviceId(), device.getDeviceName())); 401 } 402 } 403 return virtualDevices; 404 } 405 406 @Override // BinderCall 407 @VirtualDeviceParams.DevicePolicy getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType)408 public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) { 409 synchronized (mVirtualDeviceManagerLock) { 410 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 411 return virtualDevice != null 412 ? virtualDevice.getDevicePolicy(policyType) : DEVICE_POLICY_DEFAULT; 413 } 414 } 415 416 417 @Override // Binder call getDeviceIdForDisplayId(int displayId)418 public int getDeviceIdForDisplayId(int displayId) { 419 if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) { 420 return Context.DEVICE_ID_DEFAULT; 421 } 422 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 423 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 424 VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i); 425 if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) { 426 return virtualDevice.getDeviceId(); 427 } 428 } 429 return Context.DEVICE_ID_DEFAULT; 430 } 431 432 // Binder call 433 @Override isValidVirtualDeviceId(int deviceId)434 public boolean isValidVirtualDeviceId(int deviceId) { 435 synchronized (mVirtualDeviceManagerLock) { 436 return mVirtualDevices.contains(deviceId); 437 } 438 } 439 440 @Override // Binder call getAudioPlaybackSessionId(int deviceId)441 public int getAudioPlaybackSessionId(int deviceId) { 442 synchronized (mVirtualDeviceManagerLock) { 443 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 444 return virtualDevice != null 445 ? virtualDevice.getAudioPlaybackSessionId() : AUDIO_SESSION_ID_GENERATE; 446 } 447 } 448 449 @Override // Binder call getAudioRecordingSessionId(int deviceId)450 public int getAudioRecordingSessionId(int deviceId) { 451 synchronized (mVirtualDeviceManagerLock) { 452 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); 453 return virtualDevice != null 454 ? virtualDevice.getAudioRecordingSessionId() : AUDIO_SESSION_ID_GENERATE; 455 } 456 } 457 458 @Override // Binder call playSoundEffect(int deviceId, int effectType)459 public void playSoundEffect(int deviceId, int effectType) { 460 VirtualDeviceImpl virtualDevice; 461 synchronized (mVirtualDeviceManagerLock) { 462 virtualDevice = mVirtualDevices.get(deviceId); 463 } 464 465 if (virtualDevice != null) { 466 virtualDevice.playSoundEffect(effectType); 467 } 468 } 469 470 @Nullable getAssociationInfo(String packageName, int associationId)471 private AssociationInfo getAssociationInfo(String packageName, int associationId) { 472 final UserHandle userHandle = getCallingUserHandle(); 473 final CompanionDeviceManager cdm = 474 getContext().createContextAsUser(userHandle, 0) 475 .getSystemService(CompanionDeviceManager.class); 476 List<AssociationInfo> associations; 477 final long identity = Binder.clearCallingIdentity(); 478 try { 479 associations = cdm.getAllAssociations(); 480 } finally { 481 Binder.restoreCallingIdentity(identity); 482 } 483 final int callingUserId = userHandle.getIdentifier(); 484 if (associations != null) { 485 final int associationSize = associations.size(); 486 for (int i = 0; i < associationSize; i++) { 487 AssociationInfo associationInfo = associations.get(i); 488 if (associationInfo.belongsToPackage(callingUserId, packageName) 489 && associationId == associationInfo.getId()) { 490 return associationInfo; 491 } 492 } 493 } else { 494 Slog.w(TAG, "No associations for user " + callingUserId); 495 } 496 return null; 497 } 498 499 @Override onTransact(int code, Parcel data, Parcel reply, int flags)500 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 501 throws RemoteException { 502 try { 503 return super.onTransact(code, data, reply, flags); 504 } catch (Throwable e) { 505 Slog.e(TAG, "Error during IPC", e); 506 throw ExceptionUtils.propagate(e, RemoteException.class); 507 } 508 } 509 510 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args)511 public void dump(@NonNull FileDescriptor fd, 512 @NonNull PrintWriter fout, 513 @Nullable String[] args) { 514 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) { 515 return; 516 } 517 fout.println("Created virtual devices: "); 518 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 519 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 520 virtualDevicesSnapshot.get(i).dump(fd, fout, args); 521 } 522 } 523 } 524 525 private final class LocalService extends VirtualDeviceManagerInternal { 526 @GuardedBy("mVirtualDeviceManagerLock") 527 private final ArrayList<VirtualDisplayListener> 528 mVirtualDisplayListeners = new ArrayList<>(); 529 @GuardedBy("mVirtualDeviceManagerLock") 530 private final ArrayList<AppsOnVirtualDeviceListener> 531 mAppsOnVirtualDeviceListeners = new ArrayList<>(); 532 @GuardedBy("mVirtualDeviceManagerLock") 533 private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>(); 534 535 @Override getDeviceOwnerUid(int deviceId)536 public int getDeviceOwnerUid(int deviceId) { 537 VirtualDeviceImpl virtualDevice; 538 synchronized (mVirtualDeviceManagerLock) { 539 virtualDevice = mVirtualDevices.get(deviceId); 540 } 541 return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID; 542 } 543 544 @Override getVirtualSensor(int deviceId, int handle)545 public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) { 546 VirtualDeviceImpl virtualDevice; 547 synchronized (mVirtualDeviceManagerLock) { 548 virtualDevice = mVirtualDevices.get(deviceId); 549 } 550 return virtualDevice != null ? virtualDevice.getVirtualSensorByHandle(handle) : null; 551 } 552 553 @Override getDeviceIdsForUid(int uid)554 public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) { 555 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 556 ArraySet<Integer> result = new ArraySet<>(); 557 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 558 VirtualDeviceImpl device = virtualDevicesSnapshot.get(i); 559 if (device.isAppRunningOnVirtualDevice(uid)) { 560 result.add(device.getDeviceId()); 561 } 562 } 563 return result; 564 } 565 566 @Override onVirtualDisplayCreated(int displayId)567 public void onVirtualDisplayCreated(int displayId) { 568 final VirtualDisplayListener[] listeners; 569 synchronized (mVirtualDeviceManagerLock) { 570 listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]); 571 } 572 mHandler.post(() -> { 573 for (VirtualDisplayListener listener : listeners) { 574 listener.onVirtualDisplayCreated(displayId); 575 } 576 }); 577 } 578 579 @Override onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId)580 public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { 581 final VirtualDisplayListener[] listeners; 582 VirtualDeviceImpl virtualDeviceImpl; 583 synchronized (mVirtualDeviceManagerLock) { 584 listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]); 585 virtualDeviceImpl = mVirtualDevices.get( 586 ((VirtualDeviceImpl) virtualDevice).getDeviceId()); 587 } 588 if (virtualDeviceImpl != null) { 589 virtualDeviceImpl.onVirtualDisplayRemoved(displayId); 590 } 591 mHandler.post(() -> { 592 for (VirtualDisplayListener listener : listeners) { 593 listener.onVirtualDisplayRemoved(displayId); 594 } 595 }); 596 } 597 598 @Override onAppsOnVirtualDeviceChanged()599 public void onAppsOnVirtualDeviceChanged() { 600 ArraySet<Integer> latestRunningUids = new ArraySet<>(); 601 final AppsOnVirtualDeviceListener[] listeners; 602 synchronized (mVirtualDeviceManagerLock) { 603 int size = mAppsOnVirtualDevices.size(); 604 for (int i = 0; i < size; i++) { 605 latestRunningUids.addAll(mAppsOnVirtualDevices.valueAt(i)); 606 } 607 if (!mAllUidsOnVirtualDevice.equals(latestRunningUids)) { 608 mAllUidsOnVirtualDevice.clear(); 609 mAllUidsOnVirtualDevice.addAll(latestRunningUids); 610 listeners = 611 mAppsOnVirtualDeviceListeners.toArray( 612 new AppsOnVirtualDeviceListener[0]); 613 } else { 614 listeners = null; 615 } 616 } 617 if (listeners != null) { 618 mHandler.post(() -> { 619 for (AppsOnVirtualDeviceListener listener : listeners) { 620 listener.onAppsOnAnyVirtualDeviceChanged(latestRunningUids); 621 } 622 }); 623 } 624 } 625 626 @Override onAuthenticationPrompt(int uid)627 public void onAuthenticationPrompt(int uid) { 628 synchronized (mVirtualDeviceManagerLock) { 629 for (int i = 0; i < mVirtualDevices.size(); i++) { 630 VirtualDeviceImpl device = mVirtualDevices.valueAt(i); 631 device.showToastWhereUidIsRunning(uid, 632 R.string.app_streaming_blocked_message_for_fingerprint_dialog, 633 Toast.LENGTH_LONG, Looper.getMainLooper()); 634 } 635 } 636 } 637 638 @Override getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice)639 public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) { 640 return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags(); 641 } 642 643 @Override 644 @Nullable getPreferredLocaleListForUid(int uid)645 public LocaleList getPreferredLocaleListForUid(int uid) { 646 // TODO: b/263188984 support the case where an app is running on multiple VDs 647 synchronized (mVirtualDeviceManagerLock) { 648 for (int i = 0; i < mAppsOnVirtualDevices.size(); i++) { 649 if (mAppsOnVirtualDevices.valueAt(i).contains(uid)) { 650 int deviceId = mAppsOnVirtualDevices.keyAt(i); 651 return mVirtualDevices.get(deviceId).getDeviceLocaleList(); 652 } 653 } 654 } 655 return null; 656 } 657 658 @Override isAppRunningOnAnyVirtualDevice(int uid)659 public boolean isAppRunningOnAnyVirtualDevice(int uid) { 660 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 661 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 662 if (virtualDevicesSnapshot.get(i).isAppRunningOnVirtualDevice(uid)) { 663 return true; 664 } 665 } 666 return false; 667 } 668 669 @Override isDisplayOwnedByAnyVirtualDevice(int displayId)670 public boolean isDisplayOwnedByAnyVirtualDevice(int displayId) { 671 ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot(); 672 for (int i = 0; i < virtualDevicesSnapshot.size(); i++) { 673 if (virtualDevicesSnapshot.get(i).isDisplayOwnedByVirtualDevice(displayId)) { 674 return true; 675 } 676 } 677 return false; 678 } 679 680 @Override getDisplayIdsForDevice(int deviceId)681 public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) { 682 VirtualDeviceImpl virtualDevice; 683 synchronized (mVirtualDeviceManagerLock) { 684 virtualDevice = mVirtualDevices.get(deviceId); 685 } 686 return virtualDevice == null ? new ArraySet<>() : virtualDevice.getDisplayIds(); 687 } 688 689 @Override registerVirtualDisplayListener( @onNull VirtualDisplayListener listener)690 public void registerVirtualDisplayListener( 691 @NonNull VirtualDisplayListener listener) { 692 synchronized (mVirtualDeviceManagerLock) { 693 mVirtualDisplayListeners.add(listener); 694 } 695 } 696 697 @Override unregisterVirtualDisplayListener( @onNull VirtualDisplayListener listener)698 public void unregisterVirtualDisplayListener( 699 @NonNull VirtualDisplayListener listener) { 700 synchronized (mVirtualDeviceManagerLock) { 701 mVirtualDisplayListeners.remove(listener); 702 } 703 } 704 705 @Override registerAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)706 public void registerAppsOnVirtualDeviceListener( 707 @NonNull AppsOnVirtualDeviceListener listener) { 708 synchronized (mVirtualDeviceManagerLock) { 709 mAppsOnVirtualDeviceListeners.add(listener); 710 } 711 } 712 713 @Override unregisterAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)714 public void unregisterAppsOnVirtualDeviceListener( 715 @NonNull AppsOnVirtualDeviceListener listener) { 716 synchronized (mVirtualDeviceManagerLock) { 717 mAppsOnVirtualDeviceListeners.remove(listener); 718 } 719 } 720 } 721 722 private static final class PendingTrampolineMap { 723 /** 724 * The maximum duration, in milliseconds, to wait for a trampoline activity launch after 725 * invoking a pending intent. 726 */ 727 private static final int TRAMPOLINE_WAIT_MS = 5000; 728 729 private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>(); 730 private final Handler mHandler; 731 PendingTrampolineMap(Handler handler)732 PendingTrampolineMap(Handler handler) { 733 mHandler = handler; 734 } 735 put( @onNull String packageName, @NonNull PendingTrampoline pendingTrampoline)736 PendingTrampoline put( 737 @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) { 738 PendingTrampoline existing = mMap.put(packageName, pendingTrampoline); 739 mHandler.removeCallbacksAndMessages(existing); 740 mHandler.postDelayed( 741 () -> { 742 final String creatorPackage = 743 pendingTrampoline.mPendingIntent.getCreatorPackage(); 744 if (creatorPackage != null) { 745 remove(creatorPackage); 746 } 747 }, 748 pendingTrampoline, 749 TRAMPOLINE_WAIT_MS); 750 return existing; 751 } 752 remove(@onNull String packageName)753 PendingTrampoline remove(@NonNull String packageName) { 754 PendingTrampoline pendingTrampoline = mMap.remove(packageName); 755 mHandler.removeCallbacksAndMessages(pendingTrampoline); 756 return pendingTrampoline; 757 } 758 } 759 } 760