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