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:
Prabir Pradhan
2022-05-05 19:37:22 +00:00
committed by Automerger Merge Worker
13 changed files with 488 additions and 67 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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