diff options
5 files changed, 166 insertions, 79 deletions
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 b75728e9f97c..f03e8c713228 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -110,6 +110,7 @@ import android.util.SparseIntArray; import android.view.Display; import android.view.WindowManager; import android.widget.Toast; +import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -1411,8 +1412,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId; } - @GuardedBy("mVirtualDeviceLock") - private GenericWindowPolicyController createWindowPolicyControllerLocked( + private GenericWindowPolicyController createWindowPolicyController( @NonNull Set<String> displayCategories) { final boolean activityLaunchAllowedByDefault = getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT; @@ -1421,28 +1421,28 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub final boolean showTasksInHostDeviceRecents = getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT; - if (mActivityListenerAdapter == null) { - mActivityListenerAdapter = new GwpcActivityListener(); - } - - final GenericWindowPolicyController gwpc = new GenericWindowPolicyController( - WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, - mAttributionSource, - getAllowedUserHandles(), - activityLaunchAllowedByDefault, - mActivityPolicyExemptions, - mActivityPolicyPackageExemptions, - crossTaskNavigationAllowedByDefault, - /* crossTaskNavigationExemptions= */crossTaskNavigationAllowedByDefault - ? mParams.getBlockedCrossTaskNavigations() - : mParams.getAllowedCrossTaskNavigations(), - mActivityListenerAdapter, - displayCategories, - showTasksInHostDeviceRecents, - mParams.getHomeComponent()); - gwpc.registerRunningAppsChangedListener(/* listener= */ this); - return gwpc; + synchronized (mVirtualDeviceLock) { + if (mActivityListenerAdapter == null) { + mActivityListenerAdapter = new GwpcActivityListener(); + } + + return new GenericWindowPolicyController( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, + mAttributionSource, + getAllowedUserHandles(), + activityLaunchAllowedByDefault, + mActivityPolicyExemptions, + mActivityPolicyPackageExemptions, + crossTaskNavigationAllowedByDefault, + /* crossTaskNavigationExemptions= */crossTaskNavigationAllowedByDefault + ? mParams.getBlockedCrossTaskNavigations() + : mParams.getAllowedCrossTaskNavigations(), + mActivityListenerAdapter, + displayCategories, + showTasksInHostDeviceRecents, + mParams.getHomeComponent()); + } } @Override // Binder call @@ -1450,55 +1450,54 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @NonNull IVirtualDisplayCallback callback) { checkCallerIsDeviceOwner(); - int displayId; - boolean showPointer; - boolean isTrustedDisplay; - GenericWindowPolicyController gwpc; - synchronized (mVirtualDeviceLock) { - gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories()); - displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, + final boolean isTrustedDisplay = + (virtualDisplayConfig.getFlags() & DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED) + == DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + if (!isTrustedDisplay && getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) { + throw new SecurityException( + "All displays must be trusted for devices with custom clipboard policy."); + } + + GenericWindowPolicyController gwpc = + createWindowPolicyController(virtualDisplayConfig.getDisplayCategories()); + + // Create the display outside of the lock to avoid deadlock. DisplayManagerService will + // acquire the global WM lock while creating the display. At the same time, WM may query + // VDM and this virtual device to get policies, display ownership, etc. + int displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback, this, gwpc, mOwnerPackageName); - boolean isMirrorDisplay = - mDisplayManagerInternal.getDisplayIdToMirror(displayId) - != Display.INVALID_DISPLAY; - gwpc.setDisplayId(displayId, isMirrorDisplay); - isTrustedDisplay = - (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) - == Display.FLAG_TRUSTED; - if (!isTrustedDisplay - && getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) { - throw new SecurityException("All displays must be trusted for devices with " - + "custom clipboard policy."); - } + if (displayId == Display.INVALID_DISPLAY) { + return displayId; + } - if (mVirtualDisplays.contains(displayId)) { - gwpc.unregisterRunningAppsChangedListener(this); - throw new IllegalStateException( - "Virtual device already has a virtual display with ID " + displayId); + // DisplayManagerService will call onVirtualDisplayCreated() after the display is created, + // while holding its own lock to ensure that this device knows about the display before any + // other display listeners are notified about the display creation. + VirtualDisplayWrapper displayWrapper; + boolean showPointer; + synchronized (mVirtualDeviceLock) { + if (!mVirtualDisplays.contains(displayId)) { + throw new IllegalStateException("Virtual device was not notified about the " + + "creation of display with ID " + displayId); } - - PowerManager.WakeLock wakeLock = - isTrustedDisplay ? createAndAcquireWakeLockForDisplay(displayId) : null; - mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock, - isTrustedDisplay, isMirrorDisplay)); + displayWrapper = mVirtualDisplays.get(displayId); showPointer = mDefaultShowPointerIcon; } + displayWrapper.acquireWakeLock(); + gwpc.registerRunningAppsChangedListener(/* listener= */ this); - final long token = Binder.clearCallingIdentity(); - try { + Binder.withCleanCallingIdentity(() -> { mInputController.setMouseScalingEnabled(false, displayId); mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, displayId); - if (isTrustedDisplay) { + if (displayWrapper.isTrusted()) { mInputController.setShowPointerIcon(showPointer, displayId); mInputController.setDisplayImePolicy(displayId, WindowManager.DISPLAY_IME_POLICY_LOCAL); } else { gwpc.setShowInHostDeviceRecents(true); } - } finally { - Binder.restoreCallingIdentity(token); - } + }); Counter.logIncrementWithUid( "virtual_devices.value_virtual_display_created_count", @@ -1506,7 +1505,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return displayId; } - private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) { + private PowerManager.WakeLock createWakeLockForDisplay(int displayId) { if (Flags.deviceAwareDisplayPower()) { return null; } @@ -1516,7 +1515,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub PowerManager.WakeLock wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG + ":" + displayId, displayId); - wakeLock.acquire(); return wakeLock; } finally { Binder.restoreCallingIdentity(token); @@ -1561,17 +1559,47 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return result; } + /** + * DisplayManagerService is notifying this virtual device about the display creation. This + * should happen before the DisplayManagerInternal#createVirtualDisplay() call above + * returns. + * This is called while holding the DisplayManagerService lock, so no heavy-weight work must + * be done here and especially *** no calls to WindowManager! *** + */ + public void onVirtualDisplayCreated(int displayId, IVirtualDisplayCallback callback, + DisplayWindowPolicyController dwpc) { + final boolean isMirrorDisplay = + mDisplayManagerInternal.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY; + final boolean isTrustedDisplay = + (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) + == Display.FLAG_TRUSTED; + + GenericWindowPolicyController gwpc = (GenericWindowPolicyController) dwpc; + gwpc.setDisplayId(displayId, isMirrorDisplay); + PowerManager.WakeLock wakeLock = + isTrustedDisplay ? createWakeLockForDisplay(displayId) : null; + synchronized (mVirtualDeviceLock) { + if (mVirtualDisplays.contains(displayId)) { + Slog.wtf(TAG, "Virtual device already has a virtual display with ID " + displayId); + return; + } + mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock, + isTrustedDisplay, isMirrorDisplay)); + } + } + + /** + * This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released + * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()). + * At this point, the display is already released, but we still need to release the + * corresponding wakeLock and unregister the RunningAppsChangedListener from corresponding + * WindowPolicyController. + * + * Note that when the display is destroyed during VirtualDeviceImpl.close() call, + * this callback won't be invoked because the display is removed from + * VirtualDeviceManagerService before any resources are released. + */ void onVirtualDisplayRemoved(int displayId) { - /* This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released - * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()). - * At this point, the display is already released, but we still need to release the - * corresponding wakeLock and unregister the RunningAppsChangedListener from corresponding - * WindowPolicyController. - * - * Note that when the display is destroyed during VirtualDeviceImpl.close() call, - * this callback won't be invoked because the display is removed from - * VirtualDeviceManagerService before any resources are released. - */ VirtualDisplayWrapper virtualDisplayWrapper; synchronized (mVirtualDeviceLock) { virtualDisplayWrapper = mVirtualDisplays.removeReturnOld(displayId); @@ -1847,6 +1875,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mWindowPolicyController; } + void acquireWakeLock() { + if (mWakeLock != null && !mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } + void releaseWakeLock() { if (mWakeLock != null && mWakeLock.isHeld()) { mWakeLock.release(); 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 8a0b85859b66..ff82ca00b840 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -40,7 +40,6 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; -import android.companion.virtual.flags.Flags; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtualnative.IVirtualDeviceManagerNative; import android.compat.annotation.ChangeId; @@ -49,6 +48,7 @@ import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.IVirtualDisplayCallback; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -67,6 +67,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.widget.Toast; +import android.window.DisplayWindowPolicyController; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -751,6 +752,16 @@ public class VirtualDeviceManagerService extends SystemService { } @Override + public void onVirtualDisplayCreated(IVirtualDevice virtualDevice, int displayId, + IVirtualDisplayCallback callback, DisplayWindowPolicyController dwpc) { + VirtualDeviceImpl virtualDeviceImpl = getVirtualDeviceForId( + ((VirtualDeviceImpl) virtualDevice).getDeviceId()); + if (virtualDeviceImpl != null) { + virtualDeviceImpl.onVirtualDisplayCreated(displayId, callback, dwpc); + } + } + + @Override public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { VirtualDeviceImpl virtualDeviceImpl = getVirtualDeviceForId( ((VirtualDeviceImpl) virtualDevice).getDeviceId()); 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 471b7b4ddfc8..d412277d2605 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -24,8 +24,10 @@ import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.sensor.VirtualSensor; import android.content.Context; +import android.hardware.display.IVirtualDisplayCallback; import android.os.LocaleList; import android.util.ArraySet; +import android.window.DisplayWindowPolicyController; import java.util.Set; import java.util.function.Consumer; @@ -104,6 +106,17 @@ public abstract class VirtualDeviceManagerInternal { public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid); /** + * Notifies that a virtual display was created. + * + * @param virtualDevice The virtual device that owns the virtual display. + * @param displayId The display id of the created virtual display. + * @param callback The callback of the virtual display. + * @param dwpc The DisplayWindowPolicyController of the created virtual display. + */ + public abstract void onVirtualDisplayCreated(IVirtualDevice virtualDevice, int displayId, + IVirtualDisplayCallback callback, DisplayWindowPolicyController dwpc); + + /** * Notifies that a virtual display is removed. * * @param virtualDevice The virtual device where the virtual display located. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e83efc573ea8..854b0dd7676b 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2041,6 +2041,7 @@ public final class DisplayManagerService extends SystemService { packageName, displayUniqueId, virtualDevice, + dwpc, surface, flags, virtualDisplayConfig); @@ -2135,6 +2136,7 @@ public final class DisplayManagerService extends SystemService { String packageName, String uniqueId, IVirtualDevice virtualDevice, + DisplayWindowPolicyController dwpc, Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) { @@ -2188,6 +2190,16 @@ public final class DisplayManagerService extends SystemService { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); if (display != null) { + // Notify the virtual device that the display has been created. This needs to be called + // in this locked section before the repository had the chance to notify any listeners + // to ensure that the device is aware of the new display before others know about it. + if (virtualDevice != null) { + final VirtualDeviceManagerInternal vdm = + getLocalService(VirtualDeviceManagerInternal.class); + vdm.onVirtualDisplayCreated( + virtualDevice, display.getDisplayIdLocked(), callback, dwpc); + } + return display.getDisplayIdLocked(); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index ffcb96120b19..ab7b4da269db 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -73,6 +73,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.hardware.Sensor; +import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.IDisplayManager; @@ -173,8 +174,7 @@ public class VirtualDeviceManagerServiceTest { private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000; private static final int VIRTUAL_DEVICE_ID_1 = 42; private static final int VIRTUAL_DEVICE_ID_2 = 43; - private static final VirtualDisplayConfig VIRTUAL_DISPLAY_CONFIG = - new VirtualDisplayConfig.Builder("virtual_display", 640, 480, 400).build(); + private static final VirtualDpadConfig DPAD_CONFIG = new VirtualDpadConfig.Builder() .setVendorId(VENDOR_ID) @@ -284,7 +284,12 @@ public class VirtualDeviceManagerServiceTest { private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories, String targetDisplayCategory) { when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(), - eq(VIRTUAL_DEVICE_OWNER_PACKAGE))).thenReturn(DISPLAY_ID_1); + eq(VIRTUAL_DEVICE_OWNER_PACKAGE))) + .thenAnswer(inv -> { + mLocalService.onVirtualDisplayCreated( + mDeviceImpl, DISPLAY_ID_1, inv.getArgument(1), inv.getArgument(3)); + return DISPLAY_ID_1; + }); VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("display", 640, 480, 420).setDisplayCategories(displayCategories).build(); mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback); @@ -997,8 +1002,7 @@ public class VirtualDeviceManagerServiceTest { public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired() throws RemoteException { addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED); - assertThrows(IllegalStateException.class, - () -> addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1)); + addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED); TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), @@ -1871,8 +1875,6 @@ public class VirtualDeviceManagerServiceTest { } private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId, int flags) { - when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback), - eq(virtualDevice), any(), any())).thenReturn(displayId); final String uniqueId = UNIQUE_ID + displayId; doAnswer(inv -> { final DisplayInfo displayInfo = new DisplayInfo(); @@ -1880,7 +1882,22 @@ public class VirtualDeviceManagerServiceTest { displayInfo.flags = flags; return displayInfo; }).when(mDisplayManagerInternalMock).getDisplayInfo(eq(displayId)); - virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback); + + when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback), + eq(virtualDevice), any(), any())).thenAnswer(inv -> { + mLocalService.onVirtualDisplayCreated( + virtualDevice, displayId, mVirtualDisplayCallback, inv.getArgument(3)); + return displayId; + }); + + final int virtualDisplayFlags = (flags & Display.FLAG_TRUSTED) == 0 + ? 0 + : DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + VirtualDisplayConfig virtualDisplayConfig = + new VirtualDisplayConfig.Builder("virtual_display", 640, 480, 400) + .setFlags(virtualDisplayFlags) + .build(); + virtualDevice.createVirtualDisplay(virtualDisplayConfig, mVirtualDisplayCallback); mInputManagerMockHelper.addDisplayIdMapping(uniqueId, displayId); } |