Add API to hide cursor for virtual mice

Bug: 216244627
Test: atest FrameworksServicesTests:com.android.server.companion.virtual
CTS-Coverage-Bug: 208247880
Change-Id: Ie1a2bc526a705f2f9788898afbff87f5a9448f85
This commit is contained in:
Christine Franks
2022-01-25 00:23:18 -08:00
parent 4081821956
commit 27a354bf40
9 changed files with 313 additions and 76 deletions

View File

@@ -2742,6 +2742,7 @@ package android.companion.virtual {
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.LaunchCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
}
public final class VirtualDeviceParams implements android.os.Parcelable {

View File

@@ -77,4 +77,7 @@ interface IVirtualDevice {
void launchPendingIntent(
int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
PointF getCursorPosition(IBinder token);
/** Sets whether to show or hide the cursor while this virtual device is active. */
void setShowPointerIcon(boolean showPointerIcon);
}

View File

@@ -337,6 +337,22 @@ public final class VirtualDeviceManager {
}
}
/**
* Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
*
* @param showPointerIcon True if the pointer should be shown; false otherwise. The default
* visibility is true.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
public void setShowPointerIcon(boolean showPointerIcon) {
try {
mVirtualDevice.setShowPointerIcon(showPointerIcon);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the display flags that should be added to a particular virtual display.
* Additional device-level flags from {@link

View File

@@ -87,6 +87,12 @@ public abstract class InputManagerInternal {
*/
public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
/**
* Gets the display id that the MouseCursorController is being forced to target. Returns
* {@link android.view.Display#INVALID_DISPLAY} if there is no override
*/
public abstract int getVirtualMousePointerDisplayId();
/** Gets the current position of the mouse cursor. */
public abstract PointF getCursorPosition();
@@ -94,7 +100,7 @@ public abstract class InputManagerInternal {
* Sets the pointer acceleration.
* See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}.
*/
public abstract void setPointerAcceleration(float acceleration);
public abstract void setPointerAcceleration(float acceleration, int displayId);
/**
* Sets the eligibility of windows on a given display for pointer capture. If a display is
@@ -103,6 +109,9 @@ public abstract class InputManagerInternal {
*/
public abstract void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible);
/** Sets the visibility of the cursor. */
public abstract void setPointerIconVisible(boolean visible, int displayId);
/** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);

View File

@@ -30,7 +30,6 @@ import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualTouchEvent;
import android.os.IBinder;
import android.os.IInputConstants;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
@@ -43,6 +42,7 @@ import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@@ -76,13 +76,6 @@ class InputController {
private final DisplayManagerInternal mDisplayManagerInternal;
private final InputManagerInternal mInputManagerInternal;
/**
* Because the pointer is a singleton, it can only be targeted at one display at a time. Because
* multiple mice could be concurrently registered, mice that are associated with a different
* display than the current target display should not be allowed to affect the current target.
*/
@VisibleForTesting int mActivePointerDisplayId;
InputController(@NonNull Object lock) {
this(lock, new NativeWrapper());
}
@@ -91,18 +84,21 @@ class InputController {
InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
mLock = lock;
mNativeWrapper = nativeWrapper;
mActivePointerDisplayId = Display.INVALID_DISPLAY;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
void close() {
synchronized (mLock) {
for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator =
mInputDeviceDescriptors.entrySet().iterator();
if (iterator.hasNext()) {
final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next();
final IBinder token = entry.getKey();
final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue();
iterator.remove();
closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
mInputDeviceDescriptors.clear();
resetMouseValuesLocked();
}
}
@@ -150,8 +146,6 @@ class InputController {
new InputDeviceDescriptor(fd, binderDeathRecipient,
InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
mInputManagerInternal.setPointerAcceleration(1);
mActivePointerDisplayId = displayId;
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -197,23 +191,44 @@ class InputController {
throw new IllegalArgumentException(
"Could not unregister input device for given token");
}
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
// Reset values to the default if all virtual mice are unregistered, or set display
// id if there's another mouse (choose the most recent).
if (inputDeviceDescriptor.isMouse()) {
updateMouseValuesLocked();
}
closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
}
@GuardedBy("mLock")
private void updateMouseValuesLocked() {
private void closeInputDeviceDescriptorLocked(IBinder token,
InputDeviceDescriptor inputDeviceDescriptor) {
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
// Reset values to the default if all virtual mice are unregistered, or set display
// id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be
// removed from the mInputDeviceDescriptors instance variable prior to this point.
if (inputDeviceDescriptor.isMouse()) {
if (mInputManagerInternal.getVirtualMousePointerDisplayId()
== inputDeviceDescriptor.getDisplayId()) {
updateActivePointerDisplayIdLocked();
}
}
}
void setShowPointerIcon(boolean visible, int displayId) {
mInputManagerInternal.setPointerIconVisible(visible, displayId);
}
void setPointerAcceleration(float pointerAcceleration, int displayId) {
mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId);
}
void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible);
}
@GuardedBy("mLock")
private void updateActivePointerDisplayIdLocked() {
InputDeviceDescriptor mostRecentlyCreatedMouse = null;
for (InputDeviceDescriptor otherInputDeviceDescriptor :
mInputDeviceDescriptors.values()) {
for (InputDeviceDescriptor otherInputDeviceDescriptor : mInputDeviceDescriptors.values()) {
if (otherInputDeviceDescriptor.isMouse()) {
if (mostRecentlyCreatedMouse == null
|| (otherInputDeviceDescriptor.getCreationOrderNumber()
@@ -225,20 +240,12 @@ class InputController {
if (mostRecentlyCreatedMouse != null) {
mInputManagerInternal.setVirtualMousePointerDisplayId(
mostRecentlyCreatedMouse.getDisplayId());
mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
} else {
// All mice have been unregistered; reset all values.
resetMouseValuesLocked();
// All mice have been unregistered
mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
}
}
private void resetMouseValuesLocked() {
mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
mInputManagerInternal.setPointerAcceleration(
IInputConstants.DEFAULT_POINTER_ACCELERATION);
mActivePointerDisplayId = Display.INVALID_DISPLAY;
}
private static String createPhys(@PhysType String type) {
return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
}
@@ -269,7 +276,8 @@ class InputController {
throw new IllegalArgumentException(
"Could not send button event to input device for given token");
}
if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -300,7 +308,8 @@ class InputController {
throw new IllegalArgumentException(
"Could not send relative event to input device for given token");
}
if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -317,7 +326,8 @@ class InputController {
throw new IllegalArgumentException(
"Could not send scroll event to input device for given token");
}
if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -334,7 +344,8 @@ class InputController {
throw new IllegalArgumentException(
"Could not get cursor position for input device for given token");
}
if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
if (inputDeviceDescriptor.getDisplayId()
!= mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -354,7 +365,6 @@ class InputController {
fout.println(" type: " + inputDeviceDescriptor.getType());
fout.println(" phys: " + inputDeviceDescriptor.getPhys());
}
fout.println(" Active mouse display id: " + mActivePointerDisplayId);
}
}

View File

@@ -37,7 +37,6 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -56,8 +55,8 @@ import android.util.Slog;
import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -83,6 +82,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final VirtualDeviceParams mParams;
private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListener;
// The default setting for showing the pointer on new displays.
@GuardedBy("mVirtualDeviceLock")
private boolean mDefaultShowPointerIcon = true;
private ActivityListener createListenerAdapter(int displayId) {
return new ActivityListener() {
@@ -382,6 +384,25 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
@Override // Binder call
public void setShowPointerIcon(boolean showPointerIcon) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"Permission required to unregister this input device");
final long binderToken = Binder.clearCallingIdentity();
try {
synchronized (mVirtualDeviceLock) {
mDefaultShowPointerIcon = showPointerIcon;
for (int displayId : mVirtualDisplayIds) {
mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
}
}
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
@@ -392,6 +413,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
for (int id : mVirtualDisplayIds) {
fout.println(" " + id);
}
fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
}
mInputController.dump(fout);
}
@@ -403,15 +425,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"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);
// 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));
LocalServices.getService(
InputManagerInternal.class).setDisplayEligibilityForPointerCapture(displayId,
false);
final GenericWindowPolicyController dwpc =
new GenericWindowPolicyController(FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
@@ -471,9 +494,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mPerDisplayWakelocks.remove(displayId);
}
mVirtualDisplayIds.remove(displayId);
LocalServices.getService(
InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
displayId, true);
mWindowPolicyControllers.remove(displayId);
}
}

View File

@@ -64,6 +64,7 @@ import android.os.CombinedVibration;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInputConstants;
import android.os.IVibratorStateListener;
import android.os.InputEventInjectionResult;
import android.os.InputEventInjectionSync;
@@ -274,10 +275,19 @@ public class InputManagerService extends IInputManager.Stub
private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
@GuardedBy("mAssociationLock")
private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
private final Object mPointerDisplayIdLock = new Object();
private final Object mAdditionalDisplayInputPropertiesLock = new Object();
// Forces the MouseCursorController to target a specific display id.
@GuardedBy("mPointerDisplayIdLock")
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties =
new SparseArray<>();
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private int mIconType = PointerIcon.TYPE_NOT_SPECIFIED;
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private PointerIcon mIcon;
// Holds all the registered gesture monitors that are implemented as spy windows. The spy
@@ -609,11 +619,26 @@ public class InputManagerService extends IInputManager.Stub
}
private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
synchronized (mAdditionalDisplayInputPropertiesLock) {
final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
for (int i = viewports.size() - 1; i >= 0; --i) {
vArray[i] = viewports.get(i);
}
nativeSetDisplayViewports(mPtr, vArray);
nativeSetDisplayViewports(mPtr, vArray);
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
if (properties != null) {
updatePointerIconVisibleLocked(properties.pointerIconVisible);
updatePointerAccelerationLocked(properties.pointerAcceleration);
return;
}
}
updatePointerIconVisibleLocked(
AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE);
updatePointerAccelerationLocked(IInputConstants.DEFAULT_POINTER_ACCELERATION);
}
}
/**
@@ -1850,10 +1875,60 @@ public class InputManagerService extends IInputManager.Stub
nativeSetPointerSpeed(mPtr, speed);
}
private void setPointerAcceleration(float acceleration) {
private void setPointerAcceleration(float acceleration, int displayId) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(displayId);
if (properties == null) {
properties = new AdditionalDisplayInputProperties();
mAdditionalDisplayInputProperties.put(displayId, properties);
}
properties.pointerAcceleration = acceleration;
if (properties.allDefaults()) {
mAdditionalDisplayInputProperties.remove(displayId);
}
if (mOverriddenPointerDisplayId == displayId) {
updatePointerAccelerationLocked(acceleration);
}
}
}
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private void updatePointerAccelerationLocked(float acceleration) {
nativeSetPointerAcceleration(mPtr, acceleration);
}
private void setPointerIconVisible(boolean visible, int displayId) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(displayId);
if (properties == null) {
properties = new AdditionalDisplayInputProperties();
mAdditionalDisplayInputProperties.put(displayId, properties);
}
properties.pointerIconVisible = visible;
if (properties.allDefaults()) {
mAdditionalDisplayInputProperties.remove(displayId);
}
if (mOverriddenPointerDisplayId == displayId) {
updatePointerIconVisibleLocked(visible);
}
}
}
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private void updatePointerIconVisibleLocked(boolean visible) {
if (visible) {
if (mIconType == PointerIcon.TYPE_CUSTOM) {
nativeSetCustomPointerIcon(mPtr, mIcon);
} else {
nativeSetPointerIconType(mPtr, mIconType);
}
} else {
nativeSetPointerIconType(mPtr, PointerIcon.TYPE_NULL);
}
}
private void registerPointerSpeedSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
@@ -1988,13 +2063,27 @@ public class InputManagerService extends IInputManager.Stub
}
private void setVirtualMousePointerDisplayId(int displayId) {
synchronized (mPointerDisplayIdLock) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
mOverriddenPointerDisplayId = displayId;
if (displayId != Display.INVALID_DISPLAY) {
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(displayId);
if (properties != null) {
updatePointerAccelerationLocked(properties.pointerAcceleration);
updatePointerIconVisibleLocked(properties.pointerIconVisible);
}
}
}
// TODO(b/215597605): trigger MousePositionTracker update
nativeNotifyPointerDisplayIdChanged(mPtr);
}
private int getVirtualMousePointerDisplayId() {
synchronized (mAdditionalDisplayInputPropertiesLock) {
return mOverriddenPointerDisplayId;
}
}
private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible);
}
@@ -2283,15 +2372,44 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
public void setPointerIconType(int iconId) {
nativeSetPointerIconType(mPtr, iconId);
public void setPointerIconType(int iconType) {
if (iconType == PointerIcon.TYPE_CUSTOM) {
throw new IllegalArgumentException("Use setCustomPointerIcon to set custom pointers");
}
synchronized (mAdditionalDisplayInputPropertiesLock) {
mIcon = null;
mIconType = iconType;
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
if (properties == null || properties.pointerIconVisible) {
nativeSetPointerIconType(mPtr, mIconType);
}
} else {
nativeSetPointerIconType(mPtr, mIconType);
}
}
}
// Binder call
@Override
public void setCustomPointerIcon(PointerIcon icon) {
Objects.requireNonNull(icon);
nativeSetCustomPointerIcon(mPtr, icon);
synchronized (mAdditionalDisplayInputPropertiesLock) {
mIconType = PointerIcon.TYPE_CUSTOM;
mIcon = icon;
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
if (properties == null || properties.pointerIconVisible) {
// Only set the icon if it is not currently hidden; otherwise, it will be set
// once it's no longer hidden.
nativeSetCustomPointerIcon(mPtr, mIcon);
}
} else {
nativeSetCustomPointerIcon(mPtr, mIcon);
}
}
}
/**
@@ -2620,6 +2738,7 @@ public class InputManagerService extends IInputManager.Stub
pw.println("Input Manager Service (Java) State:");
dumpAssociations(pw, " " /*prefix*/);
dumpSpyWindowGestureMonitors(pw, " " /*prefix*/);
dumpDisplayInputPropertiesValues(pw, " " /* prefix */);
}
private void dumpAssociations(PrintWriter pw, String prefix) {
@@ -2660,6 +2779,25 @@ public class InputManagerService extends IInputManager.Stub
}
}
private void dumpDisplayInputPropertiesValues(PrintWriter pw, String prefix) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
if (mAdditionalDisplayInputProperties.size() != 0) {
pw.println(prefix + "mAdditionalDisplayInputProperties:");
for (int i = 0; i < mAdditionalDisplayInputProperties.size(); i++) {
pw.println(prefix + " displayId: "
+ mAdditionalDisplayInputProperties.keyAt(i));
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.valueAt(i);
pw.println(prefix + " pointerAcceleration: " + properties.pointerAcceleration);
pw.println(prefix + " pointerIconVisible: " + properties.pointerIconVisible);
}
}
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
pw.println(prefix + "mOverriddenPointerDisplayId: " + mOverriddenPointerDisplayId);
}
}
}
private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -2683,8 +2821,8 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ }
nativeMonitor(mPtr);
}
@@ -3102,7 +3240,7 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
private int getPointerDisplayId() {
synchronized (mPointerDisplayIdLock) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
// Prefer the override to all other displays.
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
return mOverriddenPointerDisplayId;
@@ -3591,14 +3729,19 @@ public class InputManagerService extends IInputManager.Stub
InputManagerService.this.setVirtualMousePointerDisplayId(pointerDisplayId);
}
@Override
public int getVirtualMousePointerDisplayId() {
return InputManagerService.this.getVirtualMousePointerDisplayId();
}
@Override
public PointF getCursorPosition() {
return mWindowManagerCallbacks.getCursorPosition();
}
@Override
public void setPointerAcceleration(float acceleration) {
InputManagerService.this.setPointerAcceleration(acceleration);
public void setPointerAcceleration(float acceleration, int displayId) {
InputManagerService.this.setPointerAcceleration(acceleration, displayId);
}
@Override
@@ -3606,6 +3749,11 @@ public class InputManagerService extends IInputManager.Stub
InputManagerService.this.setDisplayEligibilityForPointerCapture(displayId, isEligible);
}
@Override
public void setPointerIconVisible(boolean visible, int displayId) {
InputManagerService.this.setPointerIconVisible(visible, displayId);
}
@Override
public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
registerLidSwitchCallbackInternal(callbacks);
@@ -3633,4 +3781,21 @@ public class InputManagerService extends IInputManager.Stub
new InputShellCommand().exec(this, in, out, err, args, callback, resultReceiver);
}
private static class AdditionalDisplayInputProperties {
static final boolean DEFAULT_POINTER_ICON_VISIBLE = true;
static final float DEFAULT_POINTER_ACCELERATION =
(float) IInputConstants.DEFAULT_POINTER_ACCELERATION;
// The pointer acceleration for this display.
public float pointerAcceleration = DEFAULT_POINTER_ACCELERATION;
// Whether the pointer icon should be visible or hidden on this display.
public boolean pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
public boolean allDefaults() {
return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0
&& pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE;
}
}
}

View File

@@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManagerInternal;
@@ -30,7 +29,6 @@ import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInputConstants;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
@@ -81,17 +79,15 @@ public class InputControllerTest {
}
@Test
public void unregisterInputDevice_allMiceUnregistered_unsetValues() {
public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() {
final IBinder deviceToken = new Binder();
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f));
doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(
eq(Display.INVALID_DISPLAY));
verify(mInputManagerInternalMock).setPointerAcceleration(
eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION));
}
@Test
@@ -100,14 +96,11 @@ public class InputControllerTest {
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f));
final IBinder deviceToken2 = new Binder();
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
/* displayId= */ 2);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
verify(mInputManagerInternalMock, times(0)).setPointerAcceleration(
eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION));
}
}

View File

@@ -19,6 +19,8 @@ package com.android.server.companion.virtual;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,6 +29,7 @@ import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -117,6 +120,8 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
@@ -353,7 +358,7 @@ public class VirtualDeviceManagerServiceTest {
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
.setAction(action).build());
@@ -394,7 +399,7 @@ public class VirtualDeviceManagerServiceTest {
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
@@ -435,7 +440,7 @@ public class VirtualDeviceManagerServiceTest {
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
.setYAxisMovement(y).build());
@@ -508,4 +513,19 @@ public class VirtualDeviceManagerServiceTest {
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
majorAxisSize);
}
@Test
public void setShowPointerIcon_setsValueForAllDisplays() {
mDeviceImpl.mVirtualDisplayIds.add(1);
mDeviceImpl.mVirtualDisplayIds.add(2);
mDeviceImpl.mVirtualDisplayIds.add(3);
mDeviceImpl.createVirtualMouse(1, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
mDeviceImpl.createVirtualMouse(2, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
mDeviceImpl.createVirtualMouse(3, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
mDeviceImpl.setShowPointerIcon(false);
verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt());
verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt());
mDeviceImpl.setShowPointerIcon(true);
verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(true), anyInt());
}
}