Merge changes from topics "presubmit-am-e1ccffb4201944be9e638acb9f6f4dfc", "presubmit-am-e6236e948fb14688bc4afb3adb9e745b" into tm-dev am: dd2c72a65d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17351976 Change-Id: If2730b470e595c52d2d175f35ab33a61a6c9c08d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -75,8 +75,15 @@ public abstract class InputManagerInternal {
|
||||
/**
|
||||
* Sets the display id that the MouseCursorController will be forced to target. Pass
|
||||
* {@link android.view.Display#INVALID_DISPLAY} to clear the override.
|
||||
*
|
||||
* Note: This method generally blocks until the pointer display override has propagated.
|
||||
* When setting a new override, the caller should ensure that an input device that can control
|
||||
* the mouse pointer is connected. If a new override is set when no such input device is
|
||||
* connected, the caller may be blocked for an arbitrary period of time.
|
||||
*
|
||||
* @return true if the pointer displayId was set successfully, or false if it fails.
|
||||
*/
|
||||
public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
|
||||
public abstract boolean setVirtualMousePointerDisplayId(int pointerDisplayId);
|
||||
|
||||
/**
|
||||
* Gets the display id that the MouseCursorController is being forced to target. Returns
|
||||
|
||||
@@ -106,6 +106,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
|
||||
PointerController::~PointerController() {
|
||||
mDisplayInfoListener->onPointerControllerDestroyed();
|
||||
mUnregisterWindowInfosListener(mDisplayInfoListener);
|
||||
mContext.getPolicy()->onPointerDisplayIdChanged(ADISPLAY_ID_NONE, 0, 0);
|
||||
}
|
||||
|
||||
std::mutex& PointerController::getLock() const {
|
||||
@@ -255,6 +256,12 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
|
||||
getAdditionalMouseResources = true;
|
||||
}
|
||||
mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
|
||||
if (viewport.displayId != mLocked.pointerDisplayId) {
|
||||
float xPos, yPos;
|
||||
mCursorController.getPosition(&xPos, &yPos);
|
||||
mContext.getPolicy()->onPointerDisplayIdChanged(viewport.displayId, xPos, yPos);
|
||||
mLocked.pointerDisplayId = viewport.displayId;
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::updatePointerIcon(int32_t iconId) {
|
||||
|
||||
@@ -104,6 +104,7 @@ private:
|
||||
|
||||
struct Locked {
|
||||
Presentation presentation;
|
||||
int32_t pointerDisplayId = ADISPLAY_ID_NONE;
|
||||
|
||||
std::vector<gui::DisplayInfo> mDisplayInfos;
|
||||
std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
|
||||
virtual int32_t getDefaultPointerIconId() = 0;
|
||||
virtual int32_t getCustomPointerIconId() = 0;
|
||||
virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -56,9 +56,11 @@ public:
|
||||
std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
|
||||
virtual int32_t getDefaultPointerIconId() override;
|
||||
virtual int32_t getCustomPointerIconId() override;
|
||||
virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
|
||||
|
||||
bool allResourcesAreLoaded();
|
||||
bool noResourcesAreLoaded();
|
||||
std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
|
||||
|
||||
private:
|
||||
void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
|
||||
@@ -66,6 +68,7 @@ private:
|
||||
bool pointerIconLoaded{false};
|
||||
bool pointerResourcesLoaded{false};
|
||||
bool additionalMouseResourcesLoaded{false};
|
||||
std::optional<int32_t /*displayId*/> latestPointerDisplayId;
|
||||
};
|
||||
|
||||
void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
|
||||
@@ -126,12 +129,19 @@ void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* ic
|
||||
icon->hotSpotX = hotSpot.first;
|
||||
icon->hotSpotY = hotSpot.second;
|
||||
}
|
||||
|
||||
void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
|
||||
float /*xPos*/,
|
||||
float /*yPos*/) {
|
||||
latestPointerDisplayId = displayId;
|
||||
}
|
||||
|
||||
class PointerControllerTest : public Test {
|
||||
protected:
|
||||
PointerControllerTest();
|
||||
~PointerControllerTest();
|
||||
|
||||
void ensureDisplayViewportIsSet();
|
||||
void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
|
||||
|
||||
sp<MockSprite> mPointerSprite;
|
||||
sp<MockPointerControllerPolicyInterface> mPolicy;
|
||||
@@ -168,9 +178,9 @@ PointerControllerTest::~PointerControllerTest() {
|
||||
mThread.join();
|
||||
}
|
||||
|
||||
void PointerControllerTest::ensureDisplayViewportIsSet() {
|
||||
void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
|
||||
DisplayViewport viewport;
|
||||
viewport.displayId = ADISPLAY_ID_DEFAULT;
|
||||
viewport.displayId = displayId;
|
||||
viewport.logicalRight = 1600;
|
||||
viewport.logicalBottom = 1200;
|
||||
viewport.physicalRight = 800;
|
||||
@@ -255,6 +265,30 @@ TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
|
||||
ensureDisplayViewportIsSet();
|
||||
}
|
||||
|
||||
TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
|
||||
EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
|
||||
<< "A pointer display change does not occur when PointerController is created.";
|
||||
|
||||
ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
|
||||
|
||||
const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
|
||||
ASSERT_TRUE(lastReportedPointerDisplayId)
|
||||
<< "The policy is notified of a pointer display change when the viewport is first set.";
|
||||
EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
|
||||
<< "Incorrect pointer display notified.";
|
||||
|
||||
ensureDisplayViewportIsSet(42);
|
||||
|
||||
EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
|
||||
<< "The policy is notified when the pointer display changes.";
|
||||
|
||||
// Release the PointerController.
|
||||
mPointerController = nullptr;
|
||||
|
||||
EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
|
||||
<< "The pointer display changes to invalid when PointerController is destroyed.";
|
||||
}
|
||||
|
||||
class PointerControllerWindowInfoListenerTest : public Test {};
|
||||
|
||||
class TestPointerController : public PointerController {
|
||||
|
||||
@@ -164,6 +164,7 @@ public class InputManagerService extends IInputManager.Stub
|
||||
private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
|
||||
private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
|
||||
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
|
||||
private static final int MSG_POINTER_DISPLAY_ID_CHANGED = 7;
|
||||
|
||||
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
|
||||
|
||||
@@ -276,11 +277,24 @@ public class InputManagerService extends IInputManager.Stub
|
||||
@GuardedBy("mAssociationLock")
|
||||
private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
|
||||
|
||||
// Guards per-display input properties and properties relating to the mouse pointer.
|
||||
// Threads can wait on this lock to be notified the next time the display on which the mouse
|
||||
// pointer is shown has changed.
|
||||
private final Object mAdditionalDisplayInputPropertiesLock = new Object();
|
||||
|
||||
// Forces the MouseCursorController to target a specific display id.
|
||||
// Forces the PointerController to target a specific display id.
|
||||
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
|
||||
private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
|
||||
|
||||
// PointerController is the source of truth of the pointer display. This is the value of the
|
||||
// latest pointer display id reported by PointerController.
|
||||
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
|
||||
private int mAcknowledgedPointerDisplayId = Display.INVALID_DISPLAY;
|
||||
// This is the latest display id that IMS has requested PointerController to use. If there are
|
||||
// no devices that can control the pointer, PointerController may end up disregarding this
|
||||
// value.
|
||||
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
|
||||
private int mRequestedPointerDisplayId = Display.INVALID_DISPLAY;
|
||||
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
|
||||
private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties =
|
||||
new SparseArray<>();
|
||||
@@ -289,7 +303,6 @@ public class InputManagerService extends IInputManager.Stub
|
||||
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
|
||||
private PointerIcon mIcon;
|
||||
|
||||
|
||||
// Holds all the registered gesture monitors that are implemented as spy windows. The spy
|
||||
// windows are mapped by their InputChannel tokens.
|
||||
@GuardedBy("mInputMonitors")
|
||||
@@ -383,6 +396,10 @@ public class InputManagerService extends IInputManager.Stub
|
||||
NativeInputManagerService getNativeService(InputManagerService service) {
|
||||
return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());
|
||||
}
|
||||
|
||||
void registerLocalService(InputManagerInternal localService) {
|
||||
LocalServices.addService(InputManagerInternal.class, localService);
|
||||
}
|
||||
}
|
||||
|
||||
public InputManagerService(Context context) {
|
||||
@@ -409,7 +426,7 @@ public class InputManagerService extends IInputManager.Stub
|
||||
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
|
||||
new File(doubleTouchGestureEnablePath);
|
||||
|
||||
LocalServices.addService(InputManagerInternal.class, new LocalService());
|
||||
injector.registerLocalService(new LocalService());
|
||||
}
|
||||
|
||||
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
|
||||
@@ -559,6 +576,8 @@ public class InputManagerService extends IInputManager.Stub
|
||||
vArray[i] = viewports.get(i);
|
||||
}
|
||||
mNative.setDisplayViewports(vArray);
|
||||
// Always attempt to update the pointer display when viewports change.
|
||||
updatePointerDisplayId();
|
||||
|
||||
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
|
||||
final AdditionalDisplayInputProperties properties =
|
||||
@@ -1964,10 +1983,43 @@ public class InputManagerService extends IInputManager.Stub
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setVirtualMousePointerDisplayId(int displayId) {
|
||||
/**
|
||||
* Update the display on which the mouse pointer is shown.
|
||||
* If there is an overridden display for the mouse pointer, use that. Otherwise, query
|
||||
* WindowManager for the pointer display.
|
||||
*
|
||||
* @return true if the pointer displayId changed, false otherwise.
|
||||
*/
|
||||
private boolean updatePointerDisplayId() {
|
||||
synchronized (mAdditionalDisplayInputPropertiesLock) {
|
||||
final int pointerDisplayId = mOverriddenPointerDisplayId != Display.INVALID_DISPLAY
|
||||
? mOverriddenPointerDisplayId : mWindowManagerCallbacks.getPointerDisplayId();
|
||||
if (mRequestedPointerDisplayId == pointerDisplayId) {
|
||||
return false;
|
||||
}
|
||||
mRequestedPointerDisplayId = pointerDisplayId;
|
||||
mNative.setPointerDisplayId(pointerDisplayId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePointerDisplayIdChanged(PointerDisplayIdChangedArgs args) {
|
||||
synchronized (mAdditionalDisplayInputPropertiesLock) {
|
||||
mAcknowledgedPointerDisplayId = args.mPointerDisplayId;
|
||||
// Notify waiting threads that the display of the mouse pointer has changed.
|
||||
mAdditionalDisplayInputPropertiesLock.notifyAll();
|
||||
}
|
||||
mWindowManagerCallbacks.notifyPointerDisplayIdChanged(
|
||||
args.mPointerDisplayId, args.mXPosition, args.mYPosition);
|
||||
}
|
||||
|
||||
private boolean setVirtualMousePointerDisplayIdBlocking(int displayId) {
|
||||
// Indicates whether this request is for removing the override.
|
||||
final boolean removingOverride = displayId == Display.INVALID_DISPLAY;
|
||||
|
||||
synchronized (mAdditionalDisplayInputPropertiesLock) {
|
||||
mOverriddenPointerDisplayId = displayId;
|
||||
if (displayId != Display.INVALID_DISPLAY) {
|
||||
if (!removingOverride) {
|
||||
final AdditionalDisplayInputProperties properties =
|
||||
mAdditionalDisplayInputProperties.get(displayId);
|
||||
if (properties != null) {
|
||||
@@ -1975,9 +2027,30 @@ public class InputManagerService extends IInputManager.Stub
|
||||
updatePointerIconVisibleLocked(properties.pointerIconVisible);
|
||||
}
|
||||
}
|
||||
if (!updatePointerDisplayId() && mAcknowledgedPointerDisplayId == displayId) {
|
||||
// The requested pointer display is already set.
|
||||
return true;
|
||||
}
|
||||
if (removingOverride && mAcknowledgedPointerDisplayId == Display.INVALID_DISPLAY) {
|
||||
// The pointer display override is being removed, but the current pointer display
|
||||
// is already invalid. This can happen when the PointerController is destroyed as a
|
||||
// result of the removal of all input devices that can control the pointer.
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
// The pointer display changed, so wait until the change has propagated.
|
||||
mAdditionalDisplayInputPropertiesLock.wait(5_000 /*mills*/);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
// This request succeeds in two cases:
|
||||
// - This request was to remove the override, in which case the new pointer display
|
||||
// could be anything that WM has set.
|
||||
// - We are setting a new override, in which case the request only succeeds if the
|
||||
// reported new displayId is the one we requested. This check ensures that if two
|
||||
// competing overrides are requested in succession, the caller can be notified if one
|
||||
// of them fails.
|
||||
return removingOverride || mAcknowledgedPointerDisplayId == displayId;
|
||||
}
|
||||
// TODO(b/215597605): trigger MousePositionTracker update
|
||||
mNative.notifyPointerDisplayIdChanged();
|
||||
}
|
||||
|
||||
private int getVirtualMousePointerDisplayId() {
|
||||
@@ -3157,18 +3230,6 @@ public class InputManagerService extends IInputManager.Stub
|
||||
return mContext.createDisplayContext(display);
|
||||
}
|
||||
|
||||
// Native callback.
|
||||
@SuppressWarnings("unused")
|
||||
private int getPointerDisplayId() {
|
||||
synchronized (mAdditionalDisplayInputPropertiesLock) {
|
||||
// Prefer the override to all other displays.
|
||||
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
|
||||
return mOverriddenPointerDisplayId;
|
||||
}
|
||||
}
|
||||
return mWindowManagerCallbacks.getPointerDisplayId();
|
||||
}
|
||||
|
||||
// Native callback.
|
||||
@SuppressWarnings("unused")
|
||||
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
|
||||
@@ -3209,6 +3270,26 @@ public class InputManagerService extends IInputManager.Stub
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class PointerDisplayIdChangedArgs {
|
||||
final int mPointerDisplayId;
|
||||
final float mXPosition;
|
||||
final float mYPosition;
|
||||
PointerDisplayIdChangedArgs(int pointerDisplayId, float xPosition, float yPosition) {
|
||||
mPointerDisplayId = pointerDisplayId;
|
||||
mXPosition = xPosition;
|
||||
mYPosition = yPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// Native callback.
|
||||
@SuppressWarnings("unused")
|
||||
@VisibleForTesting
|
||||
void onPointerDisplayIdChanged(int pointerDisplayId, float xPosition, float yPosition) {
|
||||
mHandler.obtainMessage(MSG_POINTER_DISPLAY_ID_CHANGED,
|
||||
new PointerDisplayIdChangedArgs(pointerDisplayId, xPosition,
|
||||
yPosition)).sendToTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface implemented by the Window Manager.
|
||||
*/
|
||||
@@ -3332,6 +3413,14 @@ public class InputManagerService extends IInputManager.Stub
|
||||
*/
|
||||
@Nullable
|
||||
SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
|
||||
|
||||
/**
|
||||
* Notify WindowManagerService when the display of the mouse pointer changes.
|
||||
* @param displayId The display on which the mouse pointer is shown.
|
||||
* @param x The x coordinate of the mouse pointer.
|
||||
* @param y The y coordinate of the mouse pointer.
|
||||
*/
|
||||
void notifyPointerDisplayIdChanged(int displayId, float x, float y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3384,6 +3473,9 @@ public class InputManagerService extends IInputManager.Stub
|
||||
boolean inTabletMode = (boolean) args.arg1;
|
||||
deliverTabletModeChanged(whenNanos, inTabletMode);
|
||||
break;
|
||||
case MSG_POINTER_DISPLAY_ID_CHANGED:
|
||||
handlePointerDisplayIdChanged((PointerDisplayIdChangedArgs) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3634,8 +3726,9 @@ public class InputManagerService extends IInputManager.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVirtualMousePointerDisplayId(int pointerDisplayId) {
|
||||
InputManagerService.this.setVirtualMousePointerDisplayId(pointerDisplayId);
|
||||
public boolean setVirtualMousePointerDisplayId(int pointerDisplayId) {
|
||||
return InputManagerService.this
|
||||
.setVirtualMousePointerDisplayIdBlocking(pointerDisplayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -176,6 +176,9 @@ public interface NativeInputManagerService {
|
||||
|
||||
void cancelCurrentTouch();
|
||||
|
||||
/** Set the displayId on which the mouse cursor should be shown. */
|
||||
void setPointerDisplayId(int displayId);
|
||||
|
||||
/** The native implementation of InputManagerService methods. */
|
||||
class NativeImpl implements NativeInputManagerService {
|
||||
/** Pointer to native input manager service object, used by native code. */
|
||||
@@ -388,5 +391,8 @@ public interface NativeInputManagerService {
|
||||
|
||||
@Override
|
||||
public native void cancelCurrentTouch();
|
||||
|
||||
@Override
|
||||
public native void setPointerDisplayId(int displayId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +270,22 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPointerDisplayIdChanged(int displayId, float x, float y) {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
mService.setMousePointerDisplayId(displayId);
|
||||
if (displayId == Display.INVALID_DISPLAY) return;
|
||||
|
||||
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
|
||||
if (dc == null) {
|
||||
Slog.wtf(TAG, "The mouse pointer was moved to display " + displayId
|
||||
+ " that does not have a valid DisplayContent.");
|
||||
return;
|
||||
}
|
||||
mService.restorePointerIconLocked(dc, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Waits until the built-in input devices have been configured. */
|
||||
public boolean waitForInputDevicesReady(long timeoutMillis) {
|
||||
synchronized (mInputDevicesReadyMonitor) {
|
||||
|
||||
@@ -7208,18 +7208,42 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
private float mLatestMouseX;
|
||||
private float mLatestMouseY;
|
||||
|
||||
void updatePosition(float x, float y) {
|
||||
/**
|
||||
* The display that the pointer (mouse cursor) is currently shown on. This is updated
|
||||
* directly by InputManagerService when the pointer display changes.
|
||||
*/
|
||||
private int mPointerDisplayId = INVALID_DISPLAY;
|
||||
|
||||
/**
|
||||
* Update the mouse cursor position as a result of a mouse movement.
|
||||
* @return true if the position was successfully updated, false otherwise.
|
||||
*/
|
||||
boolean updatePosition(int displayId, float x, float y) {
|
||||
synchronized (this) {
|
||||
mLatestEventWasMouse = true;
|
||||
|
||||
if (displayId != mPointerDisplayId) {
|
||||
// The display of the position update does not match the display on which the
|
||||
// mouse pointer is shown, so do not update the position.
|
||||
return false;
|
||||
}
|
||||
mLatestMouseX = x;
|
||||
mLatestMouseY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void setPointerDisplayId(int displayId) {
|
||||
synchronized (this) {
|
||||
mPointerDisplayId = displayId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerEvent(MotionEvent motionEvent) {
|
||||
if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
|
||||
updatePosition(motionEvent.getRawX(), motionEvent.getRawY());
|
||||
updatePosition(motionEvent.getDisplayId(), motionEvent.getRawX(),
|
||||
motionEvent.getRawY());
|
||||
} else {
|
||||
synchronized (this) {
|
||||
mLatestEventWasMouse = false;
|
||||
@@ -7229,6 +7253,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
};
|
||||
|
||||
void updatePointerIcon(IWindow client) {
|
||||
int pointerDisplayId;
|
||||
float mouseX, mouseY;
|
||||
|
||||
synchronized(mMousePositionTracker) {
|
||||
@@ -7237,6 +7262,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
mouseX = mMousePositionTracker.mLatestMouseX;
|
||||
mouseY = mMousePositionTracker.mLatestMouseY;
|
||||
pointerDisplayId = mMousePositionTracker.mPointerDisplayId;
|
||||
}
|
||||
|
||||
synchronized (mGlobalLock) {
|
||||
@@ -7253,6 +7279,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
if (displayContent == null) {
|
||||
return;
|
||||
}
|
||||
if (pointerDisplayId != displayContent.getDisplayId()) {
|
||||
// Do not let the pointer icon be updated by a window on a different display.
|
||||
return;
|
||||
}
|
||||
WindowState windowUnderPointer =
|
||||
displayContent.getTouchableWinAtPointLocked(mouseX, mouseY);
|
||||
if (windowUnderPointer != callingWin) {
|
||||
@@ -7270,7 +7300,11 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
void restorePointerIconLocked(DisplayContent displayContent, float latestX, float latestY) {
|
||||
// Mouse position tracker has not been getting updates while dragging, update it now.
|
||||
mMousePositionTracker.updatePosition(latestX, latestY);
|
||||
if (!mMousePositionTracker.updatePosition(
|
||||
displayContent.getDisplayId(), latestX, latestY)) {
|
||||
// The mouse position could not be updated, so ignore this request.
|
||||
return;
|
||||
}
|
||||
|
||||
WindowState windowUnderPointer =
|
||||
displayContent.getTouchableWinAtPointLocked(latestX, latestY);
|
||||
@@ -7294,6 +7328,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
void setMousePointerDisplayId(int displayId) {
|
||||
mMousePositionTracker.setPointerDisplayId(displayId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a tap exclude region in the window identified by the provided id. Touches down on this
|
||||
* region will not:
|
||||
|
||||
@@ -107,6 +107,7 @@ static struct {
|
||||
jmethodID interceptKeyBeforeDispatching;
|
||||
jmethodID dispatchUnhandledKey;
|
||||
jmethodID checkInjectEventsPermission;
|
||||
jmethodID onPointerDisplayIdChanged;
|
||||
jmethodID onPointerDownOutsideFocus;
|
||||
jmethodID getVirtualKeyQuietTimeMillis;
|
||||
jmethodID getExcludedDeviceNames;
|
||||
@@ -120,7 +121,6 @@ static struct {
|
||||
jmethodID getLongPressTimeout;
|
||||
jmethodID getPointerLayer;
|
||||
jmethodID getPointerIcon;
|
||||
jmethodID getPointerDisplayId;
|
||||
jmethodID getKeyboardLayoutOverlay;
|
||||
jmethodID getDeviceAlias;
|
||||
jmethodID getTouchCalibrationForInputDevice;
|
||||
@@ -277,6 +277,7 @@ public:
|
||||
void setFocusedDisplay(int32_t displayId);
|
||||
void setInputDispatchMode(bool enabled, bool frozen);
|
||||
void setSystemUiLightsOut(bool lightsOut);
|
||||
void setPointerDisplayId(int32_t displayId);
|
||||
void setPointerSpeed(int32_t speed);
|
||||
void setPointerAcceleration(float acceleration);
|
||||
void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
|
||||
@@ -288,7 +289,6 @@ public:
|
||||
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
|
||||
void setCustomPointerIcon(const SpriteIcon& icon);
|
||||
void setMotionClassifierEnabled(bool enabled);
|
||||
void notifyPointerDisplayIdChanged();
|
||||
|
||||
/* --- InputReaderPolicyInterface implementation --- */
|
||||
|
||||
@@ -346,6 +346,7 @@ public:
|
||||
std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId);
|
||||
virtual int32_t getDefaultPointerIconId();
|
||||
virtual int32_t getCustomPointerIconId();
|
||||
virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos);
|
||||
|
||||
private:
|
||||
sp<InputManagerInterface> mInputManager;
|
||||
@@ -394,7 +395,6 @@ private:
|
||||
void updateInactivityTimeoutLocked();
|
||||
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
|
||||
void ensureSpriteControllerLocked();
|
||||
int32_t getPointerDisplayId();
|
||||
sp<SurfaceControl> getParentSurfaceForPointers(int displayId);
|
||||
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
|
||||
|
||||
@@ -498,13 +498,9 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
|
||||
}
|
||||
}
|
||||
|
||||
// Get the preferred pointer controller displayId.
|
||||
int32_t pointerDisplayId = getPointerDisplayId();
|
||||
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
mLocked.viewports = viewports;
|
||||
mLocked.pointerDisplayId = pointerDisplayId;
|
||||
std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
|
||||
if (controller != nullptr) {
|
||||
controller->onDisplayViewportsUpdated(mLocked.viewports);
|
||||
@@ -666,15 +662,12 @@ std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerCon
|
||||
return controller;
|
||||
}
|
||||
|
||||
int32_t NativeInputManager::getPointerDisplayId() {
|
||||
void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId, float xPos,
|
||||
float yPos) {
|
||||
JNIEnv* env = jniEnv();
|
||||
jint pointerDisplayId = env->CallIntMethod(mServiceObj,
|
||||
gServiceClassInfo.getPointerDisplayId);
|
||||
if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
|
||||
pointerDisplayId = ADISPLAY_ID_DEFAULT;
|
||||
}
|
||||
|
||||
return pointerDisplayId;
|
||||
env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
|
||||
xPos, yPos);
|
||||
checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
|
||||
}
|
||||
|
||||
sp<SurfaceControl> NativeInputManager::getParentSurfaceForPointers(int displayId) {
|
||||
@@ -1032,6 +1025,22 @@ void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
|
||||
: InactivityTimeout::NORMAL);
|
||||
}
|
||||
|
||||
void NativeInputManager::setPointerDisplayId(int32_t displayId) {
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
if (mLocked.pointerDisplayId == displayId) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGI("Setting pointer display id to %d.", displayId);
|
||||
mLocked.pointerDisplayId = displayId;
|
||||
} // release lock
|
||||
|
||||
mInputManager->getReader().requestRefreshConfiguration(
|
||||
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
|
||||
}
|
||||
|
||||
void NativeInputManager::setPointerSpeed(int32_t speed) {
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
@@ -1494,18 +1503,6 @@ void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
|
||||
mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
|
||||
}
|
||||
|
||||
void NativeInputManager::notifyPointerDisplayIdChanged() {
|
||||
int32_t pointerDisplayId = getPointerDisplayId();
|
||||
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
mLocked.pointerDisplayId = pointerDisplayId;
|
||||
} // release lock
|
||||
|
||||
mInputManager->getReader().requestRefreshConfiguration(
|
||||
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
|
||||
@@ -2199,11 +2196,6 @@ static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jobject nativeImplO
|
||||
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
|
||||
}
|
||||
|
||||
static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jobject nativeImplObj) {
|
||||
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
|
||||
im->notifyPointerDisplayIdChanged();
|
||||
}
|
||||
|
||||
static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jobject nativeImplObj,
|
||||
jint displayId, jboolean isEligible) {
|
||||
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
|
||||
@@ -2321,6 +2313,11 @@ static void nativeCancelCurrentTouch(JNIEnv* env, jobject nativeImplObj) {
|
||||
im->getInputManager()->getDispatcher().cancelCurrentTouch();
|
||||
}
|
||||
|
||||
static void nativeSetPointerDisplayId(JNIEnv* env, jobject nativeImplObj, jint displayId) {
|
||||
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
|
||||
im->setPointerDisplayId(displayId);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static const JNINativeMethod gInputManagerMethods[] = {
|
||||
@@ -2393,7 +2390,6 @@ static const JNINativeMethod gInputManagerMethods[] = {
|
||||
{"canDispatchToDisplay", "(II)Z", (void*)nativeCanDispatchToDisplay},
|
||||
{"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged},
|
||||
{"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation},
|
||||
{"notifyPointerDisplayIdChanged", "()V", (void*)nativeNotifyPointerDisplayIdChanged},
|
||||
{"setDisplayEligibilityForPointerCapture", "(IZ)V",
|
||||
(void*)nativeSetDisplayEligibilityForPointerCapture},
|
||||
{"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled},
|
||||
@@ -2403,6 +2399,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
|
||||
{"disableSensor", "(II)V", (void*)nativeDisableSensor},
|
||||
{"flushSensor", "(II)Z", (void*)nativeFlushSensor},
|
||||
{"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch},
|
||||
{"setPointerDisplayId", "(I)V", (void*)nativeSetPointerDisplayId},
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
@@ -2498,6 +2495,9 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
|
||||
"checkInjectEventsPermission", "(II)Z");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
|
||||
"(IFF)V");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
|
||||
"onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
|
||||
|
||||
@@ -2537,9 +2537,6 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
|
||||
"getPointerIcon", "(I)Landroid/view/PointerIcon;");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
|
||||
"getPointerDisplayId", "()I");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
|
||||
"getKeyboardLayoutOverlay",
|
||||
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.server.companion.virtual;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
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;
|
||||
@@ -67,7 +66,7 @@ public class InputControllerTest {
|
||||
mInputManagerMockHelper = new InputManagerMockHelper(
|
||||
TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
|
||||
|
||||
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
|
||||
doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
|
||||
LocalServices.removeServiceForTest(InputManagerInternal.class);
|
||||
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
|
||||
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
|
||||
|
||||
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
|
||||
doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
|
||||
doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
|
||||
doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
|
||||
LocalServices.removeServiceForTest(InputManagerInternal.class);
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.input
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.hardware.display.DisplayViewport
|
||||
import android.hardware.input.InputManagerInternal
|
||||
import android.os.test.TestLooper
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.view.Display
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Tests for {@link InputManagerService}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksServicesTests:InputManagerServiceTests
|
||||
*/
|
||||
@Presubmit
|
||||
class InputManagerServiceTests {
|
||||
|
||||
@get:Rule
|
||||
val rule = MockitoJUnit.rule()!!
|
||||
|
||||
@Mock
|
||||
private lateinit var native: NativeInputManagerService
|
||||
|
||||
@Mock
|
||||
private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
|
||||
|
||||
private lateinit var service: InputManagerService
|
||||
private lateinit var localService: InputManagerInternal
|
||||
private lateinit var context: Context
|
||||
private lateinit var testLooper: TestLooper
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
|
||||
testLooper = TestLooper()
|
||||
service =
|
||||
InputManagerService(object : InputManagerService.Injector(context, testLooper.looper) {
|
||||
override fun getNativeService(
|
||||
service: InputManagerService?
|
||||
): NativeInputManagerService {
|
||||
return native
|
||||
}
|
||||
|
||||
override fun registerLocalService(service: InputManagerInternal?) {
|
||||
localService = service!!
|
||||
}
|
||||
})
|
||||
assertTrue("Local service must be registered", this::localService.isInitialized)
|
||||
service.setWindowManagerCallbacks(wmCallbacks)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() {
|
||||
val displayId = 123
|
||||
`when`(wmCallbacks.pointerDisplayId).thenReturn(displayId)
|
||||
val viewports = listOf<DisplayViewport>()
|
||||
localService.setDisplayViewports(viewports)
|
||||
verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java))
|
||||
verify(native).setPointerDisplayId(displayId)
|
||||
|
||||
val x = 42f
|
||||
val y = 314f
|
||||
service.onPointerDisplayIdChanged(displayId, x, y)
|
||||
testLooper.dispatchNext()
|
||||
verify(wmCallbacks).notifyPointerDisplayIdChanged(displayId, x, y)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetVirtualMousePointerDisplayId() {
|
||||
// Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
|
||||
// until the native callback happens.
|
||||
var countDownLatch = CountDownLatch(1)
|
||||
val overrideDisplayId = 123
|
||||
Thread {
|
||||
assertTrue("Setting virtual pointer display should succeed",
|
||||
localService.setVirtualMousePointerDisplayId(overrideDisplayId))
|
||||
countDownLatch.countDown()
|
||||
}.start()
|
||||
assertFalse("Setting virtual pointer display should block",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
val x = 42f
|
||||
val y = 314f
|
||||
service.onPointerDisplayIdChanged(overrideDisplayId, x, y)
|
||||
testLooper.dispatchNext()
|
||||
verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, x, y)
|
||||
assertTrue("Native callback unblocks calling thread",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
verify(native).setPointerDisplayId(overrideDisplayId)
|
||||
|
||||
// Ensure that setting the same override again succeeds immediately.
|
||||
assertTrue("Setting the same virtual mouse pointer displayId again should succeed",
|
||||
localService.setVirtualMousePointerDisplayId(overrideDisplayId))
|
||||
|
||||
// Ensure that we did not query WM for the pointerDisplayId when setting the override
|
||||
verify(wmCallbacks, never()).pointerDisplayId
|
||||
|
||||
// Unset the virtual mouse pointer displayId, and ensure that we query WM for the new
|
||||
// pointer displayId and the calling thread is blocked until the native callback happens.
|
||||
countDownLatch = CountDownLatch(1)
|
||||
val pointerDisplayId = 42
|
||||
`when`(wmCallbacks.pointerDisplayId).thenReturn(pointerDisplayId)
|
||||
Thread {
|
||||
assertTrue("Unsetting virtual mouse pointer displayId should succeed",
|
||||
localService.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY))
|
||||
countDownLatch.countDown()
|
||||
}.start()
|
||||
assertFalse("Unsetting virtual mouse pointer displayId should block",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
service.onPointerDisplayIdChanged(pointerDisplayId, x, y)
|
||||
testLooper.dispatchNext()
|
||||
verify(wmCallbacks).notifyPointerDisplayIdChanged(pointerDisplayId, x, y)
|
||||
assertTrue("Native callback unblocks calling thread",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
verify(native).setPointerDisplayId(pointerDisplayId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetVirtualMousePointerDisplayId_unsuccessfulUpdate() {
|
||||
// Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
|
||||
// until the native callback happens.
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
val overrideDisplayId = 123
|
||||
Thread {
|
||||
assertFalse("Setting virtual pointer display should be unsuccessful",
|
||||
localService.setVirtualMousePointerDisplayId(overrideDisplayId))
|
||||
countDownLatch.countDown()
|
||||
}.start()
|
||||
assertFalse("Setting virtual pointer display should block",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
val x = 42f
|
||||
val y = 314f
|
||||
// Assume the native callback updates the pointerDisplayId to the incorrect value.
|
||||
service.onPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
|
||||
testLooper.dispatchNext()
|
||||
verify(wmCallbacks).notifyPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
|
||||
assertTrue("Native callback unblocks calling thread",
|
||||
countDownLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
verify(native).setPointerDisplayId(overrideDisplayId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetVirtualMousePointerDisplayId_competingRequests() {
|
||||
val firstRequestSyncLatch = CountDownLatch(1)
|
||||
doAnswer {
|
||||
firstRequestSyncLatch.countDown()
|
||||
}.`when`(native).setPointerDisplayId(anyInt())
|
||||
|
||||
val firstRequestLatch = CountDownLatch(1)
|
||||
val firstOverride = 123
|
||||
Thread {
|
||||
assertFalse("Setting virtual pointer display from thread 1 should be unsuccessful",
|
||||
localService.setVirtualMousePointerDisplayId(firstOverride))
|
||||
firstRequestLatch.countDown()
|
||||
}.start()
|
||||
assertFalse("Setting virtual pointer display should block",
|
||||
firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
assertTrue("Wait for first thread's request should succeed",
|
||||
firstRequestSyncLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
val secondRequestLatch = CountDownLatch(1)
|
||||
val secondOverride = 42
|
||||
Thread {
|
||||
assertTrue("Setting virtual mouse pointer from thread 2 should be successful",
|
||||
localService.setVirtualMousePointerDisplayId(secondOverride))
|
||||
secondRequestLatch.countDown()
|
||||
}.start()
|
||||
assertFalse("Setting virtual mouse pointer should block",
|
||||
secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
val x = 42f
|
||||
val y = 314f
|
||||
// Assume the native callback updates directly to the second request.
|
||||
service.onPointerDisplayIdChanged(secondOverride, x, y)
|
||||
testLooper.dispatchNext()
|
||||
verify(wmCallbacks).notifyPointerDisplayIdChanged(secondOverride, x, y)
|
||||
assertTrue("Native callback unblocks first thread",
|
||||
firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
assertTrue("Native callback unblocks second thread",
|
||||
secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
|
||||
verify(native, times(2)).setPointerDisplayId(anyInt())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user