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.EnforcePermission; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.StringRes; 29 import android.annotation.UserIdInt; 30 import android.app.Activity; 31 import android.app.ActivityOptions; 32 import android.app.PendingIntent; 33 import android.app.admin.DevicePolicyManager; 34 import android.companion.AssociationInfo; 35 import android.companion.virtual.IVirtualDevice; 36 import android.companion.virtual.IVirtualDeviceActivityListener; 37 import android.companion.virtual.IVirtualDeviceIntentInterceptor; 38 import android.companion.virtual.IVirtualDeviceSoundEffectListener; 39 import android.companion.virtual.VirtualDeviceManager; 40 import android.companion.virtual.VirtualDeviceManager.ActivityListener; 41 import android.companion.virtual.VirtualDeviceParams; 42 import android.companion.virtual.audio.IAudioConfigChangedCallback; 43 import android.companion.virtual.audio.IAudioRoutingCallback; 44 import android.companion.virtual.sensor.VirtualSensor; 45 import android.companion.virtual.sensor.VirtualSensorConfig; 46 import android.companion.virtual.sensor.VirtualSensorEvent; 47 import android.content.ComponentName; 48 import android.content.Context; 49 import android.content.Intent; 50 import android.content.IntentFilter; 51 import android.content.pm.ActivityInfo; 52 import android.graphics.PointF; 53 import android.hardware.display.DisplayManager; 54 import android.hardware.display.DisplayManagerGlobal; 55 import android.hardware.display.DisplayManagerInternal; 56 import android.hardware.display.IVirtualDisplayCallback; 57 import android.hardware.display.VirtualDisplayConfig; 58 import android.hardware.input.VirtualDpadConfig; 59 import android.hardware.input.VirtualKeyEvent; 60 import android.hardware.input.VirtualKeyboardConfig; 61 import android.hardware.input.VirtualMouseButtonEvent; 62 import android.hardware.input.VirtualMouseConfig; 63 import android.hardware.input.VirtualMouseRelativeEvent; 64 import android.hardware.input.VirtualMouseScrollEvent; 65 import android.hardware.input.VirtualNavigationTouchpadConfig; 66 import android.hardware.input.VirtualTouchEvent; 67 import android.hardware.input.VirtualTouchscreenConfig; 68 import android.os.Binder; 69 import android.os.IBinder; 70 import android.os.LocaleList; 71 import android.os.Looper; 72 import android.os.PermissionEnforcer; 73 import android.os.PowerManager; 74 import android.os.RemoteException; 75 import android.os.ResultReceiver; 76 import android.os.UserHandle; 77 import android.os.UserManager; 78 import android.util.ArrayMap; 79 import android.util.ArraySet; 80 import android.util.Slog; 81 import android.util.SparseArray; 82 import android.view.Display; 83 import android.view.WindowManager; 84 import android.widget.Toast; 85 86 import com.android.internal.annotations.GuardedBy; 87 import com.android.internal.annotations.VisibleForTesting; 88 import com.android.internal.app.BlockedAppStreamingActivity; 89 import com.android.server.LocalServices; 90 import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; 91 import com.android.server.companion.virtual.audio.VirtualAudioController; 92 93 import java.io.FileDescriptor; 94 import java.io.PrintWriter; 95 import java.util.ArrayList; 96 import java.util.Collections; 97 import java.util.List; 98 import java.util.Map; 99 import java.util.Objects; 100 import java.util.Set; 101 import java.util.function.Consumer; 102 103 104 final class VirtualDeviceImpl extends IVirtualDevice.Stub 105 implements IBinder.DeathRecipient, RunningAppsChangedListener { 106 107 private static final String TAG = "VirtualDeviceImpl"; 108 109 private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = 110 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC 111 | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT 112 | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY 113 | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL 114 | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH 115 | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; 116 117 /** 118 * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. 119 */ 120 private static final long PENDING_TRAMPOLINE_TIMEOUT_MS = 5000; 121 122 private final Object mVirtualDeviceLock = new Object(); 123 124 private final Context mContext; 125 private final AssociationInfo mAssociationInfo; 126 private final VirtualDeviceManagerService mService; 127 private final PendingTrampolineCallback mPendingTrampolineCallback; 128 private final int mOwnerUid; 129 private int mDeviceId; 130 // Thou shall not hold the mVirtualDeviceLock over the mInputController calls. 131 // Holding the lock can lead to lock inversion with GlobalWindowManagerLock. 132 // 1. After display is created the window manager calls into VDM during construction 133 // of display specific context to fetch device id corresponding to the display. 134 // mVirtualDeviceLock will be held while this is done. 135 // 2. InputController interactions result in calls to DisplayManager (to set IME, 136 // possibly more indirect calls), and those attempt to lock GlobalWindowManagerLock which 137 // creates lock inversion. 138 private final InputController mInputController; 139 private final SensorController mSensorController; 140 private final CameraAccessController mCameraAccessController; 141 private VirtualAudioController mVirtualAudioController; 142 private final IBinder mAppToken; 143 private final VirtualDeviceParams mParams; 144 @GuardedBy("mVirtualDeviceLock") 145 private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>(); 146 private final IVirtualDeviceActivityListener mActivityListener; 147 private final IVirtualDeviceSoundEffectListener mSoundEffectListener; 148 private final DisplayManagerGlobal mDisplayManager; 149 @GuardedBy("mVirtualDeviceLock") 150 private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>(); 151 @NonNull 152 private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; 153 // The default setting for showing the pointer on new displays. 154 @GuardedBy("mVirtualDeviceLock") 155 private boolean mDefaultShowPointerIcon = true; 156 @GuardedBy("mVirtualDeviceLock") 157 @Nullable 158 private LocaleList mLocaleList = null; 159 // This device's sensors, keyed by sensor handle. 160 @GuardedBy("mVirtualDeviceLock") 161 private SparseArray<VirtualSensor> mVirtualSensors = new SparseArray<>(); 162 @GuardedBy("mVirtualDeviceLock") 163 private List<VirtualSensor> mVirtualSensorList = null; 164 createListenerAdapter()165 private ActivityListener createListenerAdapter() { 166 return new ActivityListener() { 167 168 @Override 169 public void onTopActivityChanged(int displayId, ComponentName topActivity) { 170 try { 171 mActivityListener.onTopActivityChanged(displayId, topActivity, 172 UserHandle.USER_NULL); 173 } catch (RemoteException e) { 174 Slog.w(TAG, "Unable to call mActivityListener", e); 175 } 176 } 177 178 @Override 179 public void onTopActivityChanged(int displayId, ComponentName topActivity, 180 @UserIdInt int userId) { 181 try { 182 mActivityListener.onTopActivityChanged(displayId, topActivity, userId); 183 } catch (RemoteException e) { 184 Slog.w(TAG, "Unable to call mActivityListener", e); 185 } 186 } 187 188 @Override 189 public void onDisplayEmpty(int displayId) { 190 try { 191 mActivityListener.onDisplayEmpty(displayId); 192 } catch (RemoteException e) { 193 Slog.w(TAG, "Unable to call mActivityListener", e); 194 } 195 } 196 }; 197 } 198 199 VirtualDeviceImpl( 200 Context context, 201 AssociationInfo associationInfo, 202 VirtualDeviceManagerService service, 203 IBinder token, 204 int ownerUid, 205 int deviceId, 206 CameraAccessController cameraAccessController, 207 PendingTrampolineCallback pendingTrampolineCallback, 208 IVirtualDeviceActivityListener activityListener, 209 IVirtualDeviceSoundEffectListener soundEffectListener, 210 Consumer<ArraySet<Integer>> runningAppsChangedCallback, 211 VirtualDeviceParams params) { 212 this( 213 context, 214 associationInfo, 215 service, 216 token, 217 ownerUid, 218 deviceId, 219 /* inputController= */ null, 220 /* sensorController= */ null, 221 cameraAccessController, 222 pendingTrampolineCallback, 223 activityListener, 224 soundEffectListener, 225 runningAppsChangedCallback, 226 params, 227 DisplayManagerGlobal.getInstance()); 228 } 229 230 @VisibleForTesting 231 VirtualDeviceImpl( 232 Context context, 233 AssociationInfo associationInfo, 234 VirtualDeviceManagerService service, 235 IBinder token, 236 int ownerUid, 237 int deviceId, 238 InputController inputController, 239 SensorController sensorController, 240 CameraAccessController cameraAccessController, 241 PendingTrampolineCallback pendingTrampolineCallback, 242 IVirtualDeviceActivityListener activityListener, 243 IVirtualDeviceSoundEffectListener soundEffectListener, 244 Consumer<ArraySet<Integer>> runningAppsChangedCallback, 245 VirtualDeviceParams params, 246 DisplayManagerGlobal displayManager) { 247 super(PermissionEnforcer.fromContext(context)); 248 UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); 249 mContext = context.createContextAsUser(ownerUserHandle, 0); 250 mAssociationInfo = associationInfo; 251 mService = service; 252 mPendingTrampolineCallback = pendingTrampolineCallback; 253 mActivityListener = activityListener; 254 mSoundEffectListener = soundEffectListener; 255 mRunningAppsChangedCallback = runningAppsChangedCallback; 256 mOwnerUid = ownerUid; 257 mDeviceId = deviceId; 258 mAppToken = token; 259 mParams = params; 260 mDisplayManager = displayManager; 261 if (inputController == null) { 262 mInputController = new InputController( 263 context.getMainThreadHandler(), 264 context.getSystemService(WindowManager.class)); 265 } else { 266 mInputController = inputController; 267 } 268 if (sensorController == null) { 269 mSensorController = new SensorController(mDeviceId, mParams.getVirtualSensorCallback()); 270 } else { 271 mSensorController = sensorController; 272 } 273 final List<VirtualSensorConfig> virtualSensorConfigs = mParams.getVirtualSensorConfigs(); 274 for (int i = 0; i < virtualSensorConfigs.size(); ++i) { 275 createVirtualSensor(virtualSensorConfigs.get(i)); 276 } 277 mCameraAccessController = cameraAccessController; 278 mCameraAccessController.startObservingIfNeeded(); 279 try { 280 token.linkToDeath(this, 0); 281 } catch (RemoteException e) { 282 throw e.rethrowFromSystemServer(); 283 } 284 } 285 286 /** 287 * Returns the flags that should be added to any virtual displays created on this virtual 288 * device. 289 */ 290 int getBaseVirtualDisplayFlags() { 291 int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS; 292 if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) { 293 flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; 294 } 295 return flags; 296 } 297 298 /** Returns the camera access controller of this device. */ 299 CameraAccessController getCameraAccessController() { 300 return mCameraAccessController; 301 } 302 303 /** Returns the device display name. */ 304 CharSequence getDisplayName() { 305 return mAssociationInfo.getDisplayName(); 306 } 307 308 /** Returns the optional name of the device. */ 309 String getDeviceName() { 310 return mParams.getName(); 311 } 312 313 /** Returns the locale of the device. */ 314 LocaleList getDeviceLocaleList() { 315 synchronized (mVirtualDeviceLock) { 316 return mLocaleList; 317 } 318 } 319 320 /** Returns the policy specified for this policy type */ 321 public @VirtualDeviceParams.DevicePolicy int getDevicePolicy( 322 @VirtualDeviceParams.PolicyType int policyType) { 323 return mParams.getDevicePolicy(policyType); 324 } 325 326 /** Returns device-specific audio session id for playback. */ 327 public int getAudioPlaybackSessionId() { 328 return mParams.getAudioPlaybackSessionId(); 329 } 330 331 /** Returns device-specific audio session id for recording. */ 332 public int getAudioRecordingSessionId() { 333 return mParams.getAudioRecordingSessionId(); 334 } 335 336 /** Returns the unique device ID of this device. */ 337 @Override // Binder call 338 public int getDeviceId() { 339 return mDeviceId; 340 } 341 342 @Override // Binder call 343 public int getAssociationId() { 344 return mAssociationInfo.getId(); 345 } 346 347 @Override // Binder call 348 public void launchPendingIntent(int displayId, PendingIntent pendingIntent, 349 ResultReceiver resultReceiver) { 350 Objects.requireNonNull(pendingIntent); 351 synchronized (mVirtualDeviceLock) { 352 if (!mVirtualDisplays.contains(displayId)) { 353 throw new SecurityException("Display ID " + displayId 354 + " not found for this virtual device"); 355 } 356 } 357 if (pendingIntent.isActivity()) { 358 try { 359 sendPendingIntent(displayId, pendingIntent); 360 resultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); 361 } catch (PendingIntent.CanceledException e) { 362 Slog.w(TAG, "Pending intent canceled", e); 363 resultReceiver.send( 364 VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); 365 } 366 } else { 367 PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent, 368 resultReceiver, displayId); 369 mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline); 370 mContext.getMainThreadHandler().postDelayed(() -> { 371 pendingTrampoline.mResultReceiver.send( 372 VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); 373 mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); 374 }, PENDING_TRAMPOLINE_TIMEOUT_MS); 375 try { 376 sendPendingIntent(displayId, pendingIntent); 377 } catch (PendingIntent.CanceledException e) { 378 Slog.w(TAG, "Pending intent canceled", e); 379 resultReceiver.send( 380 VirtualDeviceManager.LAUNCH_FAILURE_PENDING_INTENT_CANCELED, null); 381 mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline); 382 } 383 } 384 } 385 386 private void sendPendingIntent(int displayId, PendingIntent pendingIntent) 387 throws PendingIntent.CanceledException { 388 final ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId); 389 options.setPendingIntentBackgroundActivityLaunchAllowed(true); 390 options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); 391 pendingIntent.send( 392 mContext, 393 /* code= */ 0, 394 /* intent= */ null, 395 /* onFinished= */ null, 396 /* handler= */ null, 397 /* requiredPermission= */ null, 398 options.toBundle()); 399 } 400 401 @Override // Binder call 402 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 403 public void close() { 404 super.close_enforcePermission(); 405 // Remove about-to-be-closed virtual device from the service before butchering it. 406 boolean removed = mService.removeVirtualDevice(mDeviceId); 407 mDeviceId = Context.DEVICE_ID_INVALID; 408 409 // Device is already closed. 410 if (!removed) { 411 return; 412 } 413 414 final long ident = Binder.clearCallingIdentity(); 415 try { 416 VirtualDisplayWrapper[] virtualDisplaysToBeReleased; 417 synchronized (mVirtualDeviceLock) { 418 if (mVirtualAudioController != null) { 419 mVirtualAudioController.stopListening(); 420 mVirtualAudioController = null; 421 } 422 mLocaleList = null; 423 virtualDisplaysToBeReleased = new VirtualDisplayWrapper[mVirtualDisplays.size()]; 424 for (int i = 0; i < mVirtualDisplays.size(); i++) { 425 virtualDisplaysToBeReleased[i] = mVirtualDisplays.valueAt(i); 426 } 427 mVirtualDisplays.clear(); 428 mVirtualSensorList = null; 429 mVirtualSensors.clear(); 430 } 431 // Destroy the display outside locked section. 432 for (VirtualDisplayWrapper virtualDisplayWrapper : virtualDisplaysToBeReleased) { 433 mDisplayManager.releaseVirtualDisplay(virtualDisplayWrapper.getToken()); 434 // The releaseVirtualDisplay call above won't trigger 435 // VirtualDeviceImpl.onVirtualDisplayRemoved callback because we already removed the 436 // virtual device from the service - we release the other display-tied resources 437 // here with the guarantee it will be done exactly once. 438 releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); 439 } 440 441 mAppToken.unlinkToDeath(this, 0); 442 mCameraAccessController.stopObservingIfNeeded(); 443 444 mInputController.close(); 445 mSensorController.close(); 446 } finally { 447 Binder.restoreCallingIdentity(ident); 448 } 449 } 450 451 @Override 452 public void binderDied() { 453 close(); 454 } 455 456 @Override 457 public void onRunningAppsChanged(ArraySet<Integer> runningUids) { 458 mCameraAccessController.blockCameraAccessIfNeeded(runningUids); 459 mRunningAppsChangedCallback.accept(runningUids); 460 } 461 462 @VisibleForTesting 463 VirtualAudioController getVirtualAudioControllerForTesting() { 464 return mVirtualAudioController; 465 } 466 467 @Override // Binder call 468 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 469 public void onAudioSessionStarting(int displayId, 470 @NonNull IAudioRoutingCallback routingCallback, 471 @Nullable IAudioConfigChangedCallback configChangedCallback) { 472 super.onAudioSessionStarting_enforcePermission(); 473 synchronized (mVirtualDeviceLock) { 474 if (!mVirtualDisplays.contains(displayId)) { 475 throw new SecurityException( 476 "Cannot start audio session for a display not associated with this virtual " 477 + "device"); 478 } 479 480 if (mVirtualAudioController == null) { 481 mVirtualAudioController = new VirtualAudioController(mContext); 482 GenericWindowPolicyController gwpc = mVirtualDisplays.get( 483 displayId).getWindowPolicyController(); 484 mVirtualAudioController.startListening(gwpc, routingCallback, 485 configChangedCallback); 486 } 487 } 488 } 489 490 @Override // Binder call 491 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 492 public void onAudioSessionEnded() { 493 super.onAudioSessionEnded_enforcePermission(); 494 synchronized (mVirtualDeviceLock) { 495 if (mVirtualAudioController != null) { 496 mVirtualAudioController.stopListening(); 497 mVirtualAudioController = null; 498 } 499 } 500 } 501 502 @Override // Binder call 503 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 504 public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) { 505 super.createVirtualDpad_enforcePermission(); 506 Objects.requireNonNull(config); 507 synchronized (mVirtualDeviceLock) { 508 if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { 509 throw new SecurityException( 510 "Cannot create a virtual dpad for a display not associated with " 511 + "this virtual device"); 512 } 513 } 514 final long ident = Binder.clearCallingIdentity(); 515 try { 516 mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(), 517 config.getProductId(), deviceToken, config.getAssociatedDisplayId()); 518 } finally { 519 Binder.restoreCallingIdentity(ident); 520 } 521 } 522 523 @Override // Binder call 524 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 525 public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) { 526 super.createVirtualKeyboard_enforcePermission(); 527 Objects.requireNonNull(config); 528 synchronized (mVirtualDeviceLock) { 529 if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { 530 throw new SecurityException( 531 "Cannot create a virtual keyboard for a display not associated with " 532 + "this virtual device"); 533 } 534 mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag()); 535 } 536 final long ident = Binder.clearCallingIdentity(); 537 try { 538 mInputController.createKeyboard(config.getInputDeviceName(), config.getVendorId(), 539 config.getProductId(), deviceToken, config.getAssociatedDisplayId(), 540 config.getLanguageTag(), config.getLayoutType()); 541 } finally { 542 Binder.restoreCallingIdentity(ident); 543 } 544 } 545 546 @Override // Binder call 547 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 548 public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) { 549 super.createVirtualMouse_enforcePermission(); 550 Objects.requireNonNull(config); 551 synchronized (mVirtualDeviceLock) { 552 if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { 553 throw new SecurityException( 554 "Cannot create a virtual mouse for a display not associated with this " 555 + "virtual device"); 556 } 557 } 558 final long ident = Binder.clearCallingIdentity(); 559 try { 560 mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(), 561 config.getProductId(), deviceToken, config.getAssociatedDisplayId()); 562 } finally { 563 Binder.restoreCallingIdentity(ident); 564 } 565 } 566 567 @Override // Binder call 568 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 569 public void createVirtualTouchscreen(VirtualTouchscreenConfig config, 570 @NonNull IBinder deviceToken) { 571 super.createVirtualTouchscreen_enforcePermission(); 572 Objects.requireNonNull(config); 573 synchronized (mVirtualDeviceLock) { 574 if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { 575 throw new SecurityException( 576 "Cannot create a virtual touchscreen for a display not associated with " 577 + "this virtual device"); 578 } 579 } 580 int screenHeight = config.getHeight(); 581 int screenWidth = config.getWidth(); 582 if (screenHeight <= 0 || screenWidth <= 0) { 583 throw new IllegalArgumentException( 584 "Cannot create a virtual touchscreen, screen dimensions must be positive. Got: " 585 + "(" + screenWidth + ", " + screenHeight + ")"); 586 } 587 588 final long ident = Binder.clearCallingIdentity(); 589 try { 590 mInputController.createTouchscreen(config.getInputDeviceName(), config.getVendorId(), 591 config.getProductId(), deviceToken, config.getAssociatedDisplayId(), 592 screenHeight, screenWidth); 593 } finally { 594 Binder.restoreCallingIdentity(ident); 595 } 596 } 597 598 @Override // Binder call 599 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 600 public void createVirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config, 601 @NonNull IBinder deviceToken) { 602 super.createVirtualNavigationTouchpad_enforcePermission(); 603 Objects.requireNonNull(config); 604 synchronized (mVirtualDeviceLock) { 605 if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { 606 throw new SecurityException( 607 "Cannot create a virtual navigation touchpad for a display not associated " 608 + "with this virtual device"); 609 } 610 } 611 int touchpadHeight = config.getHeight(); 612 int touchpadWidth = config.getWidth(); 613 if (touchpadHeight <= 0 || touchpadWidth <= 0) { 614 throw new IllegalArgumentException( 615 "Cannot create a virtual navigation touchpad, touchpad dimensions must be positive." 616 + " Got: (" + touchpadHeight + ", " + touchpadWidth + ")"); 617 } 618 619 final long ident = Binder.clearCallingIdentity(); 620 try { 621 mInputController.createNavigationTouchpad( 622 config.getInputDeviceName(), config.getVendorId(), 623 config.getProductId(), deviceToken, config.getAssociatedDisplayId(), 624 touchpadHeight, touchpadWidth); 625 } finally { 626 Binder.restoreCallingIdentity(ident); 627 } 628 } 629 630 @Override // Binder call 631 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 632 public void unregisterInputDevice(IBinder token) { 633 super.unregisterInputDevice_enforcePermission(); 634 final long ident = Binder.clearCallingIdentity(); 635 try { 636 mInputController.unregisterInputDevice(token); 637 } finally { 638 Binder.restoreCallingIdentity(ident); 639 } 640 } 641 642 @Override // Binder call 643 public int getInputDeviceId(IBinder token) { 644 final long ident = Binder.clearCallingIdentity(); 645 try { 646 return mInputController.getInputDeviceId(token); 647 } finally { 648 Binder.restoreCallingIdentity(ident); 649 } 650 } 651 652 653 @Override // Binder call 654 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 655 public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) { 656 super.sendDpadKeyEvent_enforcePermission(); 657 final long ident = Binder.clearCallingIdentity(); 658 try { 659 return mInputController.sendDpadKeyEvent(token, event); 660 } finally { 661 Binder.restoreCallingIdentity(ident); 662 } 663 } 664 665 @Override // Binder call 666 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 667 public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) { 668 super.sendKeyEvent_enforcePermission(); 669 final long ident = Binder.clearCallingIdentity(); 670 try { 671 return mInputController.sendKeyEvent(token, event); 672 } finally { 673 Binder.restoreCallingIdentity(ident); 674 } 675 } 676 677 @Override // Binder call 678 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 679 public boolean sendButtonEvent(IBinder token, VirtualMouseButtonEvent event) { 680 super.sendButtonEvent_enforcePermission(); 681 final long ident = Binder.clearCallingIdentity(); 682 try { 683 return mInputController.sendButtonEvent(token, event); 684 } finally { 685 Binder.restoreCallingIdentity(ident); 686 } 687 } 688 689 @Override // Binder call 690 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 691 public boolean sendTouchEvent(IBinder token, VirtualTouchEvent event) { 692 super.sendTouchEvent_enforcePermission(); 693 final long ident = Binder.clearCallingIdentity(); 694 try { 695 return mInputController.sendTouchEvent(token, event); 696 } finally { 697 Binder.restoreCallingIdentity(ident); 698 } 699 } 700 701 @Override // Binder call 702 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 703 public boolean sendRelativeEvent(IBinder token, VirtualMouseRelativeEvent event) { 704 super.sendRelativeEvent_enforcePermission(); 705 final long ident = Binder.clearCallingIdentity(); 706 try { 707 return mInputController.sendRelativeEvent(token, event); 708 } finally { 709 Binder.restoreCallingIdentity(ident); 710 } 711 } 712 713 @Override // Binder call 714 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 715 public boolean sendScrollEvent(IBinder token, VirtualMouseScrollEvent event) { 716 super.sendScrollEvent_enforcePermission(); 717 final long ident = Binder.clearCallingIdentity(); 718 try { 719 return mInputController.sendScrollEvent(token, event); 720 } finally { 721 Binder.restoreCallingIdentity(ident); 722 } 723 } 724 725 @Override // Binder call 726 public PointF getCursorPosition(IBinder token) { 727 final long ident = Binder.clearCallingIdentity(); 728 try { 729 return mInputController.getCursorPosition(token); 730 } finally { 731 Binder.restoreCallingIdentity(ident); 732 } 733 } 734 735 @Override // Binder call 736 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 737 public void setShowPointerIcon(boolean showPointerIcon) { 738 super.setShowPointerIcon_enforcePermission(); 739 final long ident = Binder.clearCallingIdentity(); 740 try { 741 synchronized (mVirtualDeviceLock) { 742 mDefaultShowPointerIcon = showPointerIcon; 743 for (int i = 0; i < mVirtualDisplays.size(); i++) { 744 final int displayId = mVirtualDisplays.keyAt(i); 745 mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); 746 } 747 } 748 } finally { 749 Binder.restoreCallingIdentity(ident); 750 } 751 } 752 753 private void createVirtualSensor(@NonNull VirtualSensorConfig config) { 754 final IBinder sensorToken = 755 new Binder("android.hardware.sensor.VirtualSensor:" + config.getName()); 756 final long ident = Binder.clearCallingIdentity(); 757 try { 758 int handle = mSensorController.createSensor(sensorToken, config); 759 VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(), 760 this, sensorToken); 761 synchronized (mVirtualDeviceLock) { 762 mVirtualSensors.put(handle, sensor); 763 } 764 } finally { 765 Binder.restoreCallingIdentity(ident); 766 } 767 } 768 769 @Override // Binder call 770 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 771 @Nullable 772 public List<VirtualSensor> getVirtualSensorList() { 773 super.getVirtualSensorList_enforcePermission(); 774 synchronized (mVirtualDeviceLock) { 775 if (mVirtualSensorList == null) { 776 mVirtualSensorList = new ArrayList<>(); 777 for (int i = 0; i < mVirtualSensors.size(); ++i) { 778 mVirtualSensorList.add(mVirtualSensors.valueAt(i)); 779 } 780 mVirtualSensorList = Collections.unmodifiableList(mVirtualSensorList); 781 } 782 return mVirtualSensorList; 783 } 784 } 785 786 VirtualSensor getVirtualSensorByHandle(int handle) { 787 synchronized (mVirtualDeviceLock) { 788 return mVirtualSensors.get(handle); 789 } 790 } 791 792 @Override // Binder call 793 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 794 public boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) { 795 super.sendSensorEvent_enforcePermission(); 796 final long ident = Binder.clearCallingIdentity(); 797 try { 798 return mSensorController.sendSensorEvent(token, event); 799 } finally { 800 Binder.restoreCallingIdentity(ident); 801 } 802 } 803 804 @Override // Binder call 805 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 806 public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor, 807 IntentFilter filter) { 808 super.registerIntentInterceptor_enforcePermission(); 809 Objects.requireNonNull(intentInterceptor); 810 Objects.requireNonNull(filter); 811 synchronized (mVirtualDeviceLock) { 812 mIntentInterceptors.put(intentInterceptor.asBinder(), filter); 813 } 814 } 815 816 @Override // Binder call 817 @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) 818 public void unregisterIntentInterceptor( 819 @NonNull IVirtualDeviceIntentInterceptor intentInterceptor) { 820 super.unregisterIntentInterceptor_enforcePermission(); 821 Objects.requireNonNull(intentInterceptor); 822 synchronized (mVirtualDeviceLock) { 823 mIntentInterceptors.remove(intentInterceptor.asBinder()); 824 } 825 } 826 827 @Override 828 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 829 fout.println(" VirtualDevice: "); 830 fout.println(" mDeviceId: " + mDeviceId); 831 fout.println(" mAssociationId: " + mAssociationInfo.getId()); 832 fout.println(" mParams: " + mParams); 833 fout.println(" mVirtualDisplayIds: "); 834 synchronized (mVirtualDeviceLock) { 835 for (int i = 0; i < mVirtualDisplays.size(); i++) { 836 fout.println(" " + mVirtualDisplays.keyAt(i)); 837 } 838 fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon); 839 } 840 mInputController.dump(fout); 841 mSensorController.dump(fout); 842 } 843 844 private GenericWindowPolicyController createWindowPolicyController( 845 @NonNull Set<String> displayCategories) { 846 final GenericWindowPolicyController gwpc = 847 new GenericWindowPolicyController(FLAG_SECURE, 848 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, 849 getAllowedUserHandles(), 850 mParams.getAllowedCrossTaskNavigations(), 851 mParams.getBlockedCrossTaskNavigations(), 852 mParams.getAllowedActivities(), 853 mParams.getBlockedActivities(), 854 mParams.getDefaultActivityPolicy(), 855 createListenerAdapter(), 856 this::onEnteringPipBlocked, 857 this::onActivityBlocked, 858 this::onSecureWindowShown, 859 this::shouldInterceptIntent, 860 displayCategories, 861 mParams.getDevicePolicy( 862 VirtualDeviceParams.POLICY_TYPE_RECENTS) 863 == VirtualDeviceParams.DEVICE_POLICY_DEFAULT); 864 gwpc.registerRunningAppsChangedListener(/* listener= */ this); 865 return gwpc; 866 } 867 868 int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig, 869 @NonNull IVirtualDisplayCallback callback, String packageName) { 870 GenericWindowPolicyController gwpc = createWindowPolicyController( 871 virtualDisplayConfig.getDisplayCategories()); 872 DisplayManagerInternal displayManager = LocalServices.getService( 873 DisplayManagerInternal.class); 874 int displayId; 875 displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback, 876 this, gwpc, packageName); 877 gwpc.setDisplayId(displayId); 878 879 synchronized (mVirtualDeviceLock) { 880 if (mVirtualDisplays.contains(displayId)) { 881 gwpc.unregisterRunningAppsChangedListener(this); 882 throw new IllegalStateException( 883 "Virtual device already has a virtual display with ID " + displayId); 884 } 885 886 PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId); 887 mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock)); 888 } 889 890 final long token = Binder.clearCallingIdentity(); 891 try { 892 mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); 893 mInputController.setPointerAcceleration(1f, displayId); 894 mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, 895 displayId); 896 mInputController.setLocalIme(displayId); 897 } finally { 898 Binder.restoreCallingIdentity(token); 899 } 900 901 return displayId; 902 } 903 904 private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) { 905 final long token = Binder.clearCallingIdentity(); 906 try { 907 PowerManager powerManager = mContext.getSystemService(PowerManager.class); 908 PowerManager.WakeLock wakeLock = powerManager.newWakeLock( 909 PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 910 TAG + ":" + displayId, displayId); 911 wakeLock.acquire(); 912 return wakeLock; 913 } finally { 914 Binder.restoreCallingIdentity(token); 915 } 916 } 917 918 private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { 919 Intent intent = BlockedAppStreamingActivity.createIntent( 920 activityInfo, mAssociationInfo.getDisplayName()); 921 mContext.startActivityAsUser( 922 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), 923 ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), 924 mContext.getUser()); 925 } 926 927 private void onSecureWindowShown(int displayId, int uid) { 928 synchronized (mVirtualDeviceLock) { 929 if (!mVirtualDisplays.contains(displayId)) { 930 return; 931 } 932 } 933 934 // If a virtual display isn't secure, the screen can't be captured. Show a warning toast 935 // if the secure window is shown on a non-secure virtual display. 936 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 937 Display display = displayManager.getDisplay(displayId); 938 if ((display.getFlags() & FLAG_SECURE) == 0) { 939 showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window, 940 Toast.LENGTH_LONG, mContext.getMainLooper()); 941 } 942 } 943 944 private ArraySet<UserHandle> getAllowedUserHandles() { 945 ArraySet<UserHandle> result = new ArraySet<>(); 946 final long token = Binder.clearCallingIdentity(); 947 try { 948 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 949 UserManager userManager = mContext.getSystemService(UserManager.class); 950 for (UserHandle profile : userManager.getAllProfiles()) { 951 int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy( 952 profile.getIdentifier()); 953 if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED 954 || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) { 955 result.add(profile); 956 } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) { 957 if (mParams.getUsersWithMatchingAccounts().contains(profile)) { 958 result.add(profile); 959 } 960 } 961 } 962 } finally { 963 Binder.restoreCallingIdentity(token); 964 } 965 return result; 966 } 967 968 969 void onVirtualDisplayRemoved(int displayId) { 970 /* This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released 971 * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()). 972 * At this point, the display is already released, but we still need to release the 973 * corresponding wakeLock and unregister the RunningAppsChangedListener from corresponding 974 * WindowPolicyController. 975 * 976 * Note that when the display is destroyed during VirtualDeviceImpl.close() call, 977 * this callback won't be invoked because the display is removed from 978 * VirtualDeviceManagerService before any resources are released. 979 */ 980 VirtualDisplayWrapper virtualDisplayWrapper; 981 synchronized (mVirtualDeviceLock) { 982 virtualDisplayWrapper = mVirtualDisplays.removeReturnOld(displayId); 983 } 984 985 if (virtualDisplayWrapper == null) { 986 Slog.w(TAG, "Virtual device " + mDeviceId + " doesn't have a virtual display with ID " 987 + displayId); 988 return; 989 } 990 991 final long ident = Binder.clearCallingIdentity(); 992 try { 993 releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); 994 } finally { 995 Binder.restoreCallingIdentity(ident); 996 } 997 } 998 999 /** 1000 * Release resources tied to virtual display owned by this VirtualDevice instance. 1001 * 1002 * Note that this method won't release the virtual display itself. 1003 * 1004 * @param virtualDisplayWrapper - VirtualDisplayWrapper to release resources for. 1005 */ 1006 private void releaseOwnedVirtualDisplayResources(VirtualDisplayWrapper virtualDisplayWrapper) { 1007 virtualDisplayWrapper.getWakeLock().release(); 1008 virtualDisplayWrapper.getWindowPolicyController().unregisterRunningAppsChangedListener( 1009 this); 1010 } 1011 1012 int getOwnerUid() { 1013 return mOwnerUid; 1014 } 1015 1016 ArraySet<Integer> getDisplayIds() { 1017 synchronized (mVirtualDeviceLock) { 1018 final int size = mVirtualDisplays.size(); 1019 ArraySet<Integer> arraySet = new ArraySet<>(size); 1020 for (int i = 0; i < size; i++) { 1021 arraySet.append(mVirtualDisplays.keyAt(i)); 1022 } 1023 return arraySet; 1024 } 1025 } 1026 1027 @VisibleForTesting 1028 GenericWindowPolicyController getDisplayWindowPolicyControllerForTest(int displayId) { 1029 VirtualDisplayWrapper virtualDisplayWrapper; 1030 synchronized (mVirtualDeviceLock) { 1031 virtualDisplayWrapper = mVirtualDisplays.get(displayId); 1032 } 1033 return virtualDisplayWrapper != null ? virtualDisplayWrapper.getWindowPolicyController() 1034 : null; 1035 } 1036 1037 /** 1038 * Returns true if an app with the given {@code uid} is currently running on this virtual 1039 * device. 1040 */ 1041 boolean isAppRunningOnVirtualDevice(int uid) { 1042 synchronized (mVirtualDeviceLock) { 1043 for (int i = 0; i < mVirtualDisplays.size(); i++) { 1044 if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) { 1045 return true; 1046 } 1047 } 1048 } 1049 return false; 1050 } 1051 1052 /** 1053 * Shows a toast on virtual displays owned by this device which have a given uid running. 1054 */ 1055 void showToastWhereUidIsRunning(int uid, @StringRes int resId, @Toast.Duration int duration, 1056 Looper looper) { 1057 showToastWhereUidIsRunning(uid, mContext.getString(resId), duration, looper); 1058 } 1059 1060 /** 1061 * Shows a toast on virtual displays owned by this device which have a given uid running. 1062 */ 1063 void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration, 1064 Looper looper) { 1065 ArrayList<Integer> displayIdsForUid = getDisplayIdsWhereUidIsRunning(uid); 1066 if (displayIdsForUid.isEmpty()) { 1067 return; 1068 } 1069 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 1070 for (int i = 0; i < displayIdsForUid.size(); i++) { 1071 Display display = displayManager.getDisplay(displayIdsForUid.get(i)); 1072 if (display != null && display.isValid()) { 1073 Toast.makeText(mContext.createDisplayContext(display), looper, text, 1074 duration).show(); 1075 } 1076 } 1077 } 1078 1079 private ArrayList<Integer> getDisplayIdsWhereUidIsRunning(int uid) { 1080 ArrayList<Integer> displayIdsForUid = new ArrayList<>(); 1081 synchronized (mVirtualDeviceLock) { 1082 for (int i = 0; i < mVirtualDisplays.size(); i++) { 1083 if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) { 1084 displayIdsForUid.add(mVirtualDisplays.keyAt(i)); 1085 } 1086 } 1087 } 1088 return displayIdsForUid; 1089 } 1090 1091 boolean isDisplayOwnedByVirtualDevice(int displayId) { 1092 synchronized (mVirtualDeviceLock) { 1093 return mVirtualDisplays.contains(displayId); 1094 } 1095 } 1096 1097 void onEnteringPipBlocked(int uid) { 1098 // Do nothing. ActivityRecord#checkEnterPictureInPictureState logs that the display does not 1099 // support PiP. 1100 } 1101 1102 void playSoundEffect(int effectType) { 1103 try { 1104 mSoundEffectListener.onPlaySoundEffect(effectType); 1105 } catch (RemoteException exception) { 1106 Slog.w(TAG, "Unable to invoke sound effect listener", exception); 1107 } 1108 } 1109 1110 /** 1111 * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if 1112 * the intent matches any filter notifying the DisplayPolicyController to abort the 1113 * activity launch to be replaced by the interception. 1114 */ 1115 private boolean shouldInterceptIntent(Intent intent) { 1116 synchronized (mVirtualDeviceLock) { 1117 boolean hasInterceptedIntent = false; 1118 for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) { 1119 if (interceptor.getValue().match( 1120 intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), 1121 intent.getCategories(), TAG) >= 0) { 1122 try { 1123 // For privacy reasons, only returning the intents action and data. Any 1124 // other required field will require a review. 1125 IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey()) 1126 .onIntentIntercepted(new Intent(intent.getAction(), intent.getData())); 1127 hasInterceptedIntent = true; 1128 } catch (RemoteException e) { 1129 Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e); 1130 } 1131 } 1132 } 1133 1134 return hasInterceptedIntent; 1135 } 1136 } 1137 1138 interface PendingTrampolineCallback { 1139 /** 1140 * Called when the callback should start waiting for the given pending trampoline. 1141 * Implementations should try to listen for activity starts associated with the given 1142 * {@code pendingTrampoline}, and launch the activity on the display with 1143 * {@link PendingTrampoline#mDisplayId}. 1144 */ 1145 void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline); 1146 1147 /** 1148 * Called when the callback should stop waiting for the given pending trampoline. This can 1149 * happen, for example, when the pending intent failed to send. 1150 */ 1151 void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline); 1152 } 1153 1154 /** 1155 * A data class storing a pending trampoline this device is expecting. 1156 */ 1157 static class PendingTrampoline { 1158 1159 /** 1160 * The original pending intent sent, for which a trampoline activity launch is expected. 1161 */ 1162 final PendingIntent mPendingIntent; 1163 1164 /** 1165 * The result receiver associated with this pending call. {@link Activity#RESULT_OK} will 1166 * be sent to the receiver if the trampoline activity was captured successfully. 1167 * {@link Activity#RESULT_CANCELED} is sent otherwise. 1168 */ 1169 final ResultReceiver mResultReceiver; 1170 1171 /** 1172 * The display ID to send the captured trampoline activity launch to. 1173 */ 1174 final int mDisplayId; 1175 1176 private PendingTrampoline(PendingIntent pendingIntent, ResultReceiver resultReceiver, 1177 int displayId) { 1178 mPendingIntent = pendingIntent; 1179 mResultReceiver = resultReceiver; 1180 mDisplayId = displayId; 1181 } 1182 1183 @Override 1184 public String toString() { 1185 return "PendingTrampoline{" 1186 + "pendingIntent=" + mPendingIntent 1187 + ", resultReceiver=" + mResultReceiver 1188 + ", displayId=" + mDisplayId + "}"; 1189 } 1190 } 1191 1192 /** Data class wrapping resources tied to single virtual display. */ 1193 private static final class VirtualDisplayWrapper { 1194 private final IVirtualDisplayCallback mToken; 1195 private final GenericWindowPolicyController mWindowPolicyController; 1196 private final PowerManager.WakeLock mWakeLock; 1197 1198 VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token, 1199 @NonNull GenericWindowPolicyController windowPolicyController, 1200 @NonNull PowerManager.WakeLock wakeLock) { 1201 mToken = Objects.requireNonNull(token); 1202 mWindowPolicyController = Objects.requireNonNull(windowPolicyController); 1203 mWakeLock = Objects.requireNonNull(wakeLock); 1204 } 1205 1206 GenericWindowPolicyController getWindowPolicyController() { 1207 return mWindowPolicyController; 1208 } 1209 1210 PowerManager.WakeLock getWakeLock() { 1211 return mWakeLock; 1212 } 1213 1214 IVirtualDisplayCallback getToken() { 1215 return mToken; 1216 } 1217 } 1218 } 1219