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.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED; 20 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY; 21 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY; 22 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 23 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.RequiresPermission; 28 import android.annotation.StringRes; 29 import android.app.Activity; 30 import android.app.ActivityOptions; 31 import android.app.PendingIntent; 32 import android.app.admin.DevicePolicyManager; 33 import android.companion.AssociationInfo; 34 import android.companion.virtual.IVirtualDevice; 35 import android.companion.virtual.IVirtualDeviceActivityListener; 36 import android.companion.virtual.VirtualDeviceManager; 37 import android.companion.virtual.VirtualDeviceManager.ActivityListener; 38 import android.companion.virtual.VirtualDeviceParams; 39 import android.companion.virtual.audio.IAudioConfigChangedCallback; 40 import android.companion.virtual.audio.IAudioRoutingCallback; 41 import android.content.ComponentName; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.pm.ActivityInfo; 45 import android.graphics.Point; 46 import android.graphics.PointF; 47 import android.hardware.display.DisplayManager; 48 import android.hardware.input.VirtualKeyEvent; 49 import android.hardware.input.VirtualMouseButtonEvent; 50 import android.hardware.input.VirtualMouseRelativeEvent; 51 import android.hardware.input.VirtualMouseScrollEvent; 52 import android.hardware.input.VirtualTouchEvent; 53 import android.os.Binder; 54 import android.os.IBinder; 55 import android.os.Looper; 56 import android.os.PowerManager; 57 import android.os.RemoteException; 58 import android.os.ResultReceiver; 59 import android.os.UserHandle; 60 import android.os.UserManager; 61 import android.util.ArrayMap; 62 import android.util.ArraySet; 63 import android.util.Slog; 64 import android.util.SparseArray; 65 import android.view.Display; 66 import android.view.WindowManager; 67 import android.widget.Toast; 68 69 import com.android.internal.annotations.GuardedBy; 70 import com.android.internal.annotations.VisibleForTesting; 71 import com.android.internal.app.BlockedAppStreamingActivity; 72 import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; 73 import com.android.server.companion.virtual.audio.VirtualAudioController; 74 75 import java.io.FileDescriptor; 76 import java.io.PrintWriter; 77 import java.util.Map; 78 import java.util.Set; 79 import java.util.function.Consumer; 80 81 82 final class VirtualDeviceImpl extends IVirtualDevice.Stub 83 implements IBinder.DeathRecipient, RunningAppsChangedListener { 84 85 private static final String TAG = "VirtualDeviceImpl"; 86 87 /** 88 * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. 89 */ 90 private static final long PENDING_TRAMPOLINE_TIMEOUT_MS = 5000; 91 92 private final Object mVirtualDeviceLock = new Object(); 93 94 private final Context mContext; 95 private final AssociationInfo mAssociationInfo; 96 private final PendingTrampolineCallback mPendingTrampolineCallback; 97 private final int mOwnerUid; 98 private final InputController mInputController; 99 private VirtualAudioController mVirtualAudioController; 100 @VisibleForTesting 101 final Set<Integer> mVirtualDisplayIds = new ArraySet<>(); 102 private final OnDeviceCloseListener mListener; 103 private final IBinder mAppToken; 104 private final VirtualDeviceParams mParams; 105 private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>(); 106 private final IVirtualDeviceActivityListener mActivityListener; 107 @NonNull 108 private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; 109 // The default setting for showing the pointer on new displays. 110 @GuardedBy("mVirtualDeviceLock") 111 private boolean mDefaultShowPointerIcon = true; 112 createListenerAdapter()113 private ActivityListener createListenerAdapter() { 114 return new ActivityListener() { 115 116 @Override 117 public void onTopActivityChanged(int displayId, ComponentName topActivity) { 118 try { 119 mActivityListener.onTopActivityChanged(displayId, topActivity); 120 } catch (RemoteException e) { 121 Slog.w(TAG, "Unable to call mActivityListener", e); 122 } 123 } 124 125 @Override 126 public void onDisplayEmpty(int displayId) { 127 try { 128 mActivityListener.onDisplayEmpty(displayId); 129 } catch (RemoteException e) { 130 Slog.w(TAG, "Unable to call mActivityListener", e); 131 } 132 } 133 }; 134 } 135 136 /** 137 * A mapping from the virtual display ID to its corresponding 138 * {@link GenericWindowPolicyController}. 139 */ 140 private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers = 141 new SparseArray<>(); 142 143 VirtualDeviceImpl(Context context, AssociationInfo associationInfo, 144 IBinder token, int ownerUid, OnDeviceCloseListener listener, 145 PendingTrampolineCallback pendingTrampolineCallback, 146 IVirtualDeviceActivityListener activityListener, 147 Consumer<ArraySet<Integer>> runningAppsChangedCallback, 148 VirtualDeviceParams params) { 149 this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener, 150 pendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); 151 } 152 153 @VisibleForTesting 154 VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token, 155 int ownerUid, InputController inputController, OnDeviceCloseListener listener, 156 PendingTrampolineCallback pendingTrampolineCallback, 157 IVirtualDeviceActivityListener activityListener, 158 Consumer<ArraySet<Integer>> runningAppsChangedCallback, 159 VirtualDeviceParams params) { 160 UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); 161 mContext = context.createContextAsUser(ownerUserHandle, 0); 162 mAssociationInfo = associationInfo; 163 mPendingTrampolineCallback = pendingTrampolineCallback; 164 mActivityListener = activityListener; 165 mRunningAppsChangedCallback = runningAppsChangedCallback; 166 mOwnerUid = ownerUid; 167 mAppToken = token; 168 mParams = params; 169 if (inputController == null) { 170 mInputController = new InputController( 171 mVirtualDeviceLock, 172 context.getMainThreadHandler(), 173 context.getSystemService(WindowManager.class)); 174 } else { 175 mInputController = inputController; 176 } 177 mListener = listener; 178 try { 179 token.linkToDeath(this, 0); 180 } catch (RemoteException e) { 181 throw e.rethrowFromSystemServer(); 182 } 183 } 184 185 /** 186 * Returns the flags that should be added to any virtual displays created on this virtual 187 * device. 188 */ 189 int getBaseVirtualDisplayFlags() { 190 int flags = 0; 191 if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) { 192 flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; 193 } 194 return flags; 195 } 196 197 /** Returns the device display name. */ 198 CharSequence getDisplayName() { 199 return mAssociationInfo.getDisplayName(); 200 } 201 202 @Override // Binder call 203 public int getAssociationId() { 204 return mAssociationInfo.getId(); 205 } 206 207 @Override // Binder call 208 public void launchPendingIntent(int displayId, PendingIntent pendingIntent, 209 ResultReceiver resultReceiver) { 210 if (!mVirtualDisplayIds.contains(displayId)) { 211 throw new SecurityException("Display ID " + displayId 212 + " not found for this virtual device"); 213 } 214 if (pendingIntent.isActivity()) { 215 try { 216 sendPendingIntent(displayId, pendingIntent); 217 resultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); 218 } catch (PendingIntent.CanceledException e) { 219 Slog.w(TAG, "Pending intent canceled", e); 220 resultReceiver.send( 221 VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); 222 } 223 } else { 224 PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent, 225 resultReceiver, displayId); 226 mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline); 227 mContext.getMainThreadHandler().postDelayed(() -> { 228 pendingTrampoline.mResultReceiver.send( 229 VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); 230 mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); 231 }, PENDING_TRAMPOLINE_TIMEOUT_MS); 232 try { 233 sendPendingIntent(displayId, pendingIntent); 234 } catch (PendingIntent.CanceledException e) { 235 Slog.w(TAG, "Pending intent canceled", e); 236 resultReceiver.send( 237 VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); 238 mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); 239 } 240 } 241 } 242 243 private void sendPendingIntent(int displayId, PendingIntent pendingIntent) 244 throws PendingIntent.CanceledException { 245 final ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId); 246 options.setPendingIntentBackgroundActivityLaunchAllowed(true); 247 options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); 248 pendingIntent.send( 249 mContext, 250 /* code= */ 0, 251 /* intent= */ null, 252 /* onFinished= */ null, 253 /* handler= */ null, 254 /* requiredPermission= */ null, 255 options.toBundle()); 256 } 257 258 @Override // Binder call 259 public void close() { 260 mContext.enforceCallingOrSelfPermission( 261 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 262 "Permission required to close the virtual device"); 263 264 synchronized (mVirtualDeviceLock) { 265 if (!mPerDisplayWakelocks.isEmpty()) { 266 mPerDisplayWakelocks.forEach((displayId, wakeLock) -> { 267 Slog.w(TAG, "VirtualDisplay " + displayId + " owned by UID " + mOwnerUid 268 + " was not properly released"); 269 wakeLock.release(); 270 }); 271 mPerDisplayWakelocks.clear(); 272 } 273 if (mVirtualAudioController != null) { 274 mVirtualAudioController.stopListening(); 275 mVirtualAudioController = null; 276 } 277 } 278 mListener.onClose(mAssociationInfo.getId()); 279 mAppToken.unlinkToDeath(this, 0); 280 281 final long token = Binder.clearCallingIdentity(); 282 try { 283 mInputController.close(); 284 } finally { 285 Binder.restoreCallingIdentity(token); 286 } 287 } 288 289 @Override 290 public void binderDied() { 291 close(); 292 } 293 294 @Override 295 public void onRunningAppsChanged(ArraySet<Integer> runningUids) { 296 mRunningAppsChangedCallback.accept(runningUids); 297 } 298 299 @VisibleForTesting 300 VirtualAudioController getVirtualAudioControllerForTesting() { 301 return mVirtualAudioController; 302 } 303 304 @VisibleForTesting 305 SparseArray<GenericWindowPolicyController> getWindowPolicyControllersForTesting() { 306 return mWindowPolicyControllers; 307 } 308 309 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 310 @Override // Binder call 311 public void onAudioSessionStarting(int displayId, 312 @NonNull IAudioRoutingCallback routingCallback, 313 @Nullable IAudioConfigChangedCallback configChangedCallback) { 314 mContext.enforceCallingOrSelfPermission( 315 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 316 "Permission required to start audio session"); 317 synchronized (mVirtualDeviceLock) { 318 if (!mVirtualDisplayIds.contains(displayId)) { 319 throw new SecurityException( 320 "Cannot start audio session for a display not associated with this virtual " 321 + "device"); 322 } 323 324 if (mVirtualAudioController == null) { 325 mVirtualAudioController = new VirtualAudioController(mContext); 326 GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId); 327 mVirtualAudioController.startListening(gwpc, routingCallback, 328 configChangedCallback); 329 } 330 } 331 } 332 333 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 334 @Override // Binder call 335 public void onAudioSessionEnded() { 336 mContext.enforceCallingOrSelfPermission( 337 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 338 "Permission required to stop audio session"); 339 synchronized (mVirtualDeviceLock) { 340 if (mVirtualAudioController != null) { 341 mVirtualAudioController.stopListening(); 342 mVirtualAudioController = null; 343 } 344 } 345 } 346 347 @Override // Binder call 348 public void createVirtualKeyboard( 349 int displayId, 350 @NonNull String deviceName, 351 int vendorId, 352 int productId, 353 @NonNull IBinder deviceToken) { 354 mContext.enforceCallingOrSelfPermission( 355 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 356 "Permission required to create a virtual keyboard"); 357 synchronized (mVirtualDeviceLock) { 358 if (!mVirtualDisplayIds.contains(displayId)) { 359 throw new SecurityException( 360 "Cannot create a virtual keyboard for a display not associated with " 361 + "this virtual device"); 362 } 363 } 364 final long token = Binder.clearCallingIdentity(); 365 try { 366 mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken, 367 displayId); 368 } finally { 369 Binder.restoreCallingIdentity(token); 370 } 371 } 372 373 @Override // Binder call 374 public void createVirtualMouse( 375 int displayId, 376 @NonNull String deviceName, 377 int vendorId, 378 int productId, 379 @NonNull IBinder deviceToken) { 380 mContext.enforceCallingOrSelfPermission( 381 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 382 "Permission required to create a virtual mouse"); 383 synchronized (mVirtualDeviceLock) { 384 if (!mVirtualDisplayIds.contains(displayId)) { 385 throw new SecurityException( 386 "Cannot create a virtual mouse for a display not associated with this " 387 + "virtual device"); 388 } 389 } 390 final long token = Binder.clearCallingIdentity(); 391 try { 392 mInputController.createMouse(deviceName, vendorId, productId, deviceToken, displayId); 393 } finally { 394 Binder.restoreCallingIdentity(token); 395 } 396 } 397 398 @Override // Binder call 399 public void createVirtualTouchscreen( 400 int displayId, 401 @NonNull String deviceName, 402 int vendorId, 403 int productId, 404 @NonNull IBinder deviceToken, 405 @NonNull Point screenSize) { 406 mContext.enforceCallingOrSelfPermission( 407 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 408 "Permission required to create a virtual touchscreen"); 409 synchronized (mVirtualDeviceLock) { 410 if (!mVirtualDisplayIds.contains(displayId)) { 411 throw new SecurityException( 412 "Cannot create a virtual touchscreen for a display not associated with " 413 + "this virtual device"); 414 } 415 } 416 final long token = Binder.clearCallingIdentity(); 417 try { 418 mInputController.createTouchscreen(deviceName, vendorId, productId, 419 deviceToken, displayId, screenSize); 420 } finally { 421 Binder.restoreCallingIdentity(token); 422 } 423 } 424 425 @Override // Binder call 426 public void unregisterInputDevice(IBinder token) { 427 mContext.enforceCallingOrSelfPermission( 428 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 429 "Permission required to unregister this input device"); 430 431 final long binderToken = Binder.clearCallingIdentity(); 432 try { 433 mInputController.unregisterInputDevice(token); 434 } finally { 435 Binder.restoreCallingIdentity(binderToken); 436 } 437 } 438 439 @Override // Binder call 440 public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) { 441 final long binderToken = Binder.clearCallingIdentity(); 442 try { 443 return mInputController.sendKeyEvent(token, event); 444 } finally { 445 Binder.restoreCallingIdentity(binderToken); 446 } 447 } 448 449 @Override // Binder call 450 public boolean sendButtonEvent(IBinder token, VirtualMouseButtonEvent event) { 451 final long binderToken = Binder.clearCallingIdentity(); 452 try { 453 return mInputController.sendButtonEvent(token, event); 454 } finally { 455 Binder.restoreCallingIdentity(binderToken); 456 } 457 } 458 459 @Override // Binder call 460 public boolean sendTouchEvent(IBinder token, VirtualTouchEvent event) { 461 final long binderToken = Binder.clearCallingIdentity(); 462 try { 463 return mInputController.sendTouchEvent(token, event); 464 } finally { 465 Binder.restoreCallingIdentity(binderToken); 466 } 467 } 468 469 @Override // Binder call 470 public boolean sendRelativeEvent(IBinder token, VirtualMouseRelativeEvent event) { 471 final long binderToken = Binder.clearCallingIdentity(); 472 try { 473 return mInputController.sendRelativeEvent(token, event); 474 } finally { 475 Binder.restoreCallingIdentity(binderToken); 476 } 477 } 478 479 @Override // Binder call 480 public boolean sendScrollEvent(IBinder token, VirtualMouseScrollEvent event) { 481 final long binderToken = Binder.clearCallingIdentity(); 482 try { 483 return mInputController.sendScrollEvent(token, event); 484 } finally { 485 Binder.restoreCallingIdentity(binderToken); 486 } 487 } 488 489 @Override // Binder call 490 public PointF getCursorPosition(IBinder token) { 491 final long binderToken = Binder.clearCallingIdentity(); 492 try { 493 return mInputController.getCursorPosition(token); 494 } finally { 495 Binder.restoreCallingIdentity(binderToken); 496 } 497 } 498 499 @Override // Binder call 500 public void setShowPointerIcon(boolean showPointerIcon) { 501 mContext.enforceCallingOrSelfPermission( 502 android.Manifest.permission.CREATE_VIRTUAL_DEVICE, 503 "Permission required to unregister this input device"); 504 505 final long binderToken = Binder.clearCallingIdentity(); 506 try { 507 synchronized (mVirtualDeviceLock) { 508 mDefaultShowPointerIcon = showPointerIcon; 509 for (int displayId : mVirtualDisplayIds) { 510 mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); 511 } 512 } 513 } finally { 514 Binder.restoreCallingIdentity(binderToken); 515 } 516 } 517 518 @Override 519 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 520 fout.println(" VirtualDevice: "); 521 fout.println(" mAssociationId: " + mAssociationInfo.getId()); 522 fout.println(" mParams: " + mParams); 523 fout.println(" mVirtualDisplayIds: "); 524 synchronized (mVirtualDeviceLock) { 525 for (int id : mVirtualDisplayIds) { 526 fout.println(" " + id); 527 } 528 fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon); 529 } 530 mInputController.dump(fout); 531 } 532 533 GenericWindowPolicyController createWindowPolicyController() { 534 synchronized (mVirtualDeviceLock) { 535 final GenericWindowPolicyController gwpc = 536 new GenericWindowPolicyController(FLAG_SECURE, 537 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, 538 getAllowedUserHandles(), 539 mParams.getAllowedCrossTaskNavigations(), 540 mParams.getBlockedCrossTaskNavigations(), 541 mParams.getAllowedActivities(), 542 mParams.getBlockedActivities(), 543 mParams.getDefaultActivityPolicy(), 544 createListenerAdapter(), 545 this::onActivityBlocked, 546 this::onSecureWindowShown, 547 mAssociationInfo.getDeviceProfile()); 548 gwpc.registerRunningAppsChangedListener(/* listener= */ this); 549 return gwpc; 550 } 551 } 552 553 void onVirtualDisplayCreatedLocked(GenericWindowPolicyController gwpc, int displayId) { 554 synchronized (mVirtualDeviceLock) { 555 if (displayId == Display.INVALID_DISPLAY) { 556 return; 557 } 558 if (mVirtualDisplayIds.contains(displayId)) { 559 throw new IllegalStateException( 560 "Virtual device already has a virtual display with ID " + displayId); 561 } 562 mVirtualDisplayIds.add(displayId); 563 564 gwpc.setDisplayId(displayId); 565 mWindowPolicyControllers.put(displayId, gwpc); 566 567 mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); 568 mInputController.setPointerAcceleration(1f, displayId); 569 mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, 570 displayId); 571 mInputController.setLocalIme(displayId); 572 573 574 if (mPerDisplayWakelocks.containsKey(displayId)) { 575 Slog.e(TAG, "Not creating wakelock for displayId " + displayId); 576 return; 577 } 578 PowerManager powerManager = mContext.getSystemService(PowerManager.class); 579 PowerManager.WakeLock wakeLock = powerManager.newWakeLock( 580 PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 581 TAG + ":" + displayId, displayId); 582 mPerDisplayWakelocks.put(displayId, wakeLock); 583 wakeLock.acquire(); 584 } 585 } 586 587 private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { 588 Intent intent = BlockedAppStreamingActivity.createIntent( 589 activityInfo, mAssociationInfo.getDisplayName()); 590 mContext.startActivityAsUser( 591 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), 592 ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), 593 mContext.getUser()); 594 } 595 596 private void onSecureWindowShown(int displayId, int uid) { 597 if (!mVirtualDisplayIds.contains(displayId)) { 598 return; 599 } 600 601 // If a virtual display isn't secure, the screen can't be captured. Show a warning toast 602 // if the secure window is shown on a non-secure virtual display. 603 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 604 Display display = displayManager.getDisplay(displayId); 605 if ((display.getFlags() & FLAG_SECURE) == 0) { 606 showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window, 607 Toast.LENGTH_LONG, mContext.getMainLooper()); 608 } 609 } 610 611 private ArraySet<UserHandle> getAllowedUserHandles() { 612 ArraySet<UserHandle> result = new ArraySet<>(); 613 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 614 UserManager userManager = mContext.getSystemService(UserManager.class); 615 for (UserHandle profile : userManager.getAllProfiles()) { 616 int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(profile.getIdentifier()); 617 if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED 618 || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) { 619 result.add(profile); 620 } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) { 621 if (mParams.getUsersWithMatchingAccounts().contains(profile)) { 622 result.add(profile); 623 } 624 } 625 } 626 return result; 627 } 628 629 void onVirtualDisplayRemovedLocked(int displayId) { 630 synchronized (mVirtualDeviceLock) { 631 if (!mVirtualDisplayIds.contains(displayId)) { 632 throw new IllegalStateException( 633 "Virtual device doesn't have a virtual display with ID " + displayId); 634 } 635 PowerManager.WakeLock wakeLock = mPerDisplayWakelocks.get(displayId); 636 if (wakeLock != null) { 637 wakeLock.release(); 638 mPerDisplayWakelocks.remove(displayId); 639 } 640 GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId); 641 if (gwpc != null) { 642 gwpc.unregisterRunningAppsChangedListener(/* listener= */ this); 643 } 644 mVirtualDisplayIds.remove(displayId); 645 mWindowPolicyControllers.remove(displayId); 646 } 647 } 648 649 int getOwnerUid() { 650 return mOwnerUid; 651 } 652 653 /** 654 * Returns true if an app with the given {@code uid} is currently running on this virtual 655 * device. 656 */ 657 boolean isAppRunningOnVirtualDevice(int uid) { 658 final int size = mWindowPolicyControllers.size(); 659 for (int i = 0; i < size; i++) { 660 if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) { 661 return true; 662 } 663 } 664 return false; 665 } 666 667 /** 668 * Shows a toast on virtual displays owned by this device which have a given uid running. 669 */ 670 void showToastWhereUidIsRunning(int uid, @StringRes int resId, @Toast.Duration int duration, 671 Looper looper) { 672 showToastWhereUidIsRunning(uid, mContext.getString(resId), duration, looper); 673 } 674 675 /** 676 * Shows a toast on virtual displays owned by this device which have a given uid running. 677 */ 678 void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration, 679 Looper looper) { 680 synchronized (mVirtualDeviceLock) { 681 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 682 final int size = mWindowPolicyControllers.size(); 683 for (int i = 0; i < size; i++) { 684 if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) { 685 int displayId = mWindowPolicyControllers.keyAt(i); 686 Display display = displayManager.getDisplay(displayId); 687 if (display != null && display.isValid()) { 688 Toast.makeText(mContext.createDisplayContext(display), looper, text, 689 duration).show(); 690 } 691 } 692 } 693 } 694 } 695 696 boolean isDisplayOwnedByVirtualDevice(int displayId) { 697 return mVirtualDisplayIds.contains(displayId); 698 } 699 700 interface OnDeviceCloseListener { 701 void onClose(int associationId); 702 } 703 704 interface PendingTrampolineCallback { 705 /** 706 * Called when the callback should start waiting for the given pending trampoline. 707 * Implementations should try to listen for activity starts associated with the given 708 * {@code pendingTrampoline}, and launch the activity on the display with 709 * {@link PendingTrampoline#mDisplayId}. 710 */ 711 void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline); 712 713 /** 714 * Called when the callback should stop waiting for the given pending trampoline. This can 715 * happen, for example, when the pending intent failed to send. 716 */ 717 void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline); 718 } 719 720 /** 721 * A data class storing a pending trampoline this device is expecting. 722 */ 723 static class PendingTrampoline { 724 725 /** 726 * The original pending intent sent, for which a trampoline activity launch is expected. 727 */ 728 final PendingIntent mPendingIntent; 729 730 /** 731 * The result receiver associated with this pending call. {@link Activity#RESULT_OK} will 732 * be sent to the receiver if the trampoline activity was captured successfully. 733 * {@link Activity#RESULT_CANCELED} is sent otherwise. 734 */ 735 final ResultReceiver mResultReceiver; 736 737 /** 738 * The display ID to send the captured trampoline activity launch to. 739 */ 740 final int mDisplayId; 741 742 private PendingTrampoline(PendingIntent pendingIntent, ResultReceiver resultReceiver, 743 int displayId) { 744 mPendingIntent = pendingIntent; 745 mResultReceiver = resultReceiver; 746 mDisplayId = displayId; 747 } 748 749 @Override 750 public String toString() { 751 return "PendingTrampoline{" 752 + "pendingIntent=" + mPendingIntent 753 + ", resultReceiver=" + mResultReceiver 754 + ", displayId=" + mDisplayId + "}"; 755 } 756 } 757 } 758