1 /*
2 * Copyright (C) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <array>
17 #include <iostream>
18 #include "ani_accessibility_system_ability_client.h"
19 #include "ani_accessibility_common.h"
20 #include "accessibility_utils_ani.h"
21 #include "hilog_wrapper.h"
22
23 using namespace OHOS::Accessibility;
24 using namespace OHOS::AccessibilityAni;
25
26 constexpr int32_t ANI_SCOPE_SIZE = 16;
27
28 std::shared_ptr<StateListenerImpl> ANIAccessibilityClient::accessibilityStateListeners_ =
29 std::make_shared<StateListenerImpl>(AccessibilityStateEventType::EVENT_ACCESSIBILITY_STATE_CHANGED);
30 std::shared_ptr<StateListenerImpl> ANIAccessibilityClient::touchGuideStateListeners_ =
31 std::make_shared<StateListenerImpl>(AccessibilityStateEventType::EVENT_TOUCH_GUIDE_STATE_CHANGED);
32 std::shared_ptr<StateListenerImpl> ANIAccessibilityClient::screenReaderStateListeners_ =
33 std::make_shared<StateListenerImpl>(AccessibilityStateEventType::EVENT_SCREEN_READER_STATE_CHANGED);
34 std::shared_ptr<StateListenerImpl> ANIAccessibilityClient::touchModeListeners_ =
35 std::make_shared<StateListenerImpl>(AccessibilityStateEventType::EVENT_TOUCH_MODE_CHANGED);
36
SubscribeToFramework()37 void StateListenerImpl::SubscribeToFramework()
38 {
39 HILOG_INFO("SubscribeFromFramework");
40 auto asaClient = AccessibilitySystemAbilityClient::GetInstance();
41 if (asaClient) {
42 asaClient->SubscribeStateObserver(shared_from_this(), type_);
43 }
44 }
45
UnsubscribeFromFramework()46 void StateListenerImpl::UnsubscribeFromFramework()
47 {
48 HILOG_INFO("UnsubscribeFromFramework");
49 auto asaClient = AccessibilitySystemAbilityClient::GetInstance();
50 if (asaClient) {
51 asaClient->UnsubscribeStateObserver(shared_from_this(), type_);
52 }
53 }
54
OnStateChanged(const bool state)55 void StateListenerImpl::OnStateChanged(const bool state)
56 {
57 HILOG_INFO("state is %{public}d", state);
58 std::string touchMode = "";
59 if (type_ == AccessibilityStateEventType::EVENT_TOUCH_MODE_CHANGED) {
60 for (auto &observer : observers_) {
61 touchMode = state ? "singleTouchMode" : "doubleTouchMode";
62 observer->OnStateChanged(touchMode);
63 }
64 return;
65 }
66
67 for (auto &observer : observers_) {
68 if (observer->isBoolObserver_) {
69 observer->OnStateChanged(state);
70 } else if (!state) {
71 // notify the touch mode change
72 touchMode = "none";
73 observer->OnStateChanged(touchMode);
74 }
75 }
76 }
77
SubscribeObserver(ani_env * env,ani_object observer,bool isBoolObserver)78 void StateListenerImpl::SubscribeObserver(ani_env *env, ani_object observer, bool isBoolObserver)
79 {
80 std::lock_guard<ffrt::mutex> lock(mutex_);
81 ani_ref fnRef;
82 env->GlobalReference_Create(observer, &fnRef);
83 for (auto iter = observers_.begin(); iter != observers_.end();) {
84 if (ANICommon::CheckObserverEqual(env, fnRef, (*iter)->env_, (*iter)->fnRef_)) {
85 HILOG_WARN("SubscribeObserver Observer exist");
86 return;
87 } else {
88 iter++;
89 }
90 }
91
92 std::shared_ptr<StateListener> stateListener = std::make_shared<StateListener>(env, fnRef, isBoolObserver);
93 observers_.emplace_back(stateListener);
94 HILOG_INFO("observer size%{public}zu", observers_.size());
95 }
96
UnsubscribeObserver(ani_env * env,ani_object observer)97 void StateListenerImpl::UnsubscribeObserver(ani_env *env, ani_object observer)
98 {
99 std::lock_guard<ffrt::mutex> lock(mutex_);
100 ani_ref fnRef;
101 env->GlobalReference_Create(observer, &fnRef);
102 for (auto iter = observers_.begin(); iter != observers_.end();) {
103 if (ANICommon::CheckObserverEqual(env, fnRef, (*iter)->env_, (*iter)->fnRef_)) {
104 HILOG_INFO("UnsubscribeObserver Observer exist");
105 env->GlobalReference_Delete(fnRef);
106 observers_.erase(iter);
107 return;
108 } else {
109 iter++;
110 }
111 }
112 env->GlobalReference_Delete(fnRef);
113 HILOG_WARN("UnsubscribeObserver Observer not exist");
114 }
115
UnsubscribeObservers()116 void StateListenerImpl::UnsubscribeObservers()
117 {
118 HILOG_INFO();
119 std::lock_guard<ffrt::mutex> lock(mutex_);
120 for (auto iter = observers_.begin(); iter != observers_.end(); iter++) {
121 (*iter)->env_->GlobalReference_Delete((*iter)->fnRef_);
122 }
123 observers_.clear();
124 }
125
NotifyETS(ani_env * env,bool state,ani_ref fnRef)126 void StateListener::NotifyETS(ani_env *env, bool state, ani_ref fnRef)
127 {
128 HILOG_INFO("state = [%{public}s]", state ? "true" : "false");
129
130 std::shared_ptr<ANIStateCallbackInfo> callbackInfo = std::make_shared<ANIStateCallbackInfo>();
131 if (callbackInfo == nullptr) {
132 HILOG_ERROR("Failed to create callbackInfo");
133 return;
134 }
135 callbackInfo->state_ = state;
136 callbackInfo->env_ = env;
137 callbackInfo->fnRef_ = fnRef;
138 auto task = [callbackInfo]() {
139 HILOG_INFO("notify state changed to ets");
140 ani_env *tmpEnv = callbackInfo->env_;
141 ani_size nr_refs = ANI_SCOPE_SIZE;
142 tmpEnv->CreateLocalScope(nr_refs);
143 auto fnObj = reinterpret_cast<ani_fn_object>(callbackInfo->fnRef_);
144 ani_object state = ANICommon::CreateBoolObject(tmpEnv, static_cast<ani_boolean>(callbackInfo->state_));
145 if (state == nullptr) {
146 HILOG_ERROR("create boolean object failed");
147 return;
148 }
149 std::vector<ani_ref> args = {reinterpret_cast<ani_ref>(state)};
150 ani_ref result;
151 tmpEnv->FunctionalObject_Call(fnObj, 1, args.data(), &result);
152 tmpEnv->DestroyLocalScope();
153 };
154 if (!ANICommon::SendEventToMainThread(task)) {
155 HILOG_ERROR("failed to send event");
156 }
157 }
158
NotifyETS(ani_env * env,std::string mode,ani_ref fnRef)159 void StateListener::NotifyETS(ani_env *env, std::string mode, ani_ref fnRef)
160 {
161 HILOG_INFO("mode = [%{public}s]", mode.c_str());
162
163 std::shared_ptr<ANIStateCallbackInfo> callbackInfo = std::make_shared<ANIStateCallbackInfo>();
164 if (callbackInfo == nullptr) {
165 HILOG_ERROR("Failed to create callbackInfo");
166 return;
167 }
168 callbackInfo->mode_ = mode;
169 callbackInfo->env_ = env;
170 callbackInfo->fnRef_ = fnRef;
171 auto task = [callbackInfo]() {
172 HILOG_INFO("notify mode changed to ets");
173 ani_env *tmpEnv = callbackInfo->env_;
174 ani_size nr_refs = ANI_SCOPE_SIZE;
175 tmpEnv->CreateLocalScope(nr_refs);
176 auto fnObj = reinterpret_cast<ani_fn_object>(callbackInfo->fnRef_);
177 ani_string modeStr;
178 tmpEnv->String_NewUTF8(callbackInfo->mode_.c_str(), callbackInfo->mode_.length(), &modeStr);
179 std::vector<ani_ref> args = {reinterpret_cast<ani_ref>(modeStr)};
180 ani_ref result;
181 tmpEnv->FunctionalObject_Call(fnObj, 1, args.data(), &result);
182 tmpEnv->DestroyLocalScope();
183 };
184 if (!ANICommon::SendEventToMainThread(task)) {
185 HILOG_ERROR("failed to send event");
186 }
187 }
188
OnStateChanged(const bool state)189 void StateListener::OnStateChanged(const bool state)
190 {
191 NotifyETS(env_, state, fnRef_);
192 }
193
OnStateChanged(const std::string mode)194 void StateListener::OnStateChanged(const std::string mode)
195 {
196 NotifyETS(env_, mode, fnRef_);
197 }
198
SubscribeState(ani_env * env,ani_string type,ani_object callback)199 void ANIAccessibilityClient::SubscribeState(ani_env *env, ani_string type, ani_object callback)
200 {
201 std::string eventType = ANICommon::ANIStringToStdString(env, type);
202 if (std::strcmp(eventType.c_str(), "accessibilityStateChange") == 0) {
203 accessibilityStateListeners_->SubscribeObserver(env, callback);
204 } else if (std::strcmp(eventType.c_str(), "touchGuideStateChange") == 0) {
205 touchGuideStateListeners_->SubscribeObserver(env, callback);
206 } else if (std::strcmp(eventType.c_str(), "screenReaderStateChange") == 0) {
207 screenReaderStateListeners_->SubscribeObserver(env, callback);
208 } else if (std::strcmp(eventType.c_str(), "touchModeChange") == 0) {
209 touchModeListeners_->SubscribeObserver(env, callback);
210 touchGuideStateListeners_->SubscribeObserver(env, callback, false);
211 } else {
212 HILOG_ERROR("SubscribeState eventType[%{public}s] is error", eventType.c_str());
213 ThrowBusinessError(env, QueryRetMsg(RET_ERR_INVALID_PARAM));
214 }
215 }
216
UnsubscribeState(ani_env * env,ani_string type,ani_object callback)217 void ANIAccessibilityClient::UnsubscribeState(ani_env *env, ani_string type, ani_object callback)
218 {
219 std::string eventType = ANICommon::ANIStringToStdString(env, type);
220 if (std::strcmp(eventType.c_str(), "accessibilityStateChange") == 0) {
221 accessibilityStateListeners_->UnsubscribeObserver(env, callback);
222 } else if (std::strcmp(eventType.c_str(), "touchGuideStateChange") == 0) {
223 touchGuideStateListeners_->UnsubscribeObserver(env, callback);
224 } else if (std::strcmp(eventType.c_str(), "screenReaderStateChange") == 0) {
225 screenReaderStateListeners_->UnsubscribeObserver(env, callback);
226 } else if (std::strcmp(eventType.c_str(), "touchModeChange") == 0) {
227 touchModeListeners_->UnsubscribeObserver(env, callback);
228 touchGuideStateListeners_->UnsubscribeObserver(env, callback);
229 } else {
230 HILOG_ERROR("UnsubscribeState eventType[%{public}s] is error", eventType.c_str());
231 ThrowBusinessError(env, QueryRetMsg(RET_ERR_INVALID_PARAM));
232 }
233 }
234
UnsubscribeStateAll(ani_env * env,ani_string type)235 void ANIAccessibilityClient::UnsubscribeStateAll(ani_env *env, ani_string type)
236 {
237 std::string eventType = ANICommon::ANIStringToStdString(env, type);
238 if (std::strcmp(eventType.c_str(), "accessibilityStateChange") == 0) {
239 accessibilityStateListeners_->UnsubscribeObservers();
240 } else if (std::strcmp(eventType.c_str(), "touchGuideStateChange") == 0) {
241 touchGuideStateListeners_->UnsubscribeObservers();
242 } else if (std::strcmp(eventType.c_str(), "screenReaderStateChange") == 0) {
243 screenReaderStateListeners_->UnsubscribeObservers();
244 } else if (std::strcmp(eventType.c_str(), "touchModeChange") == 0) {
245 touchModeListeners_->UnsubscribeObservers();
246 touchGuideStateListeners_->UnsubscribeObservers();
247 } else {
248 HILOG_ERROR("UnsubscribeStateAll eventType[%{public}s] is error", eventType.c_str());
249 ThrowBusinessError(env, QueryRetMsg(RET_ERR_INVALID_PARAM));
250 }
251 }
252
IsOpenTouchGuideSync(ani_env * env)253 ani_boolean ANIAccessibilityClient::IsOpenTouchGuideSync([[maybe_unused]] ani_env *env)
254 {
255 auto asaClient = AccessibilitySystemAbilityClient::GetInstance();
256 if (asaClient == nullptr) {
257 HILOG_ERROR("asaClient is nullptr!");
258 ThrowBusinessError(env, QueryRetMsg(RET_ERR_NULLPTR));
259 return false;
260 }
261 bool status = false;
262 auto ret = asaClient->IsTouchExplorationEnabled(status);
263 if (ret != RET_OK) {
264 HILOG_ERROR("get touch guide state failed!");
265 ThrowBusinessError(env, QueryRetMsg(RET_ERR_FAILED));
266 return false;
267 }
268
269 return status;
270 }
271
IsOpenAccessibilitySync(ani_env * env)272 ani_boolean ANIAccessibilityClient::IsOpenAccessibilitySync([[maybe_unused]] ani_env *env)
273 {
274 auto asaClient = AccessibilitySystemAbilityClient::GetInstance();
275 if (asaClient == nullptr) {
276 HILOG_ERROR("asaClient is nullptr!");
277 ThrowBusinessError(env, QueryRetMsg(RET_ERR_NULLPTR));
278 return false;
279 }
280 bool status = false;
281 auto ret = asaClient->IsEnabled(status);
282 if (ret != RET_OK) {
283 HILOG_ERROR("get accessibility state failed!");
284 ThrowBusinessError(env, QueryRetMsg(RET_ERR_FAILED));
285 return false;
286 }
287
288 return status;
289 }
290
SendAccessibilityEvent(ani_env * env,ani_object eventObject)291 void ANIAccessibilityClient::SendAccessibilityEvent(ani_env *env, ani_object eventObject)
292 {
293 AccessibilityEventInfo eventInfo {};
294
295 auto asaClient = AccessibilitySystemAbilityClient::GetInstance();
296 if (asaClient == nullptr) {
297 HILOG_ERROR("asaClient is nullptr!");
298 ThrowBusinessError(env, QueryRetMsg(RET_ERR_INVALID_PARAM));
299 return;
300 }
301
302 bool ret = ANICommon::ConvertEventInfoMandatoryFields(env, eventObject, eventInfo);
303 if (!ret) {
304 HILOG_ERROR("ConvertEventInfoMandatoryFields failed");
305 ThrowBusinessError(env, QueryRetMsg(RET_ERR_INVALID_PARAM));
306 return;
307 }
308
309 ANICommon::ConvertEventInfoStringFields(env, eventObject, eventInfo);
310 ANICommon::ConvertEventInfoIntFields(env, eventObject, eventInfo);
311
312 auto result = asaClient->SendEvent(eventInfo);
313 if (result != RET_OK) {
314 HILOG_ERROR("SendAccessibilityEvent failed!");
315 ThrowBusinessError(env, QueryRetMsg(result));
316 }
317 return;
318 }