From c0d3c4b24ec64e2fdef7b7a9133db8672a5df923 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 1 May 2019 14:49:39 -0700 Subject: Removing support for menu key from status bar Bug: 131763491 Test: Verified that menu key doesn't show up for legacy apps Change-Id: I1a93c2552945788331fdda376785647e7978da07 --- include/input/InputWindow.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 916af699e0..f852cd0540 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -63,7 +63,6 @@ struct InputWindowInfo { FLAG_DISMISS_KEYGUARD = 0x00400000, FLAG_SPLIT_TOUCH = 0x00800000, FLAG_SLIPPERY = 0x20000000, - FLAG_NEEDS_MENU_KEY = 0x40000000, }; // Window types from WindowManager.LayoutParams -- cgit v1.2.3-59-g8ed1b From af87b3e09141558215e28f84d954590b5e7f2f16 Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 1 Oct 2019 16:59:28 -0700 Subject: Added input support for cloned layers This was done with a few changes: 1. Added a layerId in the input info so the InputInfo objects can be uniquely identified as per layer 2. When setting input info in InputDispatcher, compare InputInfo objects using layer id instead of input token. 3. Updated the crop region for layers based on the cloned hierarchy so the input is cropped to the correct region. Bug: 140756730 Test: InputDispatcherMultiWindowSameTokenTests Change-Id: I980f5d29d091fecb407f5cd6a289615505800927 --- include/input/InputWindow.h | 6 + libs/input/InputWindow.cpp | 2 + libs/input/tests/InputWindow_test.cpp | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 22 ++- .../inputflinger/tests/InputDispatcher_test.cpp | 149 +++++++++++++++++++++ services/surfaceflinger/BufferLayer.cpp | 15 ++- services/surfaceflinger/Layer.cpp | 63 ++++++--- services/surfaceflinger/Layer.h | 7 +- 8 files changed, 239 insertions(+), 27 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index f852cd0540..cbd64d520e 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -119,7 +119,11 @@ struct InputWindowInfo { /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ + // This value should NOT be used to uniquely identify the window. There may be different + // input windows that have the same token. sp token; + // This uniquely identifies the input window. + int32_t id = 0; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; @@ -203,6 +207,8 @@ public: sp getToken() const; + int32_t getId() const { return mInfo.id; } + sp getApplicationToken() { return mInfo.applicationInfo.token; } diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index ec28757933..74a05055eb 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -73,6 +73,7 @@ status_t InputWindowInfo::write(Parcel& output) const { status_t s = output.writeStrongBinder(token); if (s != OK) return s; + output.writeInt32(id); output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); @@ -116,6 +117,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { } ret.token = token; + ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index 6db18abacf..cdc81d2f04 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { sp touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); + i.id = 1; i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; @@ -72,6 +73,7 @@ TEST(InputWindowInfo, Parcelling) { p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5a49b5e16a..116625c03e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -240,6 +240,18 @@ static bool removeByValue(std::unordered_map& map, const V& value) { return removed; } +static bool haveSameToken(const sp& first, const sp& second) { + if (first == second) { + return true; + } + + if (first == nullptr || second == nullptr) { + return false; + } + + return first->getToken() == second->getToken(); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -3278,9 +3290,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. const std::vector>& oldHandles = getWindowHandlesLocked(displayId); - std::unordered_map, sp, IBinderHash> oldHandlesByTokens; + std::unordered_map> oldHandlesById; for (const sp& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; + oldHandlesById[handle->getId()] = handle; } std::vector> newHandles; @@ -3311,8 +3323,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); + if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { + const sp& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { @@ -3370,7 +3382,7 @@ void InputDispatcher::setInputWindows(const std::vector>& sp oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - if (oldFocusedWindowHandle != newFocusedWindowHandle) { + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b4d7608770..c25122c72b 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -526,6 +526,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; + mInfo.id = 0; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -564,6 +565,13 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } + void setId(int32_t id) { mInfo.id = id; } + + void setWindowScale(float xScale, float yScale) { + mInfo.windowXScale = xScale; + mInfo.windowYScale = yScale; + } + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -586,12 +594,21 @@ public: expectedFlags); } + InputEvent* consume() { + if (mInputReceiver == nullptr) { + return nullptr; + } + return mInputReceiver->consume(); + } + void assertNoEvents() { ASSERT_NE(mInputReceiver, nullptr) << "Call 'assertNoEvents' on a window with an InputReceiver"; mInputReceiver->assertNoEvents(); } + sp getToken() { return mInfo.token; } + private: std::unique_ptr mInputReceiver; }; @@ -667,6 +684,10 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, const std::vector& points) { size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; @@ -1291,4 +1312,132 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownWasNotCalled(); } +// These tests ensures we can send touch events to a single client when there are multiple input +// windows that point to the same client token. +class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + sp application = new FakeApplicationHandle(); + mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", + ADISPLAY_ID_DEFAULT); + // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. + // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. + mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow1->setId(0); + mWindow1->setFrame(Rect(0, 0, 100, 100)); + + mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", + ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow2->setId(1); + mWindow2->setFrame(Rect(100, 100, 200, 200)); + + mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); + } + +protected: + sp mWindow1; + sp mWindow2; + + // Helper function to convert the point from screen coordinates into the window's space + static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { + float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); + float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); + return {x, y}; + } + + void consumeMotionEvent(const sp& window, int32_t expectedAction, + const std::vector& points) { + std::string name = window->mName; + InputEvent* event = window->consume(); + + ASSERT_NE(nullptr, event) << name.c_str() + << ": consumer should have returned non-NULL event."; + + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) + << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; + + const MotionEvent& motionEvent = static_cast(*event); + EXPECT_EQ(expectedAction, motionEvent.getAction()); + + for (size_t i = 0; i < points.size(); i++) { + float expectedX = points[i].x; + float expectedY = points[i].y; + + EXPECT_EQ(expectedX, motionEvent.getX(i)) + << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getX(i); + EXPECT_EQ(expectedY, motionEvent.getY(i)) + << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getY(i); + } + } +}; + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + } // namespace android::inputdispatcher diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 054acc5069..bdecdb78d0 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -770,17 +770,20 @@ void BufferLayer::updateCloneBufferInfo() { // After buffer info is updated, the drawingState from the real layer needs to be copied into // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives. - // Therefore, temporarily store the relatives so they can be set in the cloned drawingState - // again. + // called. However, copying the drawingState would also overwrite the cloned layer's relatives + // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in + // the cloned drawingState again. wp tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; SortedVector> tmpZOrderRelatives = mDrawingState.zOrderRelatives; + wp tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; + InputWindowInfo tmpInputInfo = mDrawingState.inputInfo; + mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; + + mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; mDrawingState.zOrderRelatives = tmpZOrderRelatives; + mDrawingState.inputInfo = tmpInputInfo; } } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 35fc4be056..e7572f0054 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2025,6 +2025,7 @@ bool Layer::isRemovedFromCurrentState() const { InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; + info.id = sequence; if (info.displayId == ADISPLAY_ID_NONE) { info.displayId = getLayerStack(); @@ -2081,9 +2082,29 @@ InputWindowInfo Layer::fillInputInfo() { info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); } + // If the layer is a clone, we need to crop the input region to cloned root to prevent + // touches from going outside the cloned area. + if (isClone()) { + sp clonedRoot = getClonedRoot(); + if (clonedRoot != nullptr) { + Rect rect(clonedRoot->mScreenBounds); + info.touchableRegion = info.touchableRegion.intersect(rect); + } + } + return info; } +sp Layer::getClonedRoot() { + if (mClonedChild != nullptr) { + return this; + } + if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { + return nullptr; + } + return mDrawingParent.promote()->getClonedRoot(); +} + bool Layer::hasInput() const { return mDrawingState.inputInfo.token != nullptr; } @@ -2119,10 +2140,6 @@ void Layer::setInitialValuesForClone(const sp& clonedFrom) { // copy drawing state from cloned layer mDrawingState = clonedFrom->mDrawingState; mClonedFrom = clonedFrom; - - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; } void Layer::updateMirrorInfo() { @@ -2157,9 +2174,6 @@ void Layer::updateClonedDrawingState(std::map, sp>& clonedLayer if (isClonedFromAlive()) { sp clonedFrom = getClonedFrom(); mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; clonedLayersMap.emplace(clonedFrom, this); } @@ -2198,7 +2212,24 @@ void Layer::updateClonedChildren(const sp& mirrorRoot, } } -void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap) { +void Layer::updateClonedInputInfo(const std::map, sp>& clonedLayersMap) { + auto cropLayer = mDrawingState.touchableRegionCrop.promote(); + if (cropLayer != nullptr) { + if (clonedLayersMap.count(cropLayer) == 0) { + // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to + // self as crop layer to avoid going outside bounds. + mDrawingState.touchableRegionCrop = this; + } else { + const sp& clonedCropLayer = clonedLayersMap.at(cropLayer); + mDrawingState.touchableRegionCrop = clonedCropLayer; + } + } + // Cloned layers shouldn't handle watch outside since their z order is not determined by + // WM or the client. + mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH; +} + +void Layer::updateClonedRelatives(const std::map, sp>& clonedLayersMap) { mDrawingState.zOrderRelativeOf = nullptr; mDrawingState.zOrderRelatives.clear(); @@ -2206,11 +2237,11 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap return; } - sp clonedFrom = getClonedFrom(); + const sp& clonedFrom = getClonedFrom(); for (wp& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { - sp relative = relativeWeak.promote(); - auto clonedRelative = clonedLayersMap[relative]; - if (clonedRelative != nullptr) { + const sp& relative = relativeWeak.promote(); + if (clonedLayersMap.count(relative) > 0) { + auto& clonedRelative = clonedLayersMap.at(relative); mDrawingState.zOrderRelatives.add(clonedRelative); } } @@ -2220,12 +2251,14 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap // In that case, we treat the layer as if the relativeOf has been removed. This way, it will // still traverse the children, but the layer with the missing relativeOf will not be shown // on screen. - sp relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); - sp clonedRelativeOf = clonedLayersMap[relativeOf]; - if (clonedRelativeOf != nullptr) { + const sp& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); + if (clonedLayersMap.count(relativeOf) > 0) { + const sp& clonedRelativeOf = clonedLayersMap.at(relativeOf); mDrawingState.zOrderRelativeOf = clonedRelativeOf; } + updateClonedInputInfo(clonedLayersMap); + for (sp& child : mDrawingChildren) { child->updateClonedRelatives(clonedLayersMap); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 843d3ae88e..d697a6a182 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -497,8 +497,9 @@ protected: void updateClonedDrawingState(std::map, sp>& clonedLayersMap); void updateClonedChildren(const sp& mirrorRoot, std::map, sp>& clonedLayersMap); - void updateClonedRelatives(std::map, sp> clonedLayersMap); + void updateClonedRelatives(const std::map, sp>& clonedLayersMap); void addChildToDrawing(const sp& layer); + void updateClonedInputInfo(const std::map, sp>& clonedLayersMap); public: /* @@ -972,6 +973,10 @@ private: // Returns true if the layer can draw shadows on its border. virtual bool canDrawShadows() const { return true; } + + // Find the root of the cloned hierarchy, this means the first non cloned parent. + // This will return null if first non cloned parent is not found. + sp getClonedRoot(); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From cb9232108322f4e74583feaf27c44c5331e4fa35 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 30 Dec 2019 14:05:11 -0800 Subject: Remove layer from window handle since it's no longer used Fixes: 146671630 Test: Builds and runs Change-Id: I095228a361ecdda8e1abd48c6d32b085b0dd20cf --- include/input/InputWindow.h | 1 - libs/input/InputWindow.cpp | 2 -- libs/input/tests/InputWindow_test.cpp | 2 -- .../benchmarks/InputDispatcher_benchmarks.cpp | 1 - services/inputflinger/dispatcher/InputDispatcher.cpp | 17 +++++++---------- services/inputflinger/tests/InputDispatcher_test.cpp | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index cbd64d520e..c44db514d2 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -161,7 +161,6 @@ struct InputWindowInfo { bool hasFocus; bool hasWallpaper; bool paused; - int32_t layer; int32_t ownerPid; int32_t ownerUid; int32_t inputFeatures; diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 74a05055eb..03ca459fb9 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -91,7 +91,6 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeBool(hasFocus); output.writeBool(hasWallpaper); output.writeBool(paused); - output.writeInt32(layer); output.writeInt32(ownerPid); output.writeInt32(ownerUid); output.writeInt32(inputFeatures); @@ -135,7 +134,6 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.hasFocus = from.readBool(); ret.hasWallpaper = from.readBool(); ret.paused = from.readBool(); - ret.layer = from.readInt32(); ret.ownerPid = from.readInt32(); ret.ownerUid = from.readInt32(); ret.inputFeatures = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index cdc81d2f04..d1cb527a57 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -58,7 +58,6 @@ TEST(InputWindowInfo, Parcelling) { i.hasFocus = false; i.hasWallpaper = false; i.paused = false; - i.layer = 7; i.ownerPid = 19; i.ownerUid = 24; i.inputFeatures = 29; @@ -91,7 +90,6 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.hasFocus, i2.hasFocus); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); - ASSERT_EQ(i.layer, i2.layer); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9686ceaf2f..0d3c821215 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -170,7 +170,6 @@ public: mInfo.hasFocus = true; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 116625c03e..4766bcefed 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3793,12 +3793,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " "portalToDisplayId=%d, paused=%s, hasFocus=%s, " - "hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, " - "type=0x%08x, layer=%d, " + "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, " + "flags=0x%08x, type=0x%08x, " "frame=[%d,%d][%d,%d], globalScale=%f, " - "windowScale=(%f,%f), " - "touchableRegion=", + "windowScale=(%f,%f), touchableRegion=", i, windowInfo->name.c_str(), windowInfo->displayId, windowInfo->portalToDisplayId, toString(windowInfo->paused), @@ -3807,11 +3805,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { toString(windowInfo->visible), toString(windowInfo->canReceiveKeys), windowInfo->layoutParamsFlags, - windowInfo->layoutParamsType, windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->globalScaleFactor, windowInfo->windowXScale, - windowInfo->windowYScale); + windowInfo->layoutParamsType, windowInfo->frameLeft, + windowInfo->frameTop, windowInfo->frameRight, + windowInfo->frameBottom, windowInfo->globalScaleFactor, + windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c25122c72b..5ffc89d210 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -543,7 +543,6 @@ public: mInfo.hasFocus = false; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; -- cgit v1.2.3-59-g8ed1b From 7174efe2182376411ba2bcfb67d7e6025111183f Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 16:55:27 -0700 Subject: InputWindow: Default initialize all values A future patch will initialize InputWindows from a new location. It's a little troublesome to have to keep track of which values are initialized by default and not. Bug: 152064592 Test: Existing tests pass Change-Id: I200d9c050d8bf6d96c45ce75f95cb4d6537ab0ba --- include/input/InputWindow.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c44db514d2..edaf8f530b 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -123,17 +123,17 @@ struct InputWindowInfo { // input windows that have the same token. sp token; // This uniquely identifies the input window. - int32_t id = 0; + int32_t id = -1; std::string name; - int32_t layoutParamsFlags; - int32_t layoutParamsType; - nsecs_t dispatchingTimeout; + int32_t layoutParamsFlags = 0; + int32_t layoutParamsType = 0; + nsecs_t dispatchingTimeout = -1; /* These values are filled in by SurfaceFlinger. */ - int32_t frameLeft; - int32_t frameTop; - int32_t frameRight; - int32_t frameBottom; + int32_t frameLeft = -1; + int32_t frameTop = -1; + int32_t frameRight = -1; + int32_t frameBottom = -1; /* * SurfaceFlinger consumes this value to shrink the computed frame. This is @@ -145,7 +145,7 @@ struct InputWindowInfo { // A global scaling factor for all windows. Unlike windowScaleX/Y this results // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. - float globalScaleFactor; + float globalScaleFactor = 1.0f; // Scaling factors applied to individual windows. float windowXScale = 1.0f; @@ -156,18 +156,18 @@ struct InputWindowInfo { * to absolute coordinates by SurfaceFlinger once the frame is computed. */ Region touchableRegion; - bool visible; - bool canReceiveKeys; - bool hasFocus; - bool hasWallpaper; - bool paused; - int32_t ownerPid; - int32_t ownerUid; - int32_t inputFeatures; - int32_t displayId; + bool visible = false; + bool canReceiveKeys = false; + bool hasFocus = false; + bool hasWallpaper = false; + bool paused = false; + int32_t ownerPid = -1; + int32_t ownerUid = -1; + int32_t inputFeatures = 0; + int32_t displayId = ADISPLAY_ID_NONE; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; - bool replaceTouchableRegionWithCrop; + bool replaceTouchableRegionWithCrop = false; wp touchableRegionCropHandle; void addTouchableRegion(const Rect& region); -- cgit v1.2.3-59-g8ed1b From 2984b7af4f7969cdc02dea6a1722635cc9a432dd Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 17:06:45 -0700 Subject: InputDispatcher: Fix support for INPUT_FEATURE_NO_INPUT_CHANNEL In preparation for an occlusion detection fix, we are going to begin sending windows with NO_INPUT_CHANNEL to InputFlinger. There are 3 obstacles we address here: 1. InputDispatcher ignores windows with no InputChannel when updating input windows. We modify the code to allow such windows if they have INPUT_FEATURE_NO_INPUT_CHANNEL. 2. The parcelling code currently has an optimization to avoid sending the rest of the fields if token is null. We rebase this optimization on whether or not a name is set. 3. InputWindowHandle::getName checks if there is a token to consider the window invalid. We instead check if a name is set. Bug: 152064592 Test: Existing tests pass Change-Id: I8a85f46b6c44866c7f73daafa5e8ff4c9251c366 --- include/input/InputWindow.h | 2 +- libs/input/InputWindow.cpp | 9 ++------- services/inputflinger/dispatcher/InputDispatcher.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index edaf8f530b..a695a8ffda 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -213,7 +213,7 @@ public: } inline std::string getName() const { - return mInfo.token ? mInfo.name : ""; + return !mInfo.name.empty() ? mInfo.name : ""; } inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 03ca459fb9..6e4c97ded2 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -65,7 +65,7 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { } status_t InputWindowInfo::write(Parcel& output) const { - if (token == nullptr) { + if (name.empty()) { output.writeInt32(0); return OK; } @@ -110,12 +110,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { return ret; } - sp token = from.readStrongBinder(); - if (token == nullptr) { - return ret; - } - - ret.token = token; + ret.token = from.readStrongBinder(); ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ec61b0c63..403e21dffd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3598,8 +3598,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); + continue; } - continue; } if (info->displayId != displayId) { -- cgit v1.2.3-59-g8ed1b From 9499a112e3a0ba25e8f5ff2049cc529c2982698f Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Mon, 4 May 2020 12:01:17 +0800 Subject: Makes TYPE_NOTIFICATION_SHADE as trusted overlay Fix the side effect after we split notification_shade window from status bar. Bug: 155373298 Test: follow the steps from b/149320322 Change-Id: I3362186b22505d21ec6e0ad779d4a26304ed782c Merged-In: I3362186b22505d21ec6e0ad779d4a26304ed782c --- include/input/InputWindow.h | 77 +++++++++++++++++++++++---------------------- libs/input/InputWindow.cpp | 19 ++++++----- 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index a695a8ffda..856c54d89e 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -68,46 +68,47 @@ struct InputWindowInfo { // Window types from WindowManager.LayoutParams enum { FIRST_APPLICATION_WINDOW = 1, - TYPE_BASE_APPLICATION = 1, - TYPE_APPLICATION = 2, + TYPE_BASE_APPLICATION = 1, + TYPE_APPLICATION = 2, TYPE_APPLICATION_STARTING = 3, LAST_APPLICATION_WINDOW = 99, - FIRST_SUB_WINDOW = 1000, - TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, - TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, - TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, - TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, - TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, - LAST_SUB_WINDOW = 1999, - FIRST_SYSTEM_WINDOW = 2000, - TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, - TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, - TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, - TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, - TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, - TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, - TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, - TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, - TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, - TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, - TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, - TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, - TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, - TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, - TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, - TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15, - TYPE_DRAG = FIRST_SYSTEM_WINDOW+16, - TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17, - TYPE_POINTER = FIRST_SYSTEM_WINDOW+18, - TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, - TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, - TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, - TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22, - TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24, - TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, - TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32, - TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34, - LAST_SYSTEM_WINDOW = 2999, + FIRST_SUB_WINDOW = 1000, + TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, + TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1, + TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2, + TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, + TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, + LAST_SUB_WINDOW = 1999, + FIRST_SYSTEM_WINDOW = 2000, + TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, + TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, + TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2, + TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, + TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4, + TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5, + TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, + TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, + TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, + TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, + TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, + TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, + TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, + TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13, + TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, + TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, + TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16, + TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, + TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18, + TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, + TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, + TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, + TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, + TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, + TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, + TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, + TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, + TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + LAST_SYSTEM_WINDOW = 2999, }; enum { diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 6e4c97ded2..b27b050d28 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -43,16 +43,15 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { } bool InputWindowInfo::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD - || layoutParamsType == TYPE_INPUT_METHOD_DIALOG - || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY - || layoutParamsType == TYPE_STATUS_BAR - || layoutParamsType == TYPE_NAVIGATION_BAR - || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL - || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY - || layoutParamsType == TYPE_DOCK_DIVIDER - || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY - || layoutParamsType == TYPE_INPUT_CONSUMER; + return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || + layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || + layoutParamsType == TYPE_NOTIFICATION_SHADE || + layoutParamsType == TYPE_NAVIGATION_BAR || + layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || + layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || + layoutParamsType == TYPE_DOCK_DIVIDER || + layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || + layoutParamsType == TYPE_INPUT_CONSUMER; } bool InputWindowInfo::supportsSplitTouch() const { -- cgit v1.2.3-59-g8ed1b From 466cdea8d2de302f6be4d9f59816dd2c6078decf Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Mon, 4 May 2020 13:53:00 -0400 Subject: Make TYPE_TRUSTED_APPLICATION_OVERLAY a trusted overlay. Test: accept a permission dialog while bubbles are there too Fixes: 149320322 Change-Id: I3767e2d93d0bcb216483a12d94ffb13ca0051c7e --- include/input/InputWindow.h | 1 + libs/input/InputWindow.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 856c54d89e..c5e56fd91f 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -108,6 +108,7 @@ struct InputWindowInfo { TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42, LAST_SYSTEM_WINDOW = 2999, }; diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index b27b050d28..85a2015e43 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -42,6 +42,7 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && y >= frameTop && y < frameBottom; } +// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. bool InputWindowInfo::isTrustedOverlay() const { return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || @@ -51,7 +52,8 @@ bool InputWindowInfo::isTrustedOverlay() const { layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || layoutParamsType == TYPE_DOCK_DIVIDER || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || - layoutParamsType == TYPE_INPUT_CONSUMER; + layoutParamsType == TYPE_INPUT_CONSUMER || + layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY; } bool InputWindowInfo::supportsSplitTouch() const { -- cgit v1.2.3-59-g8ed1b From ffaa2b163055c644dec7c2c681ed8b608e48f6b9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 May 2020 21:43:02 -0700 Subject: Add basic ANR test To reduce the delta of the upcoming ANR refactor, add a basic ANR test here. This will also help highlight the difference in behaviour from the current code to the new code. To cause an ANR today, the socket needs to be blocked, which means that we need to send ~ 50 events to the unresponsive window. These workarounds will be removed when the ANRs are refactored. Bug: 143459140 Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*InputDispatcherSingleWindowAnr*" --gtest_repeat=1000 --gtest_break_on_failure Change-Id: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e Merged-In: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e --- include/input/InputApplication.h | 5 + include/input/InputWindow.h | 5 + services/inputflinger/dispatcher/Entry.cpp | 6 + services/inputflinger/dispatcher/Entry.h | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 81 +++---- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../inputflinger/tests/InputDispatcher_test.cpp | 240 ++++++++++++++++++--- 7 files changed, 274 insertions(+), 71 deletions(-) (limited to 'include/input/InputWindow.h') diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 7f04611309..86de394a31 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -61,6 +61,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + inline sp getApplicationToken() const { return mInfo.token; } diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c5e56fd91f..2dac5b62a7 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -222,6 +222,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index a1eb0079df..21c8ae165d 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -102,6 +102,12 @@ EventEntry::~EventEntry() { releaseInjectionState(); } +std::string EventEntry::getDescription() const { + std::string result; + appendDescription(result); + return result; +} + void EventEntry::release() { refCount -= 1; if (refCount == 0) { diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ab481bd411..a135409365 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -83,6 +83,8 @@ struct EventEntry { virtual void appendDescription(std::string& msg) const = 0; + std::string getDescription() const; + protected: EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a21d1ebd2d..e6e3347ae9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,7 +78,7 @@ namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. -constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec +constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s; // Amount of time to allow for all pending events to be processed when an app switch // key is on the way. This is used to preempt input dispatch and drop input events @@ -1295,11 +1295,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - } - nsecs_t timeout; + ALOGI("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + std::chrono::nanoseconds timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != nullptr) { @@ -1311,7 +1309,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutTime = currentTime + timeout.count(); mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationToken.clear(); @@ -1353,10 +1351,10 @@ void InputDispatcher::removeWindowByTokenLocked(const sp& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp& inputConnectionToken) { - if (newTimeout > 0) { + nsecs_t timeoutExtension, const sp& inputConnectionToken) { + if (timeoutExtension > 0) { // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; + mInputTargetWaitTimeoutTime = now() + timeoutExtension; } else { // Give up. mInputTargetWaitTimeoutExpired = true; @@ -4048,11 +4046,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const int32_t displayId = it.first; const sp& applicationHandle = it.second; dump += StringPrintf(INDENT2 "displayId=%" PRId32 - ", name='%s', dispatchingTimeout=%0.3fms\n", + ", name='%s', dispatchingTimeout=%" PRId64 "ms\n", displayId, applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / - 1000000.0); + ns2ms(applicationHandle + ->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) + .count())); } } else { dump += StringPrintf(INDENT "FocusedApplications: \n"); @@ -4132,9 +4131,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 + "ms\n", windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + ns2ms(windowInfo->dispatchingTimeout)); } } else { dump += INDENT2 "Windows: \n"; @@ -4167,7 +4167,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "RecentQueue: \n"; @@ -4178,8 +4178,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "PendingEvent:\n"; dump += INDENT2; mPendingEvent->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", + ns2ms(currentTime - mPendingEvent->eventTime)); } else { dump += INDENT "PendingEvent: \n"; } @@ -4190,7 +4190,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "InboundQueue: \n"; @@ -4225,9 +4225,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 + "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime)); } } else { dump += INDENT3 "OutboundQueue: \n"; @@ -4240,10 +4241,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", + "age=%" PRId64 "ms, wait=%" PRId64 "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime), + ns2ms(currentTime - entry->deliveryTime)); } } else { dump += INDENT3 "WaitQueue: \n"; @@ -4254,16 +4255,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); + dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", + ns2ms(mAppSwitchDueTime - now())); } else { dump += INDENT "AppSwitch: not pending\n"; } dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); - dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); + dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", + ns2ms(mConfig.keyRepeatTimeout)); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector& monitors) { @@ -4365,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i return BAD_VALUE; } - [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); - ALOG_ASSERT(removed); + removeConnectionLocked(connection); mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { @@ -4468,7 +4468,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) { +sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; } @@ -4483,6 +4483,10 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn return nullptr; } +void InputDispatcher::removeConnectionLocked(const sp& connection) { + removeByValue(mConnectionsByFd, connection); +} + void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { @@ -4587,12 +4591,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); - nsecs_t newTimeout = + const nsecs_t timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token); + resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4647,11 +4651,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); } reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 89b5089e49..ff7be87609 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -197,6 +197,11 @@ private: // All registered connections mapped by channel file descriptor. std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); + sp getConnectionLocked(const sp& inputConnectionToken) const + REQUIRES(mLock); + + void removeConnectionLocked(const sp& connection) REQUIRES(mLock); + struct IBinderHash { std::size_t operator()(const sp& b) const { return std::hash{}(b.get()); @@ -209,7 +214,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - sp getConnectionLocked(const sp& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f33cc65c2a..13e835427f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,12 +17,14 @@ #include "../dispatcher/InputDispatcher.h" #include +#include #include #include #include #include #include +#include #include #include @@ -119,6 +121,33 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + // This function must be called soon after the expected ANR timer starts, + // because we are also checking how much time has passed. + void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, + const sp& expectedApplication, + const sp& expectedToken) { + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::unique_lock lock(mLock); + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + android::base::ScopedLockAssertion assumeLocked(mLock); + + // If there is an ANR, Dispatcher won't be idle because there are still events + // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle + // before checking if ANR was called. + // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide + // it some time to act. 100ms seems reasonable. + mNotifyAnr.wait_for(lock, timeToWait, + [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + ASSERT_TRUE(mNotifyAnrWasCalled); + // Ensure that the ANR didn't get raised too early. We can't be too strict here because + // the dispatcher started counting before this function was called + ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for + mNotifyAnrWasCalled = false; + ASSERT_EQ(expectedApplication, mLastAnrApplication); + ASSERT_EQ(expectedToken, mLastAnrWindowToken); + } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { mConfig.keyRepeatTimeout = timeout; mConfig.keyRepeatDelay = delay; @@ -131,14 +160,26 @@ private: sp mOnPointerDownToken GUARDED_BY(mLock); std::optional mLastNotifySwitch GUARDED_BY(mLock); + // ANR handling + bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; + sp mLastAnrApplication GUARDED_BY(mLock); + sp mLastAnrWindowToken GUARDED_BY(mLock); + std::condition_variable mNotifyAnr; + std::chrono::nanoseconds mAnrTimeout = 0ms; + virtual void notifyConfigurationChanged(nsecs_t when) override { std::scoped_lock lock(mLock); mConfigurationChangedTime = when; } - virtual nsecs_t notifyAnr(const sp&, const sp&, - const std::string&) override { - return 0; + virtual nsecs_t notifyAnr(const sp& application, + const sp& windowToken, const std::string&) override { + std::scoped_lock lock(mLock); + mLastAnrApplication = application; + mLastAnrWindowToken = windowToken; + mNotifyAnrWasCalled = true; + mNotifyAnr.notify_all(); + return mAnrTimeout.count(); } virtual void notifyInputChannelBroken(const sp&) override {} @@ -309,6 +350,20 @@ protected: mFakePolicy.clear(); mDispatcher.clear(); } + + /** + * Used for debugging when writing the test + */ + void dumpDispatcherState() { + std::string dump; + mDispatcher->dump(dump); + std::stringstream ss(dump); + std::string to; + + while (std::getline(ss, to, '\n')) { + ALOGE("%s", to.c_str()); + } + } }; @@ -502,13 +557,20 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: - FakeApplicationHandle() {} + FakeApplicationHandle() { + mInfo.name = "Fake Application"; + mInfo.token = new BBinder(); + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + } virtual ~FakeApplicationHandle() {} virtual bool updateInfo() override { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } + + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } }; class FakeInputReceiver { @@ -519,6 +581,20 @@ public: } InputEvent* consume() { + InputEvent* event; + std::optional consumeSeq = receiveEvent(&event); + if (!consumeSeq) { + return nullptr; + } + finishEvent(*consumeSeq); + return event; + } + + /** + * Receive an event without acknowledging it. + * Return the sequence number that could later be used to send finished signal. + */ + std::optional receiveEvent(InputEvent** outEvent = nullptr) { uint32_t consumeSeq; InputEvent* event; @@ -535,23 +611,29 @@ public: if (status == WOULD_BLOCK) { // Just means there's no event available. - return nullptr; + return std::nullopt; } if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return nullptr; + return std::nullopt; } if (event == nullptr) { ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - return nullptr; + return std::nullopt; } - - status = mConsumer->sendFinishedSignal(consumeSeq, true); - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + if (outEvent != nullptr) { + *outEvent = event; } - return event; + return consumeSeq; + } + + /** + * To be used together with "receiveEvent" to complete the consumption of an event. + */ + void finishEvent(uint32_t consumeSeq) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, @@ -668,6 +750,10 @@ public: void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -740,6 +826,19 @@ public: expectedFlags); } + std::optional receiveEvent() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::nullopt; + } + return mInputReceiver->receiveEvent(); + } + + void finishEvent(uint32_t sequenceNum) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->finishEvent(sequenceNum); + } + InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; @@ -765,16 +864,15 @@ private: std::atomic FakeWindowHandle::sId{1}; -static int32_t injectKeyDown(const sp& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, + int32_t displayId = ADISPLAY_ID_NONE) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, - AMETA_NONE, - /* repeatCount */ 0, currentTime, currentTime); + INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + repeatCount, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -783,10 +881,16 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionEvent(const sp& dispatcher, int32_t action, - int32_t source, int32_t displayId, int32_t x, int32_t y, - int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, - int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { +static int32_t injectKeyDown(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); +} + +static int32_t injectMotionEvent( + const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, + const PointF& position, + const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -796,8 +900,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. @@ -806,7 +910,7 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, - /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -819,14 +923,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t static int32_t injectMotionDown(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } static int32_t injectMotionUp(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); } static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { @@ -1051,7 +1153,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // left window. This event should be dispatched to the left window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, - ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400})); windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowRight->assertNoEvents(); } @@ -2185,4 +2287,82 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } +class InputDispatcherSingleWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(20ms); + mWindow = + new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 30, 30)); + mWindow->setDispatchingTimeout(10ms); + mWindow->setFocus(true); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + mWindow.clear(); + } + +protected: + sp mApplication; + sp mWindow; + static constexpr PointF WINDOW_LOCATION = {20, 20}; + + void tapOnWindow() { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + } +}; + +// Send an event to the app and have the app not respond right away. +// Make sure that ANR is raised +TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + // Also, overwhelm the socket to make sure ANR starts + for (size_t i = 0; i < 100; i++) { + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); + } + + std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// Send a key to the app and have the app not respond right away. +TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { + // Inject a key, and don't respond - expect that ANR is called. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + std::optional sequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(sequenceNum); + + // Start ANR process by sending a 2nd key, which would trigger the check for whether + // waitQueue is empty + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b