Prevent deadlocks during VirtualDisplay creation

When creating a VirtualDisplay for a VirtualDevice, we need to do some
extra work like setting up for input injection, creating a
DisplayWindowPolicyController, and grabbing a wakelock to keep the
display awake. Some parts of this extra work end up calling back into
the DisplayManagerService from another thread, but since we're already
holding the mSyncRoot lock we sometimes get into situations where
those threads get blocked trying to acquire mSyncRoot, while holding a
lock that another thread ends up needing like the global window
manager one, so we get deadlock.

The fix in this CL is to break up the extra work the
VirtualDeviceManagerService needs to do into two parts: one part that
happens before the VirtualDisplay is created, and one part
after. Neither of these needs mSyncRoot to be held.

Fixes: 230544802
Test: atest CreateVirtualDisplayTest
Change-Id: I6a6e03f816567579510b3f1ef0705375549a3c88
This commit is contained in:
Antony Sargent
2022-05-09 16:12:35 -07:00
parent 0bdc2763a9
commit 725dad7d71
13 changed files with 248 additions and 152 deletions

View File

@@ -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);
}

View File

@@ -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);
}
/**

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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;
@@ -59,6 +60,14 @@ public abstract class DisplayManagerInternal {
public abstract void initPowerManagement(DisplayPowerCallbacks callbacks,
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>

View File

@@ -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,

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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();
@@ -336,14 +385,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) {

View File

@@ -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.
@@ -30,17 +29,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.
*

View File

@@ -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,
@@ -3549,6 +3545,14 @@ public final class DisplayManagerService extends SystemService {
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
}
@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) {

View File

@@ -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());

View File

@@ -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);