* Copyright (C) 2025 Huawei Device Co., Ltd.
* 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.
*/
#include "element_operator_manager.h"
#include <cinttypes>
#include <future>
#include <chrono>
#include "accessible_ability_manager_service.h"
#include "accessibility_constants.h"
#include "accessibility_element_info.h"
#include "accessibility_window_manager.h"
#include "hilog_wrapper.h"
#include "ipc_skeleton.h"
#include "singleton.h"
#include "utils.h"
namespace OHOS {
namespace Accessibility {
namespace {
constexpr uint32_t ELEMENT_MOVE_BIT = 40;
constexpr int64_t ELEMENT_ID_INVALID = -1;
constexpr int32_t WINDOW_ID_INVALID = -1;
constexpr uint32_t ELEMENT_TIMEOUT_MS = 5000;
constexpr int32_t REQUEST_ID_MAX = 0x0000FFFF;
constexpr int32_t REQUEST_ID_MIN = 1;
}
void ElementOperatorManager::AddAccessibilityWindowConnection(
const int32_t windowId, const sptr<AccessibilityWindowConnection>& interactionConnection)
{
HILOG_INFO("windowId(%{public}d)", windowId);
std::lock_guard lock(asacConnectionsMutex_);
asacConnections_[windowId] = interactionConnection;
}
void ElementOperatorManager::RemoveAccessibilityWindowConnection(const int32_t windowId)
{
HILOG_INFO("windowId(%{public}d)", windowId);
std::lock_guard lock(asacConnectionsMutex_);
std::map<int32_t, sptr<AccessibilityWindowConnection>>::iterator it = asacConnections_.find(windowId);
if (it != asacConnections_.end()) {
asacConnections_.erase(it);
}
}
const sptr<AccessibilityWindowConnection> ElementOperatorManager::GetAccessibilityWindowConnection(
const int32_t windowId)
{
std::lock_guard lock(asacConnectionsMutex_);
HILOG_DEBUG("window id[%{public}d] interactionOperators's size[%{public}zu]", windowId, asacConnections_.size());
for (auto &asacConnection : asacConnections_) {
HILOG_DEBUG("window id of asacConnection is %{public}d", asacConnection.first);
}
if (asacConnections_.count(windowId) > 0) {
return asacConnections_[windowId];
}
return nullptr;
}
void ElementOperatorManager::DeleteConnectionAndDeathRecipient(int32_t windowId,
const sptr<AccessibilityWindowConnection>& connection)
{
HILOG_DEBUG("windowId(%{public}d)", windowId);
if (!connection || !connection->GetProxy()) {
HILOG_ERROR("connection or proxy is null");
return;
}
RemoveAccessibilityWindowConnection(windowId);
auto object = connection->GetProxy()->AsObject();
if (object) {
std::lock_guard lock(deathRecipientMutex_);
auto iter = interactionOperationDeathRecipients_.find(windowId);
if (iter != interactionOperationDeathRecipients_.end()) {
sptr<IRemoteObject::DeathRecipient> deathRecipient = iter->second;
bool result = object->RemoveDeathRecipient(deathRecipient);
HILOG_DEBUG("The result of deleting connection's death recipient is %{public}d", result);
interactionOperationDeathRecipients_.erase(iter);
}
}
}
RetError ElementOperatorManager::VerifyingToKenId(const uint32_t tokenId, const int32_t windowId,
const int64_t elementId)
{
int32_t treeId = (static_cast<uint64_t>(elementId) >> ELEMENT_MOVE_BIT);
HILOG_DEBUG("VerifyingToKenId: treeId[%{public}d], windowId[%{public}d], elementId[%{public}" PRId64 "]",
treeId, windowId, elementId);
if (elementId == ELEMENT_ID_INVALID || windowId == WINDOW_ID_INVALID) {
HILOG_DEBUG("windowId[%{public}d], elementId[%{public}" PRId64 "]", windowId, elementId);
return RET_OK;
}
HILOG_DEBUG("treeId %{public}d, windowId %{public}d", treeId, windowId);
int32_t realId = Singleton<AccessibilityWindowManager>::GetInstance().ConvertToRealWindowId(windowId,
FOCUS_TYPE_INVALID);
sptr<AccessibilityWindowConnection> connection = GetAccessibilityWindowConnection(realId);
if (connection == nullptr) {
HILOG_ERROR("connection is empty.");
return RET_ERR_REGISTER_EXIST;
}
uint32_t expectTokenId = connection->GetTokenIdMap(treeId);
if (tokenId != expectTokenId) {
HILOG_ERROR("tokenId error!");
return RET_ERR_TOKEN_ID;
}
return RET_OK;
}
ErrCode ElementOperatorManager::GetRootParentId(int32_t windowId, int32_t treeId, int64_t& parentId)
{
HILOG_INFO("aa search treeParent from aams, windowId: %{public}d, treeId: %{public}d", windowId, treeId);
sptr<AccessibilityWindowConnection> connection = GetAccessibilityWindowConnection(windowId);
if (!connection) {
HILOG_WARN("The operator of windowId[%{public}d] has not been registered.", windowId);
return RET_ERR_NO_CONNECTION;
}
connection->GetRootParentId(treeId, parentId);
return RET_OK;
}
bool ElementOperatorManager::GetParentElementRecursively(int32_t windowId, int64_t elementId,
std::vector<AccessibilityElementInfo>& infos)
{
HILOG_DEBUG("windowId(%{public}d), elementId(%{public}" PRId64 ")", windowId, elementId);
int32_t treeId = 0;
sptr<IAccessibilityElementOperator> elementOperator = nullptr;
sptr<AccessibilityWindowConnection> connection = GetAccessibilityWindowConnection(windowId);
if (!connection) {
HILOG_ERROR("GetAccessibilityWindowConnection failed");
return false;
}
if (elementId > 0) {
treeId = GetTreeIdBySplitElementId(elementId);
elementOperator = connection->GetCardProxy(treeId);
} else {
elementOperator = connection->GetProxy();
}
if (elementOperator == nullptr) {
HILOG_DEBUG("elementOperator failed elementId: %{public}" PRId64 " winId: %{public}d treeId: %{public}d",
elementId, windowId, treeId);
return false;
}
sptr<ElementOperatorCallbackImpl> callBack = new(std::nothrow) ElementOperatorCallbackImpl();
if (callBack == nullptr) {
HILOG_ERROR("Failed to create callBack.");
return false;
}
ffrt::future<void> promiseFuture = callBack->promise_.get_future();
int32_t requestId = GenerateRequestId();
AddRequestId(windowId, treeId, requestId, callBack);
elementOperator->SearchElementInfoByAccessibilityId(elementId, requestId, callBack, 0);
ffrt::future_status waitFocus = promiseFuture.wait_for(std::chrono::milliseconds(ELEMENT_TIMEOUT_MS));
if (waitFocus != ffrt::future_status::ready) {
ipcTimeoutNum_++;
HILOG_ERROR("Failed to wait result, number %{public}" PRId64 "", ipcTimeoutNum_);
RemoveRequestId(requestId);
return false;
}
for (const auto& info : callBack->elementInfosResult_) {
if (info.GetAccessibilityId() == AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID) {
HILOG_ERROR("SearchElementInfoByAccessibilityId elementInfo from ace is wrong");
return false;
}
}
infos = callBack->elementInfosResult_;
HILOG_DEBUG("Get parent element success, size %{public}zu", infos.size());
return true;
}
void ElementOperatorManager::OutsideTouch(int32_t windowId)
{
HILOG_DEBUG();
sptr<AccessibilityWindowConnection> connection = GetAccessibilityWindowConnection(windowId);
if (connection && connection->GetProxy()) {
connection->GetProxy()->OutsideTouch();
}
}
void ElementOperatorManager::AddDeathRecipient(int32_t windowId,
const sptr<IRemoteObject::DeathRecipient>& deathRecipient)
{
std::lock_guard lock(deathRecipientMutex_);
interactionOperationDeathRecipients_[windowId] = deathRecipient;
}
void ElementOperatorManager::AddDeathRecipient(int32_t windowId, int32_t treeId,
const sptr<IRemoteObject::DeathRecipient>& deathRecipient)
{
std::lock_guard lock(deathRecipientMutex_);
interactionOperationDeathMap_[windowId][treeId] = deathRecipient;
}
void ElementOperatorManager::RemoveDeathRecipient(int32_t windowId)
{
std::lock_guard lock(deathRecipientMutex_);
auto iter = interactionOperationDeathRecipients_.find(windowId);
if (iter != interactionOperationDeathRecipients_.end()) {
interactionOperationDeathRecipients_.erase(iter);
}
}
void ElementOperatorManager::RemoveDeathRecipient(int32_t windowId, int32_t treeId)
{
std::lock_guard lock(deathRecipientMutex_);
auto iter = interactionOperationDeathMap_.find(windowId);
if (iter != interactionOperationDeathMap_.end()) {
auto iterTree = iter->second.find(treeId);
if (iterTree != iter->second.end()) {
iter->second.erase(iterTree);
}
}
}
void ElementOperatorManager::RemoveTreeDeathRecipient(int32_t windowId, int32_t treeId,
const sptr<AccessibilityWindowConnection>& connection)
{
if (connection == nullptr) {
HILOG_ERROR("connection is null");
return;
}
auto object = connection->GetCardProxy(treeId);
if (object == nullptr) {
HILOG_ERROR("GetCardProxy is null");
return;
}
auto remoteObject = object->AsObject();
if (remoteObject == nullptr) {
HILOG_ERROR("remoteObject is null");
return;
}
connection->EraseProxy(treeId);
std::lock_guard lock(deathRecipientMutex_);
auto iter = interactionOperationDeathMap_.find(windowId);
if (iter != interactionOperationDeathMap_.end()) {
auto iterTree = iter->second.find(treeId);
if (iterTree != iter->second.end()) {
sptr<IRemoteObject::DeathRecipient> deathRecipient = iterTree->second;
bool result = remoteObject->RemoveDeathRecipient(deathRecipient);
HILOG_DEBUG("The result of deleting operation's death recipient is %{public}d", result);
iter->second.erase(iterTree);
} else {
HILOG_ERROR("cannot find remote object. treeId[%{public}d]", treeId);
}
} else {
HILOG_ERROR("cannot find remote object. windowId[%{public}d]", windowId);
}
}
const std::map<int32_t, sptr<AccessibilityWindowConnection>>& ElementOperatorManager::GetAsacConnections() const
{
return asacConnections_;
}
int32_t ElementOperatorManager::GetTreeIdBySplitElementId(const int64_t elementId)
{
return static_cast<int32_t>(static_cast<uint64_t>(elementId) >> ELEMENT_MOVE_BIT);
}
int32_t ElementOperatorManager::GenerateRequestId()
{
std::lock_guard lock(requestMutex_);
int32_t requestId = requestId_.fetch_add(1, std::memory_order_relaxed);
if (requestId == REQUEST_ID_MAX) {
requestId_ = REQUEST_ID_MIN;
requestId = requestId_.fetch_add(1, std::memory_order_relaxed);
}
return requestId;
}
void ElementOperatorManager::AddRequestId(int32_t windowId, int32_t treeId, int32_t requestId,
sptr<IAccessibilityElementOperatorCallback> callback)
{
std::lock_guard lock(requestMutex_);
HILOG_DEBUG("Add windowId: %{public}d treeId: %{public}d requestId: %{public}d", windowId, treeId, requestId);
if (!windowRequestIdMap_.count(windowId)) {
windowRequestIdMap_[windowId] = {};
}
if (!windowRequestIdMap_[windowId].count(treeId)) {
windowRequestIdMap_[windowId][treeId] = {};
}
if (!windowRequestIdMap_[windowId][treeId].count(requestId)) {
windowRequestIdMap_[windowId][treeId].insert(requestId);
requestIdMap_[requestId] = callback;
}
}
ErrCode ElementOperatorManager::RemoveRequestId(int32_t requestId)
{
std::lock_guard lock(requestMutex_);
HILOG_DEBUG("RemoveRequestId requestId: %{public}d", requestId);
for (auto &window : windowRequestIdMap_) {
for (auto &tree : window.second) {
auto it = tree.second.find(requestId);
if (it != tree.second.end()) {
HILOG_DEBUG("tree.second.erase requestId:%{public}d", requestId);
tree.second.erase(it);
}
auto ite = requestIdMap_.find(requestId);
if (ite != requestIdMap_.end()) {
HILOG_DEBUG("requestIdMap_.erase requestId:%{public}d", requestId);
requestIdMap_.erase(ite);
}
}
}
return ERR_OK;
}
void ElementOperatorManager::StopCallbackWait(int32_t windowId)
{
HILOG_INFO("StopCallbackWait start windowId: %{public}d", windowId);
std::lock_guard lock(requestMutex_);
if (!windowRequestIdMap_.count(windowId)) {
HILOG_DEBUG("windowId not exists");
return;
}
for (auto iter = windowRequestIdMap_[windowId].begin(); iter != windowRequestIdMap_[windowId].end(); ++iter) {
HILOG_DEBUG("stop callback wait windowId: %{public}d, treeId: %{public}d", windowId, iter->first);
StopCallbackWait(windowId, iter->first);
}
}
void ElementOperatorManager::StopCallbackWait(int32_t windowId, int32_t treeId)
{
std::lock_guard lock(requestMutex_);
HILOG_DEBUG("StopCallbackWait start windowId: %{public}d treeId: %{public}d", windowId, treeId);
if (!windowRequestIdMap_.count(windowId)) {
return;
}
if (!windowRequestIdMap_[windowId].count(treeId)) {
return;
}
auto requestIds = windowRequestIdMap_[windowId][treeId];
for (auto requestId = requestIds.begin(); requestId != requestIds.end();) {
HILOG_DEBUG("stop callback wait windowId: %{public}d, requestId: %{public}d", windowId, *requestId);
auto iter = requestIdMap_.find(*requestId);
if (iter != requestIdMap_.end()) {
HILOG_DEBUG("requestIdMap_ set callback and erase requestId:%{public}d", *requestId);
sptr<IAccessibilityElementOperatorCallback> callback = requestIdMap_[*requestId];
if (callback != nullptr) {
callback->SetExecuteActionResult(false, *requestId);
}
requestIdMap_.erase(iter);
}
requestId = requestIds.erase(requestId);
}
}
RetError ElementOperatorManager::ClearFocus(int32_t windowId)
{
HILOG_DEBUG("windowId(%{public}d)", windowId);
sptr<AccessibilityWindowConnection> connection = GetAccessibilityWindowConnection(windowId);
if (!connection) {
HILOG_WARN("No connection found for windowId(%{public}d)", windowId);
return RET_ERR_NO_CONNECTION;
}
sptr<IAccessibilityElementOperator> elementOperator = connection->GetProxy();
if (!elementOperator) {
HILOG_WARN("No elementOperator found for windowId(%{public}d)", windowId);
return RET_ERR_NO_CONNECTION;
}
elementOperator->ClearFocus();
return RET_OK;
}
void ElementOperatorManager::ClearAllConnections()
{
std::lock_guard lock(asacConnectionsMutex_);
asacConnections_.clear();
std::lock_guard deathLock(deathRecipientMutex_);
interactionOperationDeathRecipients_.clear();
interactionOperationDeathMap_.clear();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetSearchElementInfoByAccessibilityIdResult(
const std::vector<AccessibilityElementInfo> &infos, const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
std::vector<AccessibilityElementInfo> validatedInfos;
for (const auto& info : infos) {
auto& service = Singleton<AccessibleAbilityManagerService>::GetInstance();
auto accountData = service.GetCurrentAccountData();
if (accountData) {
uint32_t tokenId = IPCSkeleton::GetCallingTokenID();
RetError ret = accountData->VerifyingToKenId(tokenId, info.GetWindowId(), info.GetAccessibilityId());
if (ret == RET_OK) {
validatedInfos.push_back(info);
HILOG_DEBUG("Element info validated, windowId: %{public}d, elementId: %{public}" PRId64 "",
info.GetWindowId(), info.GetAccessibilityId());
} else {
HILOG_WARN("Element info validation failed, windowId: %{public}d, elementId: %{public}" PRId64 "",
info.GetWindowId(), info.GetAccessibilityId());
}
}
}
elementInfosResult_ = validatedInfos;
auto& service = Singleton<AccessibleAbilityManagerService>::GetInstance();
auto accountData = service.GetCurrentAccountData();
if (accountData) {
accountData->GetOperatorManager().RemoveRequestId(requestId);
}
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetSearchDefaultFocusByWindowIdResult(
const std::vector<AccessibilityElementInfo> &infos, const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
elementInfosResult_ = infos;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetSearchElementInfoByTextResult(
const std::vector<AccessibilityElementInfo> &infos, const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
elementInfosResult_ = infos;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetFindFocusedElementInfoResult(
const AccessibilityElementInfo &info, const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
accessibilityInfoResult_ = info;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetFocusMoveSearchResult(
const AccessibilityElementInfo &info, const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
accessibilityInfoResult_ = info;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetExecuteActionResult(
const bool succeeded, const int32_t requestId)
{
HILOG_DEBUG("Response [result:%{public}d, requestId:%{public}d]", succeeded, requestId);
executeActionResult_ = succeeded;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetCursorPositionResult(
const int32_t cursorPosition, const int32_t requestId)
{
HILOG_DEBUG("cursorPosition [result:%{public}d, requestId:%{public}d]", cursorPosition, requestId);
callCursorPosition_ = cursorPosition;
promise_.set_value();
}
void ElementOperatorManager::ElementOperatorCallbackImpl::SetSearchElementInfoBySpecificPropertyResult(
const std::list<AccessibilityElementInfo> &infos, const std::list<AccessibilityElementInfo> &treeInfos,
const int32_t requestId)
{
HILOG_DEBUG("Response [requestId:%{public}d]", requestId);
if (!infos.empty()) {
elementInfosResult_.assign(infos.begin(), infos.end());
} else if (!treeInfos.empty()) {
elementInfosResult_.assign(treeInfos.begin(), treeInfos.end());
}
promise_.set_value();
}
void ElementOperatorManager::SetElementOperatorDeathRecipient(int32_t windowId, const sptr<IRemoteObject>& object,
int32_t accountId)
{
HILOG_DEBUG("windowId(%{public}d), accountId(%{public}d)", windowId, accountId);
if (!object) {
HILOG_ERROR("object is null");
return;
}
sptr<IRemoteObject::DeathRecipient> deathRecipient =
new(std::nothrow) ElementOperatorDeathRecipient(windowId, accountId);
if (!deathRecipient) {
Utils::RecordUnavailableEvent(A11yUnavailableEvent::CONNECT_EVENT,
A11yError::ERROR_CONNECT_TARGET_APPLICATION_FAILED);
HILOG_ERROR("Create ElementOperatorDeathRecipient failed");
return;
}
bool result = object->AddDeathRecipient(deathRecipient);
if (result) {
std::lock_guard lock(deathRecipientMutex_);
interactionOperationDeathRecipients_[windowId] = deathRecipient;
HILOG_DEBUG("Successfully added death recipient for windowId(%{public}d)", windowId);
} else {
HILOG_ERROR("Failed to add death recipient for windowId(%{public}d)", windowId);
}
}
void ElementOperatorManager::SetElementOperatorDeathRecipient(int32_t windowId, int32_t treeId,
const sptr<IRemoteObject>& object, int32_t accountId)
{
HILOG_DEBUG("windowId(%{public}d), treeId(%{public}d), accountId(%{public}d)", windowId, treeId, accountId);
if (!object) {
HILOG_ERROR("object is null");
return;
}
sptr<IRemoteObject::DeathRecipient> deathRecipient =
new(std::nothrow) ElementOperatorDeathRecipient(windowId, treeId, accountId);
if (!deathRecipient) {
HILOG_ERROR("Create ElementOperatorDeathRecipient failed");
return;
}
bool result = object->AddDeathRecipient(deathRecipient);
if (result) {
std::lock_guard lock(deathRecipientMutex_);
interactionOperationDeathMap_[windowId][treeId] = deathRecipient;
HILOG_DEBUG("Successfully added death recipient for windowId(%{public}d), treeId(%{public}d)", windowId,
treeId);
} else {
HILOG_ERROR("Failed to add death recipient for windowId(%{public}d), treeId(%{public}d)", windowId, treeId);
}
}
void ElementOperatorManager::RemoveElementOperatorDeathRecipient(int32_t windowId, const sptr<IRemoteObject>& object)
{
HILOG_DEBUG("windowId(%{public}d)", windowId);
if (!object) {
HILOG_ERROR("object is null");
return;
}
std::lock_guard lock(deathRecipientMutex_);
auto iter = interactionOperationDeathRecipients_.find(windowId);
if (iter != interactionOperationDeathRecipients_.end()) {
HILOG_DEBUG("delete death recipient, window id = %{public}d", windowId);
sptr<IRemoteObject::DeathRecipient> deathRecipient = iter->second;
bool result = object->RemoveDeathRecipient(deathRecipient);
HILOG_DEBUG("The result of deleting operation's death recipient is %{public}d", result);
interactionOperationDeathRecipients_.erase(iter);
} else {
HILOG_INFO("cannot find remote object. windowId[%{public}d]", windowId);
}
}
void ElementOperatorManager::ElementOperatorDeathRecipient::OnRemoteDied(const wptr<IRemoteObject> &remote)
{
HILOG_INFO("ElementOperatorDeathRecipient OnRemoteDied windowId[%{public}d], treeId[%{public}d],"
" accountId[%{public}d]", windowId_, treeId_, accountId_);
auto& service = Singleton<AccessibleAbilityManagerService>::GetInstance();
int32_t currentAccountId = service.GetCurrentAccountId();
if (currentAccountId != accountId_) {
HILOG_ERROR("check accountId failed");
return;
}
if (treeId_ > 0) {
service.DeregisterElementOperatorByWindowIdAndTreeId(windowId_, treeId_);
} else {
service.DeregisterElementOperatorByWindowId(windowId_);
}
}
}
}