diff options
| author | 2022-05-30 00:09:18 +0000 | |
|---|---|---|
| committer | 2022-05-30 00:09:18 +0000 | |
| commit | fc6eb70e0e630af39d5595ea06a23737f8f0f06a (patch) | |
| tree | d4861ff7b8fcb101b5626afa62ebaefb97fd5675 | |
| parent | 9dd35d01f854341f699268c3bf02ac1511dee371 (diff) | |
| parent | 725dad7d717fc641d3ee3adb782270b82ee92cd8 (diff) | |
Merge "Prevent deadlocks during VirtualDisplay creation" into tm-dev
13 files changed, 248 insertions, 152 deletions
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index a1640ee6d2e8..5418f7e93dd9 100644 --- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl +++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl @@ -19,6 +19,8 @@ package android.companion.virtual; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.VirtualDeviceParams; +import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; /** * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService. @@ -42,4 +44,16 @@ interface IVirtualDeviceManager { IVirtualDevice createVirtualDevice( in IBinder token, String packageName, int associationId, in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener); + + /** + * Creates a virtual display owned by a particular virtual device. + * + * @param virtualDisplayConfig The configuration used in creating the display + * @param callback A callback that receives display lifecycle events + * @param virtualDevice The device that will own this display + * @param packageName The package name of the calling app + */ + int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, + in IVirtualDisplayCallback callback, in IVirtualDevice virtualDevice, + String packageName); } diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 3bb837317ad4..1b93bb851567 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -33,6 +33,8 @@ import android.content.Context; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.VirtualDisplayFlag; +import android.hardware.display.DisplayManagerGlobal; +import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.VirtualKeyboard; @@ -65,7 +67,7 @@ import java.util.function.IntConsumer; public final class VirtualDeviceManager { private static final boolean DEBUG = false; - private static final String LOG_TAG = "VirtualDeviceManager"; + private static final String TAG = "VirtualDeviceManager"; private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC @@ -150,6 +152,7 @@ public final class VirtualDeviceManager { public static class VirtualDevice implements AutoCloseable { private final Context mContext; + private final IVirtualDeviceManager mService; private final IVirtualDevice mVirtualDevice; private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners = new ArrayMap<>(); @@ -189,6 +192,7 @@ public final class VirtualDeviceManager { Context context, int associationId, VirtualDeviceParams params) throws RemoteException { + mService = service; mContext = context.getApplicationContext(); mVirtualDevice = service.createVirtualDevice( new Binder(), @@ -274,18 +278,23 @@ public final class VirtualDeviceManager { // TODO(b/205343547): Handle display groups properly instead of creating a new display // group for every new virtual display created using this API. // belongs to the same display group. - DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); - // DisplayManager will call into VirtualDeviceManagerInternal to register the - // created displays. - return displayManager.createVirtualDisplay( - mVirtualDevice, - new VirtualDisplayConfig.Builder( - getVirtualDisplayName(), width, height, densityDpi) - .setSurface(surface) - .setFlags(getVirtualDisplayFlags(flags)) - .build(), - callback, - executor); + VirtualDisplayConfig config = new VirtualDisplayConfig.Builder( + getVirtualDisplayName(), width, height, densityDpi) + .setSurface(surface) + .setFlags(getVirtualDisplayFlags(flags)) + .build(); + IVirtualDisplayCallback callbackWrapper = + new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor); + final int displayId; + try { + displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice, + mContext.getPackageName()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); + return displayManager.createVirtualDisplayWrapper(config, mContext, callbackWrapper, + displayId); } /** diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index eadcac91dcd7..b505395a091a 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -31,7 +31,6 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.KeyguardManager; -import android.companion.virtual.IVirtualDevice; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; @@ -971,17 +970,8 @@ public final class DisplayManager { executor = new HandlerExecutor( Handler.createAsync(handler != null ? handler.getLooper() : Looper.myLooper())); } - return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */, - virtualDisplayConfig, callback, executor, windowContext); - } - - /** @hide */ - public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice, - @NonNull VirtualDisplayConfig virtualDisplayConfig, - @Nullable VirtualDisplay.Callback callback, - @Nullable Executor executor) { - return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice, - virtualDisplayConfig, callback, executor, null); + return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback, + executor, windowContext); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index a62bbf623000..74356ddecc76 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -24,7 +24,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PropertyInvalidatedCache; -import android.companion.virtual.IVirtualDevice; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -586,18 +585,28 @@ public final class DisplayManagerGlobal { } public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection, - IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig, - VirtualDisplay.Callback callback, @Nullable Executor executor, - @Nullable Context windowContext) { + @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback, + @Nullable Executor executor, @Nullable Context windowContext) { VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor); IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; int displayId; try { displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper, - projectionToken, virtualDevice, context.getPackageName()); + projectionToken, context.getPackageName()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } + return createVirtualDisplayWrapper(virtualDisplayConfig, windowContext, callbackWrapper, + displayId); + } + + /** + * Create a VirtualDisplay wrapper object for a newly created virtual display ; to be called + * once the display has been created in system_server. + */ + @Nullable + public VirtualDisplay createVirtualDisplayWrapper(VirtualDisplayConfig virtualDisplayConfig, + Context windowContext, IVirtualDisplayCallback callbackWrapper, int displayId) { if (displayId < 0) { Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName()); return null; @@ -1050,7 +1059,10 @@ public final class DisplayManagerGlobal { } } - private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { + /** + * Assists in dispatching VirtualDisplay lifecycle event callbacks on a given Executor. + */ + public static final class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { @Nullable private final VirtualDisplay.Callback mCallback; @Nullable private final Executor mExecutor; @@ -1062,7 +1074,7 @@ public final class DisplayManagerGlobal { * @param executor The executor to call the {@code callback} on. Must not be {@code null} if * the callback is not {@code null}. */ - VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) { + public VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) { mCallback = callback; mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null; } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index be482c9146c9..1c3bec5b52f4 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.annotation.IntDef; import android.annotation.Nullable; +import android.companion.virtual.IVirtualDevice; import android.graphics.Point; import android.hardware.SensorManager; import android.os.Handler; @@ -60,6 +61,14 @@ public abstract class DisplayManagerInternal { Handler handler, SensorManager sensorManager); /** + * Called by the VirtualDeviceManagerService to create a VirtualDisplay owned by a + * VirtualDevice. + */ + public abstract int createVirtualDisplay(VirtualDisplayConfig config, + IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, + DisplayWindowPolicyController dwpc, String packageName); + + /** * Called by the power manager to request a new power state. * <p> * The display power controller makes a copy of the provided object and then diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index ddd18f4502e7..ca3e58094400 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -16,7 +16,6 @@ package android.hardware.display; -import android.companion.virtual.IVirtualDevice; import android.content.pm.ParceledListSlice; import android.graphics.Point; import android.hardware.display.BrightnessConfiguration; @@ -88,10 +87,10 @@ interface IDisplayManager { void requestColorMode(int displayId, int colorMode); // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate - // MediaProjection token or VirtualDevice for certain combinations of flags. + // MediaProjection token for certain combinations of flags. int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, in IVirtualDisplayCallback callback, in IMediaProjection projectionToken, - in IVirtualDevice virtualDevice, String packageName); + String packageName); // No permissions required, but must be same Uid as the creator. void resizeVirtualDisplay(in IVirtualDisplayCallback token, diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 4473a095063a..43e2b881927c 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -48,7 +48,6 @@ import com.android.internal.app.BlockedAppStreamingActivity; import java.util.List; import java.util.Set; -import java.util.function.Consumer; /** @@ -66,6 +65,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController void onRunningAppsChanged(ArraySet<Integer> runningUids); } + /** + * For communicating when activities are blocked from running on the display by this policy + * controller. + */ + public interface ActivityBlockedCallback { + /** Called when an activity is blocked.*/ + void onActivityBlocked(int displayId, ActivityInfo activityInfo); + } private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT = new ComponentName("android", BlockedAppStreamingActivity.class.getName()); @@ -89,7 +96,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private final Object mGenericWindowPolicyControllerLock = new Object(); @ActivityPolicy private final int mDefaultActivityPolicy; - private final Consumer<ActivityInfo> mActivityBlockedCallback; + private final ActivityBlockedCallback mActivityBlockedCallback; + private int mDisplayId = Display.INVALID_DISPLAY; @NonNull @GuardedBy("mGenericWindowPolicyControllerLock") @@ -98,6 +106,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private final Handler mHandler = new Handler(Looper.getMainLooper()); private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener = new ArraySet<>(); + @Nullable private final @AssociationRequest.DeviceProfile String mDeviceProfile; /** @@ -119,8 +128,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED} * @param defaultActivityPolicy Whether activities are default allowed to be displayed or * blocked. - * @param activityListener Activity listener to listen for activity changes. The display ID - * is not populated in this callback and is always {@link Display#INVALID_DISPLAY}. + * @param activityListener Activity listener to listen for activity changes. * @param activityBlockedCallback Callback that is called when an activity is blocked from * launching. * @param deviceProfile The {@link AssociationRequest.DeviceProfile} of this virtual device. @@ -133,7 +141,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull Set<ComponentName> blockedActivities, @ActivityPolicy int defaultActivityPolicy, @NonNull ActivityListener activityListener, - @NonNull Consumer<ActivityInfo> activityBlockedCallback, + @NonNull ActivityBlockedCallback activityBlockedCallback, @AssociationRequest.DeviceProfile String deviceProfile) { super(); mAllowedUsers = allowedUsers; @@ -148,6 +156,13 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mDeviceProfile = deviceProfile; } + /** + * Expected to be called once this object is associated with a newly created display. + */ + public void setDisplayId(int displayId) { + mDisplayId = displayId; + } + /** Register a listener for running applications changes. */ public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { mRunningAppsChangedListener.add(listener); @@ -169,7 +184,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController for (int i = 0; i < activityCount; i++) { final ActivityInfo aInfo = activities.get(i); if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { - mActivityBlockedCallback.accept(aInfo); + mActivityBlockedCallback.onActivityBlocked(mDisplayId, aInfo); return false; } } @@ -191,7 +206,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController } if (!canContainActivity(activityInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { - mActivityBlockedCallback.accept(activityInfo); + mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); return false; } @@ -201,14 +216,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController if (isNewTask && !mBlockedCrossTaskNavigations.isEmpty() && mBlockedCrossTaskNavigations.contains(activityComponent)) { Slog.d(TAG, "Virtual device blocking cross task navigation of " + activityComponent); - mActivityBlockedCallback.accept(activityInfo); + mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); return false; } if (isNewTask && !mAllowedCrossTaskNavigations.isEmpty() && !mAllowedCrossTaskNavigations.contains(activityComponent)) { Slog.d(TAG, "Virtual device not allowing cross task navigation of " + activityComponent); - mActivityBlockedCallback.accept(activityInfo); + mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); return false; } @@ -220,7 +235,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags) { if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) { - mActivityBlockedCallback.accept(activityInfo); + mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo); return false; } return true; @@ -234,7 +249,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController if (mActivityListener != null && topActivity != null) { // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> - mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity)); + mActivityListener.onTopActivityChanged(mDisplayId, topActivity)); } } @@ -245,7 +260,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mRunningUids.addAll(runningUids); if (mActivityListener != null && mRunningUids.isEmpty()) { // Post callback on the main thread so it doesn't block activity launching - mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY)); + mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId)); } } mHandler.post(() -> { @@ -257,7 +272,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Override public boolean canShowTasksInRecents() { - // TODO(b/234075973) : Remove this once proper API is ready. + if (mDeviceProfile == null) { + return true; + } + // TODO(b/234075973) : Remove this once proper API is ready. switch (mDeviceProfile) { case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION: return false; 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 33fd83bf6bd7..3b3bf11f2d9e 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -64,7 +64,6 @@ import android.util.SparseArray; 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; @@ -110,11 +109,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @GuardedBy("mVirtualDeviceLock") private boolean mDefaultShowPointerIcon = true; - private ActivityListener createListenerAdapter(int displayId) { + private ActivityListener createListenerAdapter() { return new ActivityListener() { @Override - public void onTopActivityChanged(int unusedDisplayId, ComponentName topActivity) { + public void onTopActivityChanged(int displayId, ComponentName topActivity) { try { mActivityListener.onTopActivityChanged(displayId, topActivity); } catch (RemoteException e) { @@ -123,7 +122,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } @Override - public void onDisplayEmpty(int unusedDisplayId) { + public void onDisplayEmpty(int displayId) { try { mActivityListener.onDisplayEmpty(displayId); } catch (RemoteException e) { @@ -529,24 +528,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mInputController.dump(fout); } - DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) { + GenericWindowPolicyController createWindowPolicyController() { synchronized (mVirtualDeviceLock) { - if (mVirtualDisplayIds.contains(displayId)) { - throw new IllegalStateException( - "Virtual device already have a virtual display with ID " + displayId); - } - mVirtualDisplayIds.add(displayId); - mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); - mInputController.setPointerAcceleration(1f, displayId); - mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, - displayId); - mInputController.setLocalIme(displayId); - - // Since we're being called in the middle of the display being created, we post a - // task to grab the wakelock instead of doing it synchronously here, to avoid - // reentrancy problems. - mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId)); - final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, @@ -556,19 +539,36 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getAllowedActivities(), mParams.getBlockedActivities(), mParams.getDefaultActivityPolicy(), - createListenerAdapter(displayId), - activityInfo -> onActivityBlocked(displayId, activityInfo), + createListenerAdapter(), + this::onActivityBlocked, mAssociationInfo.getDeviceProfile()); gwpc.registerRunningAppsChangedListener(/* listener= */ this); - mWindowPolicyControllers.put(displayId, gwpc); return gwpc; } } - void addWakeLockForDisplay(int displayId) { + void onVirtualDisplayCreatedLocked(GenericWindowPolicyController gwpc, int displayId) { synchronized (mVirtualDeviceLock) { - if (!mVirtualDisplayIds.contains(displayId) - || mPerDisplayWakelocks.containsKey(displayId)) { + if (displayId == Display.INVALID_DISPLAY) { + return; + } + if (mVirtualDisplayIds.contains(displayId)) { + throw new IllegalStateException( + "Virtual device already has a virtual display with ID " + displayId); + } + mVirtualDisplayIds.add(displayId); + + gwpc.setDisplayId(displayId); + mWindowPolicyControllers.put(displayId, gwpc); + + mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); + mInputController.setPointerAcceleration(1f, displayId); + mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, + displayId); + mInputController.setLocalIme(displayId); + + + if (mPerDisplayWakelocks.containsKey(displayId)) { Slog.e(TAG, "Not creating wakelock for displayId " + displayId); return; } @@ -576,8 +576,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub PowerManager.WakeLock wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG + ":" + displayId, displayId); - wakeLock.acquire(); mPerDisplayWakelocks.put(displayId, wakeLock); + wakeLock.acquire(); } } 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 6398b2142e37..35e9060b58d4 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -31,6 +31,10 @@ import android.companion.virtual.IVirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.content.Context; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -41,9 +45,9 @@ import android.util.ExceptionUtils; import android.util.Slog; import android.util.SparseArray; import android.widget.Toast; -import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.SystemService; import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline; @@ -203,6 +207,7 @@ public class VirtualDeviceManagerService extends SystemService { } } + @VisibleForTesting class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements VirtualDeviceImpl.PendingTrampolineCallback { @@ -265,6 +270,50 @@ public class VirtualDeviceManagerService extends SystemService { } } + @Override // Binder call + public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, + IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName) + throws RemoteException { + final int callingUid = getCallingUid(); + if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { + throw new SecurityException( + "Package name " + packageName + " does not belong to calling uid " + + callingUid); + } + VirtualDeviceImpl virtualDeviceImpl; + synchronized (mVirtualDeviceManagerLock) { + virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getAssociationId()); + if (virtualDeviceImpl == null) { + throw new SecurityException("Invalid VirtualDevice"); + } + } + if (virtualDeviceImpl.getOwnerUid() != callingUid) { + throw new SecurityException( + "uid " + callingUid + + " is not the owner of the supplied VirtualDevice"); + } + GenericWindowPolicyController gwpc; + final long token = Binder.clearCallingIdentity(); + try { + gwpc = virtualDeviceImpl.createWindowPolicyController(); + } finally { + Binder.restoreCallingIdentity(token); + } + + DisplayManagerInternal displayManager = getLocalService( + DisplayManagerInternal.class); + int displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback, + virtualDevice, gwpc, packageName); + + final long tokenTwo = Binder.clearCallingIdentity(); + try { + virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId); + return displayId; + } finally { + Binder.restoreCallingIdentity(tokenTwo); + } + } + @Nullable private AssociationInfo getAssociationInfo(String packageName, int associationId) { final int callingUserId = getCallingUserHandle().getIdentifier(); @@ -337,14 +386,6 @@ public class VirtualDeviceManagerService extends SystemService { } @Override - public DisplayWindowPolicyController onVirtualDisplayCreated(IVirtualDevice virtualDevice, - int displayId) { - synchronized (mVirtualDeviceManagerLock) { - return ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayCreatedLocked(displayId); - } - } - - @Override public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { synchronized (mVirtualDeviceManagerLock) { ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId); 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 94d3d15aec33..f2b4d42c8758 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -17,7 +17,6 @@ package com.android.server.companion.virtual; import android.companion.virtual.IVirtualDevice; -import android.window.DisplayWindowPolicyController; /** * Virtual device manager local service interface. @@ -31,17 +30,6 @@ public abstract class VirtualDeviceManagerInternal { public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice); /** - * Notify a virtual display is created. - * - * @param virtualDevice The virtual device where the virtual display located. - * @param displayId The display id of the created virtual display. - * - * @return The {@link DisplayWindowPolicyController} of the virtual device. - */ - public abstract DisplayWindowPolicyController onVirtualDisplayCreated( - IVirtualDevice virtualDevice, int displayId); - - /** * Notify 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 e77693650eda..ec7ccc4843c9 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1231,7 +1231,7 @@ public final class DisplayManagerService extends SystemService { private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IMediaProjection projection, - IVirtualDevice virtualDevice, String packageName) { + IVirtualDevice virtualDevice, DisplayWindowPolicyController dwpc, String packageName) { final int callingUid = Binder.getCallingUid(); if (!validatePackageName(callingUid, packageName)) { throw new SecurityException("packageName must match the calling uid"); @@ -1351,8 +1351,13 @@ public final class DisplayManagerService extends SystemService { final long token = Binder.clearCallingIdentity(); try { synchronized (mSyncRoot) { - return createVirtualDisplayLocked(callback, projection, virtualDevice, callingUid, + final int displayId = createVirtualDisplayLocked(callback, projection, callingUid, packageName, surface, flags, virtualDisplayConfig); + if (displayId != Display.INVALID_DISPLAY && virtualDevice != null && dwpc != null) { + mDisplayWindowPolicyControllers.put(displayId, + Pair.create(virtualDevice, dwpc)); + } + return displayId; } } finally { Binder.restoreCallingIdentity(token); @@ -1360,8 +1365,7 @@ public final class DisplayManagerService extends SystemService { } private int createVirtualDisplayLocked(IVirtualDisplayCallback callback, - IMediaProjection projection, IVirtualDevice virtualDevice, - int callingUid, String packageName, Surface surface, + IMediaProjection projection, int callingUid, String packageName, Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) { if (mVirtualDisplayAdapter == null) { Slog.w(TAG, "Rejecting request to create private virtual display " @@ -1389,16 +1393,7 @@ public final class DisplayManagerService extends SystemService { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); if (display != null) { - final int displayId = display.getDisplayIdLocked(); - if (virtualDevice != null) { - final VirtualDeviceManagerInternal vdm = - getLocalService(VirtualDeviceManagerInternal.class); - final DisplayWindowPolicyController controller = - vdm.onVirtualDisplayCreated(virtualDevice, displayId); - mDisplayWindowPolicyControllers.put(displayId, - Pair.create(virtualDevice, controller)); - } - return displayId; + return display.getDisplayIdLocked(); } // Something weird happened and the logical display was not created. @@ -3066,9 +3061,9 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IMediaProjection projection, - IVirtualDevice virtualDeviceToken, String packageName) { + String packageName) { return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection, - virtualDeviceToken, packageName); + null, null, packageName); } @Override // Binder call @@ -3534,7 +3529,8 @@ public final class DisplayManagerService extends SystemService { return !Float.isNaN(refreshRate) && (refreshRate > 0.0f); } - private final class LocalService extends DisplayManagerInternal { + @VisibleForTesting + final class LocalService extends DisplayManagerInternal { @Override public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler, @@ -3550,6 +3546,14 @@ public final class DisplayManagerService extends SystemService { } @Override + public int createVirtualDisplay(VirtualDisplayConfig config, + IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, + DisplayWindowPolicyController dwpc, String packageName) { + return createVirtualDisplayInternal(config, callback, null, virtualDevice, dwpc, + packageName); + } + + @Override public boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { 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 f9671e56fe12..f242fda15f06 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 @@ -228,36 +228,36 @@ public class VirtualDeviceManagerServiceTest { @Test public void onVirtualDisplayRemovedLocked_doesNotThrowException() { - final int displayId = 2; - mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); // This call should not throw any exceptions. - mDeviceImpl.onVirtualDisplayRemovedLocked(displayId); + mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); } @Test public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException { - final int displayId = 2; - mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), nullable(String.class), anyInt(), eq(null)); - TestableLooper.get(this).processAllMessages(); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId), eq(null)); + nullable(String.class), eq(DISPLAY_ID), eq(null)); } @Test public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired() throws RemoteException { - final int displayId = 2; - mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); + GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController(); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); assertThrows(IllegalStateException.class, - () -> mDeviceImpl.onVirtualDisplayCreatedLocked(displayId)); + () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID)); TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId), eq(null)); + nullable(String.class), eq(DISPLAY_ID), eq(null)); } @Test @@ -269,30 +269,30 @@ public class VirtualDeviceManagerServiceTest { @Test public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException { - final int displayId = 2; - mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId), eq(null)); + nullable(String.class), eq(DISPLAY_ID), eq(null)); IBinder wakeLock = wakeLockCaptor.getValue(); - mDeviceImpl.onVirtualDisplayRemovedLocked(displayId); + mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt()); } @Test public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException { - final int displayId = 2; - mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId), eq(null)); + nullable(String.class), eq(DISPLAY_ID), eq(null)); IBinder wakeLock = wakeLockCaptor.getValue(); // Close the VirtualDevice without first notifying it of the VirtualDisplay removal. @@ -416,7 +416,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void onAudioSessionStarting_hasVirtualAudioController() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); @@ -425,7 +426,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void onAudioSessionEnded_noVirtualAudioController() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); mDeviceImpl.onAudioSessionEnded(); @@ -435,7 +437,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void close_cleanVirtualAudioController() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); mDeviceImpl.close(); @@ -659,7 +662,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); @@ -677,7 +681,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); @@ -695,7 +700,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); @@ -713,7 +719,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openVendingOnVirtualDisplay_startBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); @@ -731,7 +738,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); @@ -749,7 +757,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() { - mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( DISPLAY_ID); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 6f48368c6ab2..1e97c1c5c5bc 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -45,6 +45,7 @@ import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; +import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -63,6 +64,7 @@ import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; +import android.window.DisplayWindowPolicyController; import androidx.test.InstrumentationRegistry; import androidx.test.core.app.ApplicationProvider; @@ -220,7 +222,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); builder.setFlags(flags); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -346,7 +348,7 @@ public class DisplayManagerServiceTest { builder.setFlags(flags); builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -586,8 +588,7 @@ public class DisplayManagerServiceTest { VIRTUAL_DISPLAY_NAME, width, height, dpi); builder.setUniqueId(uniqueId); final int firstDisplayId = binderService.createVirtualDisplay(builder.build(), - mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */, - PACKAGE_NAME); + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); // The second virtual display requests to mirror the first virtual display. final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2"; @@ -598,7 +599,7 @@ public class DisplayManagerServiceTest { builder2.setDisplayIdToMirror(firstDisplayId); final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(), mMockAppToken2 /* callback */, null /* projection */, - null /* virtualDeviceToken */, PACKAGE_NAME); + PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); // flush the handler @@ -636,8 +637,7 @@ public class DisplayManagerServiceTest { builder.setSurface(surface); builder.setUniqueId(uniqueId); final int displayId = binderService.createVirtualDisplay(builder.build(), - mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */, - PACKAGE_NAME); + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -669,7 +669,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP"); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId); @@ -700,7 +700,7 @@ public class DisplayManagerServiceTest { try { bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + null /* projection */, PACKAGE_NAME); fail("Creating virtual display with VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP without " + "ADD_TRUSTED_DISPLAY permission should throw SecurityException."); } catch (SecurityException e) { @@ -716,6 +716,8 @@ public class DisplayManagerServiceTest { public void testOwnDisplayGroup_allowCreationWithVirtualDevice() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + registerDefaultDisplays(displayManager); DisplayManagerService.BinderService bs = displayManager.new BinderService(); @@ -733,8 +735,9 @@ public class DisplayManagerServiceTest { when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice)) .thenReturn(true); - int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, virtualDevice /* virtualDeviceToken */, PACKAGE_NAME); + int displayId = localService.createVirtualDisplay(builder.build(), + mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */, + mock(DisplayWindowPolicyController.class), PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId); |