Merge "Added input support for cloned layers"
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index f852cd0..cbd64d5 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -119,7 +119,11 @@
/* 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<IBinder> token;
+ // This uniquely identifies the input window.
+ int32_t id = 0;
std::string name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
@@ -203,6 +207,8 @@
sp<IBinder> getToken() const;
+ int32_t getId() const { return mInfo.id; }
+
sp<IBinder> getApplicationToken() {
return mInfo.applicationInfo.token;
}
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..74a0505 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -73,6 +73,7 @@
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 @@
}
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 6db18ab..cdc81d2 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
sp<IBinder> 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 @@
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 5a49b5e..116625c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -240,6 +240,18 @@
return removed;
}
+static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& 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 @@
// 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<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
- std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+ std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
for (const sp<InputWindowHandle>& handle : oldHandles) {
- oldHandlesByTokens[handle->getToken()] = handle;
+ oldHandlesById[handle->getId()] = handle;
}
std::vector<sp<InputWindowHandle>> newHandles;
@@ -3311,8 +3323,8 @@
continue;
}
- if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
- const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken());
+ if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+ const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
} else {
@@ -3370,7 +3382,7 @@
sp<InputWindowHandle> 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 b4d7608..c25122c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -526,6 +526,7 @@
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 @@
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 @@
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<IBinder> getToken() { return mInfo.token; }
+
private:
std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
@@ -667,6 +684,10 @@
static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
const std::vector<PointF>& 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 @@
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<FakeApplicationHandle> 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<FakeWindowHandle> mWindow1;
+ sp<FakeWindowHandle> 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<FakeWindowHandle>& window, int32_t expectedAction,
+ const std::vector<PointF>& 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<const MotionEvent&>(*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 054acc5..bdecdb7 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -770,17 +770,20 @@
// 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<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ wp<Layer> 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 57aa7f9..c7eb9c3 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2030,6 +2030,7 @@
InputWindowInfo Layer::fillInputInfo() {
InputWindowInfo info = mDrawingState.inputInfo;
+ info.id = sequence;
if (info.displayId == ADISPLAY_ID_NONE) {
info.displayId = getLayerStack();
@@ -2086,9 +2087,29 @@
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<Layer> clonedRoot = getClonedRoot();
+ if (clonedRoot != nullptr) {
+ Rect rect(clonedRoot->mScreenBounds);
+ info.touchableRegion = info.touchableRegion.intersect(rect);
+ }
+ }
+
return info;
}
+sp<Layer> 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;
}
@@ -2124,10 +2145,6 @@
// 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() {
@@ -2162,9 +2179,6 @@
if (isClonedFromAlive()) {
sp<Layer> 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);
}
@@ -2203,7 +2217,24 @@
}
}
-void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap) {
+void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& 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<Layer>& 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<Layer>, sp<Layer>>& clonedLayersMap) {
mDrawingState.zOrderRelativeOf = nullptr;
mDrawingState.zOrderRelatives.clear();
@@ -2211,11 +2242,11 @@
return;
}
- sp<Layer> clonedFrom = getClonedFrom();
+ const sp<Layer>& clonedFrom = getClonedFrom();
for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
- sp<Layer> relative = relativeWeak.promote();
- auto clonedRelative = clonedLayersMap[relative];
- if (clonedRelative != nullptr) {
+ const sp<Layer>& relative = relativeWeak.promote();
+ if (clonedLayersMap.count(relative) > 0) {
+ auto& clonedRelative = clonedLayersMap.at(relative);
mDrawingState.zOrderRelatives.add(clonedRelative);
}
}
@@ -2225,12 +2256,14 @@
// 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<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
- sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf];
- if (clonedRelativeOf != nullptr) {
+ const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+ if (clonedLayersMap.count(relativeOf) > 0) {
+ const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
mDrawingState.zOrderRelativeOf = clonedRelativeOf;
}
+ updateClonedInputInfo(clonedLayersMap);
+
for (sp<Layer>& child : mDrawingChildren) {
child->updateClonedRelatives(clonedLayersMap);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 843d3ae..d697a6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -497,8 +497,9 @@
void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void updateClonedChildren(const sp<Layer>& mirrorRoot,
std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap);
+ void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void addChildToDrawing(const sp<Layer>& layer);
+ void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
public:
/*
@@ -972,6 +973,10 @@
// 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<Layer> getClonedRoot();
};
} // namespace android