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