diff options
| author | 2024-03-06 15:44:25 +0100 | |
|---|---|---|
| committer | 2024-06-27 09:46:22 +0000 | |
| commit | 9892548d2a69ce8477d4e38e75608bc97ad6d55f (patch) | |
| tree | 650befb98327ea5535fb28ad85d5bc359deb8c86 | |
| parent | 9962f0204bf0eb768c169c51d6a31372a574f9eb (diff) | |
Support CDM-less virtual devices.
- New VDMInternal#createVirtualDevice, which creates a VD with
CDM association id = 0 and display name from the params. The
name is required in this case (it's optional otherwise)
- The VD is fully functional, it just doesn't have a persistent id.
- The VD is also visible via the public VDM APIs. This is by design,
because if any activity ever runs on such device its deviceId will
not be the default one and the app must be able to get to that
VirtualDevice and its capabilities.
- Never assume that VirtualDeviceImpl#mAssociationInfo is not null
Not testable in CTS and not really flaggable. A flag disabling the new functionality will effectively crash system server when off.
Fix: 341060398
Test: manual
Flag: EXEMPT behavioural change
Change-Id: I65eca165362b0ef819d55b743de8a7780646c26e
7 files changed, 124 insertions, 22 deletions
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 24f18cc257f8..11b0327e152c 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -17,7 +17,9 @@ package android.companion.virtual; import android.app.PendingIntent; +import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceIntentInterceptor; +import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; import android.companion.virtual.sensor.VirtualSensor; @@ -282,4 +284,15 @@ interface IVirtualDevice { */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") String getVirtualCameraId(in VirtualCameraConfig camera); + + /** + * Setter for listeners that live in the client process, namely in + * {@link android.companion.virtual.VirtualDeviceInternal}. + * + * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl + * object is created before the returned VirtualDeviceInternal one. + */ + @EnforcePermission("CREATE_VIRTUAL_DEVICE") + void setListeners(in IVirtualDeviceActivityListener activityListener, + in IVirtualDeviceSoundEffectListener soundEffectListener); } diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index 60448bad8e69..bb86af754b95 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -162,6 +162,20 @@ public class VirtualDeviceInternal { mSoundEffectListener); } + VirtualDeviceInternal( + IVirtualDeviceManager service, + Context context, + IVirtualDevice virtualDevice) { + mService = service; + mContext = context.getApplicationContext(); + mVirtualDevice = virtualDevice; + try { + mVirtualDevice.setListeners(mActivityListenerBinder, mSoundEffectListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + int getDeviceId() { try { return mVirtualDevice.getDeviceId(); diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index ed55a3fe4392..a3c9ff7b653d 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -573,6 +573,12 @@ public final class VirtualDeviceManager { new VirtualDeviceInternal(service, context, associationId, params); } + /** @hide */ + public VirtualDevice(IVirtualDeviceManager service, Context context, + IVirtualDevice virtualDevice) { + mVirtualDeviceInternal = new VirtualDeviceInternal(service, context, virtualDevice); + } + /** * Returns the unique ID of this virtual device. */ diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 6704049e3612..f47c014a7e59 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -183,8 +183,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final SparseIntArray mDevicePolicies; @GuardedBy("mVirtualDeviceLock") private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>(); - private final IVirtualDeviceActivityListener mActivityListener; - private final IVirtualDeviceSoundEffectListener mSoundEffectListener; + private IVirtualDeviceActivityListener mActivityListener; + private IVirtualDeviceSoundEffectListener mSoundEffectListener; private final DisplayManagerGlobal mDisplayManager; private final DisplayManagerInternal mDisplayManagerInternal; @GuardedBy("mVirtualDeviceLock") @@ -301,7 +301,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid()); mContext = context.createContextAsUser(ownerUserHandle, 0); mAssociationInfo = associationInfo; - mPersistentDeviceId = createPersistentDeviceId(associationInfo.getId()); + mPersistentDeviceId = associationInfo == null + ? null + : createPersistentDeviceId(associationInfo.getId()); mService = service; mPendingTrampolineCallback = pendingTrampolineCallback; mActivityListener = activityListener; @@ -403,7 +405,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub /** Returns the device display name. */ CharSequence getDisplayName() { - return mAssociationInfo.getDisplayName(); + return mAssociationInfo == null ? mParams.getName() : mAssociationInfo.getDisplayName(); } /** Returns the public representation of the device. */ @@ -418,6 +420,22 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + /** + * Setter for listeners that live in the client process, namely in + * {@link android.companion.virtual.VirtualDeviceInternal}. + * + * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl + * object is created before the returned VirtualDeviceInternal one. + */ + @Override // Binder call + @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + public void setListeners(@NonNull IVirtualDeviceActivityListener activityListener, + @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { + super.setListeners_enforcePermission(); + mActivityListener = Objects.requireNonNull(activityListener); + mSoundEffectListener = Objects.requireNonNull(soundEffectListener); + } + @Override // Binder call public @VirtualDeviceParams.DevicePolicy int getDevicePolicy( @VirtualDeviceParams.PolicyType int policyType) { @@ -454,7 +472,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call public int getAssociationId() { - return mAssociationInfo.getId(); + return mAssociationInfo == null + ? VirtualDeviceManagerService.CDM_ASSOCIATION_ID_NONE + : mAssociationInfo.getId(); } @Override // Binder call @@ -1105,7 +1125,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub String indent = " "; fout.println(" VirtualDevice: "); fout.println(indent + "mDeviceId: " + mDeviceId); - fout.println(indent + "mAssociationId: " + mAssociationInfo.getId()); + fout.println(indent + "mAssociationId: " + getAssociationId()); fout.println(indent + "mOwnerPackageName: " + mOwnerPackageName); fout.println(indent + "mParams: "); mParams.dump(fout, indent + indent); @@ -1251,8 +1271,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { - Intent intent = BlockedAppStreamingActivity.createIntent( - activityInfo, mAssociationInfo.getDisplayName()); + Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName()); mContext.startActivityAsUser( intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), @@ -1339,7 +1358,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @SuppressWarnings("AndroidFrameworkRequiresPermission") private void checkVirtualInputDeviceDisplayIdAssociation(int displayId) { - if (mContext.checkCallingPermission(android.Manifest.permission.INJECT_EVENTS) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INJECT_EVENTS) == PackageManager.PERMISSION_GRANTED) { // The INJECT_EVENTS permission allows for injecting input to any window / display. return; diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java index c65aa5bd355b..b0bacfd158ed 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java @@ -19,6 +19,7 @@ package com.android.server.companion.virtual; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; +import android.os.Process; import android.util.SparseArray; import java.io.PrintWriter; @@ -35,6 +36,8 @@ final class VirtualDeviceLog { "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault()); private static final int MAX_ENTRIES = 16; + private static final String VIRTUAL_DEVICE_OWNER_SYSTEM = "system"; + private final Context mContext; private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>(); @@ -132,6 +135,8 @@ final class VirtualDeviceLog { String[] packages; if (mUidToPackagesCache.contains(ownerUid)) { return mUidToPackagesCache.get(ownerUid); + } else if (ownerUid == Process.SYSTEM_UID) { + return VIRTUAL_DEVICE_OWNER_SYSTEM; } else { packages = mPackageManager.getPackagesForUid(ownerUid); String packageName = ""; diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 9ad73cae08cd..1be1d2b1286b 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -101,6 +101,11 @@ public class VirtualDeviceManagerService extends SystemService { AssociationRequest.DEVICE_PROFILE_APP_STREAMING, AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING); + /** + * A virtual device association id corresponding to no CDM association. + */ + static final int CDM_ASSOCIATION_ID_NONE = 0; + private final Object mVirtualDeviceManagerLock = new Object(); private final VirtualDeviceManagerImpl mImpl; private final VirtualDeviceManagerNativeImpl mNativeImpl; @@ -316,7 +321,9 @@ public class VirtualDeviceManagerService extends SystemService { for (int i = 0; i < mVirtualDevices.size(); i++) { VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); - if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) { + int deviceAssociationId = virtualDevice.getAssociationId(); + if (deviceAssociationId != CDM_ASSOCIATION_ID_NONE + && !activeAssociationIds.contains(deviceAssociationId)) { virtualDevicesToRemove.add(virtualDevice); } } @@ -422,28 +429,39 @@ public class VirtualDeviceManagerService extends SystemService { @NonNull IVirtualDeviceActivityListener activityListener, @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { createVirtualDevice_enforcePermission(); - attributionSource.enforceCallingUid(); - - final int callingUid = getCallingUid(); + Objects.requireNonNull(activityListener); + Objects.requireNonNull(soundEffectListener); final String packageName = attributionSource.getPackageName(); - if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { - throw new SecurityException( - "Package name " + packageName + " does not belong to calling uid " - + callingUid); - } AssociationInfo associationInfo = getAssociationInfo(packageName, associationId); if (associationInfo == null) { throw new IllegalArgumentException("No association with ID " + associationId); - } - if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES + } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES .contains(associationInfo.getDeviceProfile()) && Flags.persistentDeviceIdApi()) { throw new IllegalArgumentException("Unsupported CDM Association device profile " + associationInfo.getDeviceProfile() + " for virtual device creation."); } + return createVirtualDevice(token, attributionSource, associationInfo, params, + activityListener, soundEffectListener); + } + + private IVirtualDevice createVirtualDevice( + IBinder token, + AttributionSource attributionSource, + AssociationInfo associationInfo, + @NonNull VirtualDeviceParams params, + @Nullable IVirtualDeviceActivityListener activityListener, + @Nullable IVirtualDeviceSoundEffectListener soundEffectListener) { + createVirtualDevice_enforcePermission(); + attributionSource.enforceCallingUid(); + + final String packageName = attributionSource.getPackageName(); + if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { + throw new SecurityException( + "Package name " + packageName + " does not belong to calling uid " + + getCallingUid()); + } Objects.requireNonNull(params); - Objects.requireNonNull(activityListener); - Objects.requireNonNull(soundEffectListener); final UserHandle userHandle = getCallingUserHandle(); final CameraAccessController cameraAccessController = @@ -724,6 +742,21 @@ public class VirtualDeviceManagerService extends SystemService { private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>(); @Override + public @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice( + @NonNull VirtualDeviceParams params) { + Objects.requireNonNull(params, "params must not be null"); + Objects.requireNonNull(params.getName(), "virtual device name must not be null"); + IVirtualDevice virtualDevice = mImpl.createVirtualDevice( + new Binder(), + getContext().getAttributionSource(), + /* associationInfo= */ null, + params, + /* activityListener= */ null, + /* soundEffectListener= */ null); + return new VirtualDeviceManager.VirtualDevice(mImpl, getContext(), virtualDevice); + } + + @Override public int getDeviceOwnerUid(int deviceId) { VirtualDeviceImpl virtualDevice; synchronized (mVirtualDeviceManagerLock) { diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index b17978370bd7..6e38733f04c2 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.VirtualDevice; +import android.companion.virtual.VirtualDeviceManager; +import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.sensor.VirtualSensor; import android.content.Context; import android.os.LocaleList; @@ -180,4 +182,14 @@ public abstract class VirtualDeviceManagerInternal { * exists, as long as one may have existed or can be created. */ public abstract @NonNull Set<String> getAllPersistentDeviceIds(); + + /** + * Creates a virtual device where applications can launch and receive input events injected by + * the creator. + * + * <p>A Companion Device Manager association is not required. Only the system may create such + * virtual devices.</p> + */ + public abstract @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice( + @NonNull VirtualDeviceParams params); } |