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.RequiresPermission;
27 import android.annotation.SuppressLint;
28 import android.app.ActivityOptions;
29 import android.companion.AssociationInfo;
30 import android.companion.AssociationRequest;
31 import android.companion.CompanionDeviceManager;
32 import android.companion.virtual.IVirtualDevice;
33 import android.companion.virtual.IVirtualDeviceActivityListener;
34 import android.companion.virtual.IVirtualDeviceListener;
35 import android.companion.virtual.IVirtualDeviceManager;
36 import android.companion.virtual.IVirtualDeviceSoundEffectListener;
37 import android.companion.virtual.VirtualDevice;
38 import android.companion.virtual.VirtualDeviceManager;
39 import android.companion.virtual.VirtualDeviceParams;
40 import android.companion.virtual.flags.Flags;
41 import android.companion.virtual.sensor.VirtualSensor;
42 import android.companion.virtualnative.IVirtualDeviceManagerNative;
43 import android.content.AttributionSource;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.hardware.display.DisplayManagerInternal;
47 import android.hardware.display.IVirtualDisplayCallback;
48 import android.hardware.display.VirtualDisplayConfig;
49 import android.os.Binder;
50 import android.os.Handler;
51 import android.os.IBinder;
52 import android.os.LocaleList;
53 import android.os.Looper;
54 import android.os.Parcel;
55 import android.os.Process;
56 import android.os.RemoteCallbackList;
57 import android.os.RemoteException;
58 import android.os.UserHandle;
59 import android.util.ArrayMap;
60 import android.util.ArraySet;
61 import android.util.ExceptionUtils;
62 import android.util.Slog;
63 import android.util.SparseArray;
64 import android.view.Display;
65 import android.widget.Toast;
66 
67 import com.android.internal.R;
68 import com.android.internal.annotations.GuardedBy;
69 import com.android.internal.annotations.VisibleForTesting;
70 import com.android.internal.util.DumpUtils;
71 import com.android.modules.expresslog.Counter;
72 import com.android.server.LocalServices;
73 import com.android.server.SystemService;
74 import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline;
75 import com.android.server.wm.ActivityInterceptorCallback;
76 import com.android.server.wm.ActivityTaskManagerInternal;
77 
78 import java.io.FileDescriptor;
79 import java.io.PrintWriter;
80 import java.util.ArrayList;
81 import java.util.Arrays;
82 import java.util.HashSet;
83 import java.util.List;
84 import java.util.Objects;
85 import java.util.Set;
86 import java.util.concurrent.ConcurrentHashMap;
87 import java.util.concurrent.atomic.AtomicInteger;
88 import java.util.function.Consumer;
89 import java.util.stream.Collectors;
90 
91 
92 @SuppressLint("LongLogTag")
93 public class VirtualDeviceManagerService extends SystemService {
94 
95     private static final String TAG = "VirtualDeviceManagerService";
96 
97     private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native";
98 
99     private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList(
100             AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
101             AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
102             AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
103 
104     private final Object mVirtualDeviceManagerLock = new Object();
105     private final VirtualDeviceManagerImpl mImpl;
106     private final VirtualDeviceManagerNativeImpl mNativeImpl;
107     private final VirtualDeviceManagerInternal mLocalService;
108     private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
109     private final Handler mHandler = new Handler(Looper.getMainLooper());
110     private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
111 
112     private static AtomicInteger sNextUniqueIndex = new AtomicInteger(
113             Context.DEVICE_ID_DEFAULT + 1);
114 
115     @GuardedBy("mVirtualDeviceManagerLock")
116     private ArrayMap<String, AssociationInfo> mActiveAssociations = new ArrayMap<>();
117 
118     private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
119             new CompanionDeviceManager.OnAssociationsChangedListener() {
120                 @Override
121                 @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
122                 public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) {
123                     syncVirtualDevicesToCdmAssociations(associations);
124                 }
125             };
126 
127     private final RemoteCallbackList<IVirtualDeviceListener> mVirtualDeviceListeners =
128             new RemoteCallbackList<>();
129 
130     /**
131      * Mapping from device IDs to virtual devices.
132      */
133     @GuardedBy("mVirtualDeviceManagerLock")
134     private final SparseArray<VirtualDeviceImpl> mVirtualDevices = new SparseArray<>();
135 
136     /**
137      * Mapping from device IDs to app UIDs running on the corresponding virtual device.
138      */
139     @GuardedBy("mVirtualDeviceManagerLock")
140     private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>();
141 
VirtualDeviceManagerService(Context context)142     public VirtualDeviceManagerService(Context context) {
143         super(context);
144         mImpl = new VirtualDeviceManagerImpl();
145         mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null;
146         mLocalService = new LocalService();
147     }
148 
149     private final ActivityInterceptorCallback mActivityInterceptorCallback =
150             new ActivityInterceptorCallback() {
151 
152                 @Nullable
153                 @Override
154                 public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
155                         ActivityInterceptorInfo info) {
156                     if (info.getCallingPackage() == null) {
157                         return null;
158                     }
159                     PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
160                     if (pt == null) {
161                         return null;
162                     }
163                     pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
164                     ActivityOptions options = info.getCheckedOptions();
165                     if (options == null) {
166                         options = ActivityOptions.makeBasic();
167                     }
168                     return new ActivityInterceptResult(
169                             info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
170                 }
171             };
172 
173     @Override
174     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
onStart()175     public void onStart() {
176         publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
177         if (Flags.enableNativeVdm()) {
178             publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
179         }
180         publishLocalService(VirtualDeviceManagerInternal.class, mLocalService);
181         ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
182                 ActivityTaskManagerInternal.class);
183         activityTaskManagerInternal.registerActivityStartInterceptor(
184                 VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
185                 mActivityInterceptorCallback);
186 
187         if (Flags.persistentDeviceIdApi()) {
188             CompanionDeviceManager cdm =
189                     getContext().getSystemService(CompanionDeviceManager.class);
190             if (cdm != null) {
191                 onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
192                 cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
193                         this::onCdmAssociationsChanged, UserHandle.USER_ALL);
194             } else {
195                 Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
196                         + " will be available.");
197             }
198         }
199     }
200 
onCameraAccessBlocked(int appUid)201     void onCameraAccessBlocked(int appUid) {
202         ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
203         for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
204             VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
205             virtualDevice.showToastWhereUidIsRunning(appUid,
206                     getContext().getString(
207                             R.string.vdm_camera_access_denied,
208                             virtualDevice.getDisplayName()),
209                     Toast.LENGTH_LONG, Looper.myLooper());
210         }
211     }
212 
getCameraAccessController(UserHandle userHandle)213     CameraAccessController getCameraAccessController(UserHandle userHandle) {
214         if (Flags.streamCamera()) {
215             return null;
216         }
217         int userId = userHandle.getIdentifier();
218         synchronized (mVirtualDeviceManagerLock) {
219             for (int i = 0; i < mVirtualDevices.size(); i++) {
220                 final CameraAccessController cameraAccessController =
221                         mVirtualDevices.valueAt(i).getCameraAccessController();
222                 if (cameraAccessController.getUserId() == userId) {
223                     return cameraAccessController;
224                 }
225             }
226         }
227         Context userContext = getContext().createContextAsUser(userHandle, 0);
228         return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked);
229     }
230 
231     @VisibleForTesting
getLocalServiceInstance()232     VirtualDeviceManagerInternal getLocalServiceInstance() {
233         return mLocalService;
234     }
235 
236     @VisibleForTesting
notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids)237     void notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids) {
238         synchronized (mVirtualDeviceManagerLock) {
239             if (!mVirtualDevices.contains(deviceId)) {
240                 Slog.e(TAG, "notifyRunningAppsChanged called for unknown deviceId:" + deviceId
241                         + " (maybe it was recently closed?)");
242                 return;
243             }
244             mAppsOnVirtualDevices.put(deviceId, uids);
245         }
246         mLocalService.onAppsOnVirtualDeviceChanged();
247     }
248 
249     @VisibleForTesting
addVirtualDevice(VirtualDeviceImpl virtualDevice)250     void addVirtualDevice(VirtualDeviceImpl virtualDevice) {
251         synchronized (mVirtualDeviceManagerLock) {
252             mVirtualDevices.put(virtualDevice.getDeviceId(), virtualDevice);
253         }
254     }
255 
256     /**
257      * Remove the virtual device. Sends the
258      * {@link VirtualDeviceManager#ACTION_VIRTUAL_DEVICE_REMOVED} broadcast as a result.
259      *
260      * @param deviceId deviceId to be removed
261      * @return {@code true} if the device was removed, {@code false} if the operation was a no-op
262      */
removeVirtualDevice(int deviceId)263     boolean removeVirtualDevice(int deviceId) {
264         synchronized (mVirtualDeviceManagerLock) {
265             if (!mVirtualDevices.contains(deviceId)) {
266                 return false;
267             }
268 
269             mAppsOnVirtualDevices.remove(deviceId);
270             mVirtualDevices.remove(deviceId);
271         }
272 
273         if (Flags.vdmPublicApis()) {
274             mVirtualDeviceListeners.broadcast(listener -> {
275                 try {
276                     listener.onVirtualDeviceClosed(deviceId);
277                 } catch (RemoteException e) {
278                     Slog.i(TAG, "Failed to invoke onVirtualDeviceClosed listener: "
279                             + e.getMessage());
280                 }
281             });
282         }
283 
284         Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED);
285         i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId);
286         i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
287         final long identity = Binder.clearCallingIdentity();
288         try {
289             getContext().sendBroadcastAsUser(i, UserHandle.ALL);
290 
291             if (!Flags.persistentDeviceIdApi()) {
292                 synchronized (mVirtualDeviceManagerLock) {
293                     if (mVirtualDevices.size() == 0) {
294                         unregisterCdmAssociationListener();
295                     }
296                 }
297             }
298         } finally {
299             Binder.restoreCallingIdentity(identity);
300         }
301         return true;
302     }
303 
304     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations)305     private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) {
306         Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
307         synchronized (mVirtualDeviceManagerLock) {
308             if (mVirtualDevices.size() == 0) {
309                 return;
310             }
311 
312             Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
313             for (AssociationInfo association : associations) {
314                 activeAssociationIds.add(association.getId());
315             }
316 
317             for (int i = 0; i < mVirtualDevices.size(); i++) {
318                 VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
319                 if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) {
320                     virtualDevicesToRemove.add(virtualDevice);
321                 }
322             }
323         }
324 
325         for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
326             virtualDevice.close();
327         }
328     }
329 
330     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
registerCdmAssociationListener()331     private void registerCdmAssociationListener() {
332         final CompanionDeviceManager cdm = getContext().getSystemService(
333                 CompanionDeviceManager.class);
334         cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
335                 mCdmAssociationListener);
336     }
337 
338     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
unregisterCdmAssociationListener()339     private void unregisterCdmAssociationListener() {
340         final CompanionDeviceManager cdm = getContext().getSystemService(
341                 CompanionDeviceManager.class);
342         cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
343     }
344 
345     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
onCdmAssociationsChanged(List<AssociationInfo> associations)346     void onCdmAssociationsChanged(List<AssociationInfo> associations) {
347         ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
348         for (int i = 0; i < associations.size(); ++i) {
349             AssociationInfo association = associations.get(i);
350             if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(association.getDeviceProfile())
351                     && !association.isRevoked()) {
352                 String persistentId =
353                         VirtualDeviceImpl.createPersistentDeviceId(association.getId());
354                 vdmAssociations.put(persistentId, association);
355             }
356         }
357         Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
358         Set<String> removedPersistentDeviceIds;
359         synchronized (mVirtualDeviceManagerLock) {
360             removedPersistentDeviceIds = mActiveAssociations.keySet();
361             removedPersistentDeviceIds.removeAll(vdmAssociations.keySet());
362             mActiveAssociations = vdmAssociations;
363 
364             for (int i = 0; i < mVirtualDevices.size(); i++) {
365                 VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
366                 if (removedPersistentDeviceIds.contains(virtualDevice.getPersistentDeviceId())) {
367                     virtualDevicesToRemove.add(virtualDevice);
368                 }
369             }
370         }
371 
372         for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
373             virtualDevice.close();
374         }
375 
376         if (!removedPersistentDeviceIds.isEmpty()) {
377             mLocalService.onPersistentDeviceIdsRemoved(removedPersistentDeviceIds);
378         }
379     }
380 
getVirtualDevicesSnapshot()381     private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() {
382         synchronized (mVirtualDeviceManagerLock) {
383             ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size());
384             for (int i = 0; i < mVirtualDevices.size(); i++) {
385                 virtualDevices.add(mVirtualDevices.valueAt(i));
386             }
387             return virtualDevices;
388         }
389     }
390 
391     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
392 
393         private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
394                 new VirtualDeviceImpl.PendingTrampolineCallback() {
395                     @Override
396                     public void startWaitingForPendingTrampoline(
397                             PendingTrampoline pendingTrampoline) {
398                         PendingTrampoline existing = mPendingTrampolines.put(
399                                 pendingTrampoline.mPendingIntent.getCreatorPackage(),
400                                 pendingTrampoline);
401                         if (existing != null) {
402                             existing.mResultReceiver.send(
403                                     VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null);
404                         }
405                     }
406 
407                     @Override
408                     public void stopWaitingForPendingTrampoline(
409                             PendingTrampoline pendingTrampoline) {
410                         mPendingTrampolines.remove(
411                                 pendingTrampoline.mPendingIntent.getCreatorPackage());
412                     }
413                 };
414 
415         @android.annotation.EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
416         @Override // Binder call
createVirtualDevice( IBinder token, AttributionSource attributionSource, int associationId, @NonNull VirtualDeviceParams params, @NonNull IVirtualDeviceActivityListener activityListener, @NonNull IVirtualDeviceSoundEffectListener soundEffectListener)417         public IVirtualDevice createVirtualDevice(
418                 IBinder token,
419                 AttributionSource attributionSource,
420                 int associationId,
421                 @NonNull VirtualDeviceParams params,
422                 @NonNull IVirtualDeviceActivityListener activityListener,
423                 @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
424             createVirtualDevice_enforcePermission();
425             attributionSource.enforceCallingUid();
426 
427             final int callingUid = getCallingUid();
428             final String packageName = attributionSource.getPackageName();
429             if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
430                 throw new SecurityException(
431                         "Package name " + packageName + " does not belong to calling uid "
432                                 + callingUid);
433             }
434             AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
435             if (associationInfo == null) {
436                 throw new IllegalArgumentException("No association with ID " + associationId);
437             }
438             if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
439                     .contains(associationInfo.getDeviceProfile())
440                     && Flags.persistentDeviceIdApi()) {
441                 throw new IllegalArgumentException("Unsupported CDM Association device profile "
442                         + associationInfo.getDeviceProfile() + " for virtual device creation.");
443             }
444             Objects.requireNonNull(params);
445             Objects.requireNonNull(activityListener);
446             Objects.requireNonNull(soundEffectListener);
447 
448             final UserHandle userHandle = getCallingUserHandle();
449             final CameraAccessController cameraAccessController =
450                     getCameraAccessController(userHandle);
451             final int deviceId = sNextUniqueIndex.getAndIncrement();
452             final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
453                     runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
454             VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
455                     VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
456                     deviceId,
457                     cameraAccessController, mPendingTrampolineCallback, activityListener,
458                     soundEffectListener, runningAppsChangedCallback, params);
459             if (Flags.expressMetrics()) {
460                 Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
461             }
462 
463             synchronized (mVirtualDeviceManagerLock) {
464                 if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
465                     final long callingId = Binder.clearCallingIdentity();
466                     try {
467                         registerCdmAssociationListener();
468                     } finally {
469                         Binder.restoreCallingIdentity(callingId);
470                     }
471                 }
472                 mVirtualDevices.put(deviceId, virtualDevice);
473             }
474 
475             if (Flags.vdmPublicApis()) {
476                 mVirtualDeviceListeners.broadcast(listener -> {
477                     try {
478                         listener.onVirtualDeviceCreated(deviceId);
479                     } catch (RemoteException e) {
480                         Slog.i(TAG, "Failed to invoke onVirtualDeviceCreated listener: "
481                                 + e.getMessage());
482                     }
483                 });
484             }
485             if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
486                 Counter.logIncrementWithUid(
487                         "virtual_devices.value_virtual_devices_created_with_uid_count",
488                         attributionSource.getUid());
489             }
490             return virtualDevice;
491         }
492 
493         @Override // Binder call
createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)494         public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
495                 IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)
496                 throws RemoteException {
497             Objects.requireNonNull(virtualDisplayConfig);
498             final int callingUid = getCallingUid();
499             if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
500                 throw new SecurityException(
501                         "Package name " + packageName + " does not belong to calling uid "
502                                 + callingUid);
503             }
504             VirtualDeviceImpl virtualDeviceImpl;
505             synchronized (mVirtualDeviceManagerLock) {
506                 virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
507                 if (virtualDeviceImpl == null) {
508                     throw new SecurityException(
509                             "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
510                                     + ")");
511                 }
512             }
513             if (virtualDeviceImpl.getOwnerUid() != callingUid) {
514                 throw new SecurityException(
515                         "uid " + callingUid
516                                 + " is not the owner of the supplied VirtualDevice (deviceId = "
517                                 + virtualDevice.getDeviceId() + ")");
518             }
519 
520             return virtualDeviceImpl.createVirtualDisplay(
521                     virtualDisplayConfig, callback, packageName);
522         }
523 
524         @Override // Binder call
getVirtualDevices()525         public List<VirtualDevice> getVirtualDevices() {
526             List<VirtualDevice> virtualDevices = new ArrayList<>();
527             synchronized (mVirtualDeviceManagerLock) {
528                 for (int i = 0; i < mVirtualDevices.size(); i++) {
529                     final VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
530                     virtualDevices.add(device.getPublicVirtualDeviceObject());
531                 }
532             }
533             return virtualDevices;
534         }
535 
536         @Override // Binder call
getVirtualDevice(int deviceId)537         public VirtualDevice getVirtualDevice(int deviceId) {
538             VirtualDeviceImpl device;
539             synchronized (mVirtualDeviceManagerLock) {
540                 device = mVirtualDevices.get(deviceId);
541             }
542             return device == null ? null : device.getPublicVirtualDeviceObject();
543         }
544 
545         @Override // Binder call
registerVirtualDeviceListener(IVirtualDeviceListener listener)546         public void registerVirtualDeviceListener(IVirtualDeviceListener listener) {
547             mVirtualDeviceListeners.register(listener);
548         }
549 
550         @Override // Binder call
unregisterVirtualDeviceListener(IVirtualDeviceListener listener)551         public void unregisterVirtualDeviceListener(IVirtualDeviceListener listener) {
552             mVirtualDeviceListeners.unregister(listener);
553         }
554 
555         @Override // BinderCall
556         @VirtualDeviceParams.DevicePolicy
getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType)557         public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
558             synchronized (mVirtualDeviceManagerLock) {
559                 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
560                 return virtualDevice != null
561                         ? virtualDevice.getDevicePolicy(policyType) : DEVICE_POLICY_DEFAULT;
562             }
563         }
564 
565 
566         @Override // Binder call
getDeviceIdForDisplayId(int displayId)567         public int getDeviceIdForDisplayId(int displayId) {
568             if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
569                 return Context.DEVICE_ID_DEFAULT;
570             }
571             ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
572             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
573                 VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
574                 if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) {
575                     return virtualDevice.getDeviceId();
576                 }
577             }
578             return Context.DEVICE_ID_DEFAULT;
579         }
580 
581         @Override // Binder call
getDisplayNameForPersistentDeviceId( @onNull String persistentDeviceId)582         public @Nullable CharSequence getDisplayNameForPersistentDeviceId(
583                 @NonNull String persistentDeviceId) {
584             final AssociationInfo associationInfo;
585             synchronized (mVirtualDeviceManagerLock) {
586                 associationInfo = mActiveAssociations.get(persistentDeviceId);
587             }
588             return associationInfo == null ? null : associationInfo.getDisplayName();
589         }
590 
591         @Override // Binder call
getAllPersistentDeviceIds()592         public @NonNull List<String> getAllPersistentDeviceIds() {
593             return new ArrayList<>(mLocalService.getAllPersistentDeviceIds());
594         }
595 
596         // Binder call
597         @Override
isValidVirtualDeviceId(int deviceId)598         public boolean isValidVirtualDeviceId(int deviceId) {
599             synchronized (mVirtualDeviceManagerLock) {
600                 return mVirtualDevices.contains(deviceId);
601             }
602         }
603 
604         @Override // Binder call
getAudioPlaybackSessionId(int deviceId)605         public int getAudioPlaybackSessionId(int deviceId) {
606             synchronized (mVirtualDeviceManagerLock) {
607                 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
608                 return virtualDevice != null
609                         ? virtualDevice.getAudioPlaybackSessionId() : AUDIO_SESSION_ID_GENERATE;
610             }
611         }
612 
613         @Override // Binder call
getAudioRecordingSessionId(int deviceId)614         public int getAudioRecordingSessionId(int deviceId) {
615             synchronized (mVirtualDeviceManagerLock) {
616                 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
617                 return virtualDevice != null
618                         ? virtualDevice.getAudioRecordingSessionId() : AUDIO_SESSION_ID_GENERATE;
619             }
620         }
621 
622         @Override // Binder call
playSoundEffect(int deviceId, int effectType)623         public void playSoundEffect(int deviceId, int effectType) {
624             VirtualDeviceImpl virtualDevice;
625             synchronized (mVirtualDeviceManagerLock) {
626                 virtualDevice = mVirtualDevices.get(deviceId);
627             }
628 
629             if (virtualDevice != null) {
630                 virtualDevice.playSoundEffect(effectType);
631             }
632         }
633 
634         @Override // Binder call
isVirtualDeviceOwnedMirrorDisplay(int displayId)635         public boolean isVirtualDeviceOwnedMirrorDisplay(int displayId) {
636             if (getDeviceIdForDisplayId(displayId) == Context.DEVICE_ID_DEFAULT) {
637                 return false;
638             }
639 
640             DisplayManagerInternal displayManager = LocalServices.getService(
641                     DisplayManagerInternal.class);
642             return displayManager.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY;
643         }
644 
645         @Nullable
getAssociationInfo(String packageName, int associationId)646         private AssociationInfo getAssociationInfo(String packageName, int associationId) {
647             final UserHandle userHandle = getCallingUserHandle();
648             final CompanionDeviceManager cdm =
649                     getContext().createContextAsUser(userHandle, 0)
650                             .getSystemService(CompanionDeviceManager.class);
651             List<AssociationInfo> associations;
652             final long identity = Binder.clearCallingIdentity();
653             try {
654                 associations = cdm.getAllAssociations();
655             } finally {
656                 Binder.restoreCallingIdentity(identity);
657             }
658             final int callingUserId = userHandle.getIdentifier();
659             if (associations != null) {
660                 final int associationSize = associations.size();
661                 for (int i = 0; i < associationSize; i++) {
662                     AssociationInfo associationInfo = associations.get(i);
663                     if (associationInfo.belongsToPackage(callingUserId, packageName)
664                             && associationId == associationInfo.getId()) {
665                         return associationInfo;
666                     }
667                 }
668             } else {
669                 Slog.w(TAG, "No associations for user " + callingUserId);
670             }
671             return null;
672         }
673 
674         @Override
onTransact(int code, Parcel data, Parcel reply, int flags)675         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
676                 throws RemoteException {
677             try {
678                 return super.onTransact(code, data, reply, flags);
679             } catch (Throwable e) {
680                 Slog.e(TAG, "Error during IPC", e);
681                 throw ExceptionUtils.propagate(e, RemoteException.class);
682             }
683         }
684 
685         @Override
dump(@onNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args)686         public void dump(@NonNull FileDescriptor fd,
687                 @NonNull PrintWriter fout,
688                 @Nullable String[] args) {
689             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) {
690                 return;
691             }
692             fout.println("Created virtual devices: ");
693             ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
694             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
695                 virtualDevicesSnapshot.get(i).dump(fd, fout, args);
696             }
697 
698             mVirtualDeviceLog.dump(fout);
699         }
700     }
701 
702     final class VirtualDeviceManagerNativeImpl extends IVirtualDeviceManagerNative.Stub {
703         @Override // Binder call
getDeviceIdsForUid(int uid)704         public int[] getDeviceIdsForUid(int uid) {
705             return mLocalService
706                     .getDeviceIdsForUid(uid).stream().mapToInt(Integer::intValue).toArray();
707         }
708 
709         @Override // Binder call
getDevicePolicy(int deviceId, int policyType)710         public int getDevicePolicy(int deviceId, int policyType) {
711             return mImpl.getDevicePolicy(deviceId, policyType);
712         }
713     }
714 
715     private final class LocalService extends VirtualDeviceManagerInternal {
716         @GuardedBy("mVirtualDeviceManagerLock")
717         private final ArrayList<AppsOnVirtualDeviceListener> mAppsOnVirtualDeviceListeners =
718                 new ArrayList<>();
719         @GuardedBy("mVirtualDeviceManagerLock")
720         private final ArrayList<Consumer<String>> mPersistentDeviceIdRemovedListeners =
721                 new ArrayList<>();
722 
723         @GuardedBy("mVirtualDeviceManagerLock")
724         private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
725 
726         @Override
getDeviceOwnerUid(int deviceId)727         public int getDeviceOwnerUid(int deviceId) {
728             VirtualDeviceImpl virtualDevice;
729             synchronized (mVirtualDeviceManagerLock) {
730                 virtualDevice = mVirtualDevices.get(deviceId);
731             }
732             return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID;
733         }
734 
735         @Override
getVirtualSensor(int deviceId, int handle)736         public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) {
737             VirtualDeviceImpl virtualDevice;
738             synchronized (mVirtualDeviceManagerLock) {
739                 virtualDevice = mVirtualDevices.get(deviceId);
740             }
741             return virtualDevice != null ? virtualDevice.getVirtualSensorByHandle(handle) : null;
742         }
743 
744         @Override
getDeviceIdsForUid(int uid)745         public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
746             ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
747             ArraySet<Integer> result = new ArraySet<>();
748             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
749                 VirtualDeviceImpl device = virtualDevicesSnapshot.get(i);
750                 if (device.isAppRunningOnVirtualDevice(uid)) {
751                     result.add(device.getDeviceId());
752                 }
753             }
754             return result;
755         }
756 
757         @Override
onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId)758         public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
759             VirtualDeviceImpl virtualDeviceImpl;
760             synchronized (mVirtualDeviceManagerLock) {
761                 virtualDeviceImpl = mVirtualDevices.get(
762                         ((VirtualDeviceImpl) virtualDevice).getDeviceId());
763             }
764             if (virtualDeviceImpl != null) {
765                 virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
766             }
767         }
768 
769         @Override
onAppsOnVirtualDeviceChanged()770         public void onAppsOnVirtualDeviceChanged() {
771             ArraySet<Integer> latestRunningUids = new ArraySet<>();
772             final AppsOnVirtualDeviceListener[] listeners;
773             synchronized (mVirtualDeviceManagerLock) {
774                 int size = mAppsOnVirtualDevices.size();
775                 for (int i = 0; i < size; i++) {
776                     latestRunningUids.addAll(mAppsOnVirtualDevices.valueAt(i));
777                 }
778                 if (!mAllUidsOnVirtualDevice.equals(latestRunningUids)) {
779                     mAllUidsOnVirtualDevice.clear();
780                     mAllUidsOnVirtualDevice.addAll(latestRunningUids);
781                     listeners =
782                             mAppsOnVirtualDeviceListeners.toArray(
783                                     new AppsOnVirtualDeviceListener[0]);
784                 } else {
785                     listeners = null;
786                 }
787             }
788             if (listeners != null) {
789                 mHandler.post(() -> {
790                     for (AppsOnVirtualDeviceListener listener : listeners) {
791                         listener.onAppsOnAnyVirtualDeviceChanged(latestRunningUids);
792                     }
793                 });
794             }
795         }
796 
797         @Override
onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds)798         public void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds) {
799             final List<Consumer<String>> persistentDeviceIdRemovedListeners;
800             synchronized (mVirtualDeviceManagerLock) {
801                 persistentDeviceIdRemovedListeners = List.copyOf(
802                         mPersistentDeviceIdRemovedListeners);
803             }
804             mHandler.post(() -> {
805                 for (String persistentDeviceId : removedPersistentDeviceIds) {
806                     for (Consumer<String> listener : persistentDeviceIdRemovedListeners) {
807                         listener.accept(persistentDeviceId);
808                     }
809                 }
810             });
811         }
812 
813         @Override
onAuthenticationPrompt(int uid)814         public void onAuthenticationPrompt(int uid) {
815             synchronized (mVirtualDeviceManagerLock) {
816                 for (int i = 0; i < mVirtualDevices.size(); i++) {
817                     VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
818                     device.showToastWhereUidIsRunning(uid,
819                             R.string.app_streaming_blocked_message_for_fingerprint_dialog,
820                             Toast.LENGTH_LONG, Looper.getMainLooper());
821                 }
822             }
823         }
824 
825         @Override
getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice)826         public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
827             return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
828         }
829 
830         @Override
831         @Nullable
getPreferredLocaleListForUid(int uid)832         public LocaleList getPreferredLocaleListForUid(int uid) {
833             // TODO: b/263188984 support the case where an app is running on multiple VDs
834             synchronized (mVirtualDeviceManagerLock) {
835                 for (int i = 0; i < mAppsOnVirtualDevices.size(); i++) {
836                     if (mAppsOnVirtualDevices.valueAt(i).contains(uid)) {
837                         int deviceId = mAppsOnVirtualDevices.keyAt(i);
838                         return mVirtualDevices.get(deviceId).getDeviceLocaleList();
839                     }
840                 }
841             }
842             return null;
843         }
844 
845         @Override
isAppRunningOnAnyVirtualDevice(int uid)846         public boolean isAppRunningOnAnyVirtualDevice(int uid) {
847             ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
848             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
849                 if (virtualDevicesSnapshot.get(i).isAppRunningOnVirtualDevice(uid)) {
850                     return true;
851                 }
852             }
853             return false;
854         }
855 
856         @Override
isInputDeviceOwnedByVirtualDevice(int inputDeviceId)857         public boolean isInputDeviceOwnedByVirtualDevice(int inputDeviceId) {
858             ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
859             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
860                 if (virtualDevicesSnapshot.get(i)
861                         .isInputDeviceOwnedByVirtualDevice(inputDeviceId)) {
862                     return true;
863                 }
864             }
865             return false;
866         }
867 
868         @Override
getDisplayIdsForDevice(int deviceId)869         public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) {
870             VirtualDeviceImpl virtualDevice;
871             synchronized (mVirtualDeviceManagerLock) {
872                 virtualDevice = mVirtualDevices.get(deviceId);
873             }
874             return virtualDevice == null ? new ArraySet<>()
875                     : Arrays.stream(virtualDevice.getDisplayIds()).boxed()
876                             .collect(Collectors.toCollection(ArraySet::new));
877         }
878 
879         @Override
getDeviceIdForDisplayId(int displayId)880         public int getDeviceIdForDisplayId(int displayId) {
881             return mImpl.getDeviceIdForDisplayId(displayId);
882         }
883 
884         @Override
isValidVirtualDeviceId(int deviceId)885         public boolean isValidVirtualDeviceId(int deviceId) {
886             return mImpl.isValidVirtualDeviceId(deviceId);
887         }
888 
889         @Override
getPersistentIdForDevice(int deviceId)890         public @Nullable String getPersistentIdForDevice(int deviceId) {
891             if (deviceId == Context.DEVICE_ID_DEFAULT) {
892                 return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
893             }
894 
895             VirtualDeviceImpl virtualDevice;
896             synchronized (mVirtualDeviceManagerLock) {
897                 virtualDevice = mVirtualDevices.get(deviceId);
898             }
899             return virtualDevice == null ? null : virtualDevice.getPersistentDeviceId();
900         }
901 
902         @Override
getAllPersistentDeviceIds()903         public @NonNull Set<String> getAllPersistentDeviceIds() {
904             synchronized (mVirtualDeviceManagerLock) {
905                 return Set.copyOf(mActiveAssociations.keySet());
906             }
907         }
908 
909         @Override
registerAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)910         public void registerAppsOnVirtualDeviceListener(
911                 @NonNull AppsOnVirtualDeviceListener listener) {
912             synchronized (mVirtualDeviceManagerLock) {
913                 mAppsOnVirtualDeviceListeners.add(listener);
914             }
915         }
916 
917         @Override
unregisterAppsOnVirtualDeviceListener( @onNull AppsOnVirtualDeviceListener listener)918         public void unregisterAppsOnVirtualDeviceListener(
919                 @NonNull AppsOnVirtualDeviceListener listener) {
920             synchronized (mVirtualDeviceManagerLock) {
921                 mAppsOnVirtualDeviceListeners.remove(listener);
922             }
923         }
924 
925         @Override
registerPersistentDeviceIdRemovedListener( @onNull Consumer<String> persistentDeviceIdRemovedListener)926         public void registerPersistentDeviceIdRemovedListener(
927                 @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
928             synchronized (mVirtualDeviceManagerLock) {
929                 mPersistentDeviceIdRemovedListeners.add(persistentDeviceIdRemovedListener);
930             }
931         }
932 
933         @Override
unregisterPersistentDeviceIdRemovedListener( @onNull Consumer<String> persistentDeviceIdRemovedListener)934         public void unregisterPersistentDeviceIdRemovedListener(
935                 @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
936             synchronized (mVirtualDeviceManagerLock) {
937                 mPersistentDeviceIdRemovedListeners.remove(persistentDeviceIdRemovedListener);
938             }
939         }
940     }
941 
942     private static final class PendingTrampolineMap {
943         /**
944          * The maximum duration, in milliseconds, to wait for a trampoline activity launch after
945          * invoking a pending intent.
946          */
947         private static final int TRAMPOLINE_WAIT_MS = 5000;
948 
949         private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>();
950         private final Handler mHandler;
951 
PendingTrampolineMap(Handler handler)952         PendingTrampolineMap(Handler handler) {
953             mHandler = handler;
954         }
955 
put( @onNull String packageName, @NonNull PendingTrampoline pendingTrampoline)956         PendingTrampoline put(
957                 @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) {
958             PendingTrampoline existing = mMap.put(packageName, pendingTrampoline);
959             mHandler.removeCallbacksAndMessages(existing);
960             mHandler.postDelayed(
961                     () -> {
962                         final String creatorPackage =
963                                 pendingTrampoline.mPendingIntent.getCreatorPackage();
964                         if (creatorPackage != null) {
965                             remove(creatorPackage);
966                         }
967                     },
968                     pendingTrampoline,
969                     TRAMPOLINE_WAIT_MS);
970             return existing;
971         }
972 
remove(@onNull String packageName)973         PendingTrampoline remove(@NonNull String packageName) {
974             PendingTrampoline pendingTrampoline = mMap.remove(packageName);
975             mHandler.removeCallbacksAndMessages(pendingTrampoline);
976             return pendingTrampoline;
977         }
978     }
979 }
980