• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }