Open BirdLearn opened 3 months ago
在日志中看到配置中的多个 black widget,在 native 层日志均有打印识别到,但是在运行到实际判断 point 是否位于黑控件区域时,却打印 Rects: 1 ,即当前Activity中仅保存有一个黑控件区域,这与配置中的多个黑控件不符,因此导致黑控件不生效。
通过走查代码发现,在处理黑控件时逻辑如下
void Preference::resolveBlackWidgets(const ElementPtr &rootXML, const std::string &activity) {
// black widgets
if (!this->_blackWidgetActions.empty()) {
for (const CustomActionPtr &blackWidgetAction: this->_blackWidgetActions) {
if (!activity.empty() && blackWidgetAction->activity != activity)
continue;
XpathPtr xpath = blackWidgetAction->xpath;
// read the bounds of black widget from the config
std::vector<float> bounds = blackWidgetAction->bounds;
bool hasBoundingBox = bounds.size() >= 4;
if (nullptr == this->_rootScreenSize) {
BLOGE("black widget match failed %s", "No root node in current page");
return;
}
if (hasBoundingBox && bounds[1] <= 1.1 && bounds[3] <= 1.1) {
int rootWidth = this->_rootScreenSize->right;// - rootSize->left;
int rootHeight = this->_rootScreenSize->bottom;// - rootSize->top;
bounds[0] = bounds[0] * static_cast<float>(rootWidth);
bounds[1] = bounds[1] * static_cast<float>(rootHeight);
bounds[2] = bounds[2] * static_cast<float>(rootWidth);
bounds[3] = bounds[3] * static_cast<float>(rootHeight);
}
bool xpathExistsInPage;
std::vector<ElementPtr> xpathElements;
if (xpath) {
this->findMatchedElements(xpathElements, xpath, rootXML);
BDLOG("find black widget %s %d", xpath->toString().c_str(),
(int) xpathElements.size());
}
xpathExistsInPage = xpath && !xpathElements.empty();
std::vector<RectPtr> cachedRects; // cache black widgets
if (xpathExistsInPage && !hasBoundingBox) {
BLOG("black widget xpath %s, has no bounds matched %d nodes",
xpath->toString().c_str(), (int) xpathElements.size());
for (const auto &matchedElement: xpathElements) {
BLOG("black widget, delete node: %s depends xpath",
matchedElement->getResourceID().c_str());
cachedRects.push_back(matchedElement->getBounds());
matchedElement->deleteElement();
}
}
else if (xpathExistsInPage || (!xpath && hasBoundingBox)) {
RectPtr rejectRect = std::make_shared<Rect>(bounds[0], bounds[1], bounds[2],
bounds[3]);
cachedRects.push_back(rejectRect);
std::vector<ElementPtr> elementsInRejectRect;
rootXML->recursiveElements([&rejectRect](const ElementPtr &child) -> bool {
return rejectRect->contains(child->getBounds()->center());
}, elementsInRejectRect);
BLOG("black widget xpath %s, with bounds matched %d nodes",
xpath ? xpath->toString().c_str() : "none",
(int) elementsInRejectRect.size());
for (const auto &elementInRejectRect: elementsInRejectRect) {
if (elementInRejectRect) {
BLOG("black widget, delete node: %s depends xpath",
elementInRejectRect->getResourceID().c_str());
elementInRejectRect->deleteElement();
}
}
}
this->_cachedBlackWidgetRects[activity] = cachedRects;
}
}
}
以上代码中,在 for 循环内定义了一个变量 std::vector<float> bounds = blackWidgetAction->bounds;
,并在当前for 循环结束时,将识别到的cachedRects 赋值给 _cachedBlackWidgetRects Map 中的 activity 对应的值,这样导致在循环结束后,只有最后一个黑控件的区域被保存在 _cachedBlackWidgetRects 中,因此导致黑控件不生效。
修改 resolveBlackWidgets 函数,将 cachedRects 定义在 for 在 for 循环外部,这样在循环结束后,所有的黑控件区域都会被保存在 _cachedBlackWidgetRects 中,代码如下
void Preference::resolveBlackWidgets(const ElementPtr &rootXML, const std::string &activity) {
// black widgets
if (!this->_blackWidgetActions.empty()) {
std::vector<RectPtr> cachedRects; // cache black widgets
for (const CustomActionPtr &blackWidgetAction: this->_blackWidgetActions) {
// ... ...
}
this->_cachedBlackWidgetRects[activity] = cachedRects;
}
}
黑控件仍然被点击到问题
复现场景
使用 fastbot 进行自动化测试时,偶现点击到黑控件区域,导致不符合预期的行为。
在实际使用中发现,即使配置了黑控件区域,仍然偶现点击到黑控件区域。
问题分析
使用 uiautomatorviewer 工具,识别黑控件区域坐标为 [0,2160][1080,2259],即在该区域下的坐标应被判定为不可点击区域。 通过走查日志发现:Fastbot 有点击 (682.29974,2224.0) 的操作,该坐标位于黑控件区域内部。
分析 java 层日志
分析 native 层日志
分析 java 层代码
测试出错原因
native 层判断是否命中黑控件区域逻辑,该函数代码位于 /native/events/Preference.cpp 文件 335 行
逻辑分析 这段代码的目的是检查给定的点 (pointX, pointY) 是否位于与活动(activity)相关联的黑色矩形列表中的某个矩形内部。代码存在一个逻辑错误: 在检查点是否在黑色矩形内部时,如果 iter 存在(即 activity 对应黑控件存在),则将 isInsideBlackList 设置为 true,这是不对的,因为这只能做为是否应该在黑控件区域内部的判断条件,而不是结果。在遍历黑色矩形列表时,如果找到一个包含点的矩形,则将 isInsideBlackList 再次设置为 true,但是由于 isInsideBlackList 为true ,当遍历完所有的区域,isInsideBlackList 仍然为 true,此时实际结果应该是 false。即当前的坐标不存在于当前activity中的任何黑控件中。 实际上,当前版本的代码,在这种情况下,仍然做了true的判定,会导致即使遍历完所有的屏幕分区,仍然无法找到一个不存在于黑控件区域的坐标,然后耗尽重试次数,导致最终点击的坐标不可控。
解决方案
为了修复这个问题,我们可以在找到一个包含点的矩形后立即返回 true,而不是继续遍历其他矩形。如果没有找到包含点的矩形,则最终返回 false。
正确逻辑应该如下
基于master 代码修改后的代码打出的so包验证该问题未再复现 有需要现成 so 包的同学,可以自取 https://github.com/BirdLearn/fastboot_fix_libs