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