• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 <set>
17 #include <string>
18 #include <unistd.h>
19 #include "nlohmann/json.hpp"
20 #include "common_utilities_hpp.h"
21 #include "ui_event_observer_ani.h"
22 #include "hilog/log.h"
23 using namespace OHOS::HiviewDFX;
24 
25 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LogType::LOG_CORE, 0xD003100, "UiTestKit"};
26 
27 namespace OHOS::uitest {
28     using namespace nlohmann;
29     using namespace std;
30 
31     /** Cached reference of UIEventObserver and JsCallbackFunction, key is the unique id.*/
32     static map<string, ani_ref> g_jsRefs;
33     static size_t g_incJsCbId = 0;
34 
Get()35     UiEventObserverAni &UiEventObserverAni::Get()
36     {
37         static UiEventObserverAni instance;
38         return instance;
39     }
40 
PreprocessCallOnce(ani_env * env,ApiCallInfo & call,ani_object & observerObj,ani_object & callbackObj,ApiReplyInfo & out)41     void UiEventObserverAni::PreprocessCallOnce(ani_env *env, ApiCallInfo &call, ani_object &observerObj,
42                                                 ani_object &callbackObj, ApiReplyInfo &out)
43     {
44         DCHECK(env != nullptr);
45         auto &paramList = call.paramList_;
46         // pop js_cb_function and save at local side
47         if (paramList.size() < 1 || paramList.at(0).type() != detail::value_t::string) {
48             HiLog::Error(LABEL,"Missing event type argument");
49             out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Missing event type argument");
50             return;
51         }
52         auto event = paramList.at(0).get<string>();
53         if (g_jsRefs.find(call.callerObjRef_) == g_jsRefs.end()) {
54             // hold the jsObserver to avoid it be recycled, it's needed in performing callback
55             ani_ref observerRef;
56             if (env->GlobalReference_Create(static_cast<ani_ref>(observerObj), &observerRef) != ANI_OK) {
57                 HiLog::Error(LABEL,"Create observerRef fail");
58                 out.exception_ = ApiCallErr(ERR_INTERNAL, "Create observerRef fail");
59                 return;
60             }
61             HiLog::Debug(LABEL,"Hold reference of %{public}s, ref: %{public}p", call.callerObjRef_.c_str(), observerRef);
62             g_jsRefs.insert({ call.callerObjRef_, observerRef });
63         }
64         const auto jsCbId = string("js_callback#") + to_string(++g_incJsCbId);
65         // hold the const  to avoid it be recycled, it's needed in performing callback
66         ani_ref callbackRef;
67         if (env->GlobalReference_Create(static_cast<ani_ref>(callbackObj), &callbackRef) != ANI_OK) {
68             HiLog::Error(LABEL,"Create callbackRef fail");
69             out.exception_ = ApiCallErr(ERR_INTERNAL, "Create callbackRef fail");
70             return;
71         }
72         HiLog::Info(LABEL,"CbId = %{public}s, CbRef = %{public}p", jsCbId.c_str(), callbackRef);
73         g_jsRefs.insert({ jsCbId, callbackRef });
74         // pass jsCbId instread of the function body
75         call.paramList_.push_back(jsCbId); // observer.once(type, cllbackId)
76     }
77 
78     struct EventCallbackContext {
79         ani_env *env;
80         string observerId;
81         string callbackId;
82         ani_ref observerRef;
83         ani_ref callbackRef;
84         bool releaseObserver; // if or not release observer object after performing this callback
85         bool releaseCallback; // if or not release callback function after performing this callback
86         nlohmann::json elmentInfo;
87     };
88 
InitCallbackContext(ani_env * env,const ApiCallInfo & in,ApiReplyInfo & out,EventCallbackContext & ctx)89     static void InitCallbackContext(ani_env *env, const ApiCallInfo &in, ApiReplyInfo &out, EventCallbackContext &ctx)
90     {
91         HiLog::Info(LABEL,"Handler api callback: %{public}s", in.apiId_.c_str());
92         if (in.apiId_ != "UIEventObserver.once") {
93             out.exception_ = ApiCallErr(ERR_INTERNAL, "Api dose not support callback: " + in.apiId_);
94             HiLog::Error(LABEL,"%{public}s", out.exception_.message_.c_str());
95             return;
96         }
97         DCHECK(env != nullptr);
98         DCHECK(in.paramList_.size() > INDEX_ZERO && in.paramList_.at(INDEX_ZERO).type() == detail::value_t::object);
99         DCHECK(in.paramList_.size() > INDEX_ONE && in.paramList_.at(INDEX_ONE).type() == detail::value_t::string);
100         DCHECK(in.paramList_.size() > INDEX_TWO && in.paramList_.at(INDEX_TWO).type() == detail::value_t::boolean);
101         DCHECK(in.paramList_.size() > INDEX_THREE && in.paramList_.at(INDEX_THREE).type() == detail::value_t::boolean);
102         auto &observerId = in.callerObjRef_;
103         auto &elementInfo = in.paramList_.at(INDEX_ZERO);
104         auto callbackId = in.paramList_.at(INDEX_ONE).get<string>();
105         HiLog::Info(LABEL,"Begin to callback UiEvent: observer=%{public}s, callback=%{public}s",
106               observerId.c_str(), callbackId.c_str());
107         auto findObserver = g_jsRefs.find(observerId);
108         auto findCallback = g_jsRefs.find(callbackId);
109         if (findObserver == g_jsRefs.end()) {
110             out.exception_ = ApiCallErr(INTERNAL_ERROR, "UIEventObserver is not referenced: " + observerId);
111             HiLog::Error(LABEL,"%{public}s", out.exception_.message_.c_str());
112             return;
113         }
114         if (findCallback == g_jsRefs.end()) {
115             out.exception_ = ApiCallErr(INTERNAL_ERROR, "JsCallbackFunction is not referenced: " + callbackId);
116             HiLog::Error(LABEL,"%{public}s", out.exception_.message_.c_str());
117             return;
118         }
119         ctx.env = env;
120         ctx.observerId = observerId;
121         ctx.callbackId = callbackId;
122         ctx.observerRef = findObserver->second;
123         ctx.callbackRef = findCallback->second;
124         ctx.elmentInfo = move(elementInfo);
125         ctx.releaseObserver = in.paramList_.at(INDEX_TWO).get<bool>();
126         ctx.releaseCallback = in.paramList_.at(INDEX_THREE).get<bool>();
127     }
128 
129     /**Handle api callback from server end.*/
HandleEventCallback(ani_vm * vm,const ApiCallInfo & in,ApiReplyInfo & out)130     void UiEventObserverAni::HandleEventCallback(ani_vm *vm, const ApiCallInfo &in, ApiReplyInfo &out)
131     {
132         ani_env *env = nullptr;
133         ani_options aniArgs {0, nullptr};
134         auto re = vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env);
135         if (re != ANI_OK) {
136             HiLog::Error(LABEL,"AttachCurrentThread fail, result: %{public}d", re);
137             out.exception_ = ApiCallErr(ERR_INTERNAL, "AttachCurrentThread fail");
138             return;
139         }
140         auto context = new EventCallbackContext();
141         InitCallbackContext(env, in, out, *context);
142         if (out.exception_.code_ != NO_ERROR) {
143             delete context;
144             HiLog::Warn(LABEL,"InitCallbackContext failed, cannot perform callback");
145             vm->DetachCurrentThread();
146             return;
147         }
148         auto bundleName = context->elmentInfo["bundleName"].get<string>();
149         auto cType = context->elmentInfo["type"].get<string>();
150         auto text = context->elmentInfo["text"].get<string>();
151         ani_string strBundleName;
152         ani_string strType;
153         ani_string strText;
154         auto re1 = env->String_NewUTF8(bundleName.c_str(), bundleName.size(), &strBundleName);
155         auto re2 = env->String_NewUTF8(cType.c_str(), cType.size(), &strType);
156         auto re3 = env->String_NewUTF8(text.c_str(), text.size(), &strText);
157         if (re1 != ANI_OK || re2 != ANI_OK || re3 != ANI_OK) {
158             HiLog::Error(LABEL,"Analysis uielementInfo fail");
159             out.exception_ = ApiCallErr(ERR_INTERNAL, "Analysis uielementInfo fail");
160             vm->DetachCurrentThread();
161             return;
162         }
163         static const char *className = "L@ohos/UiTest/UIElementInfoInner;";
164         ani_class cls;
165         if (ANI_OK != env->FindClass(className, &cls)) {
166             HiLog::Error(LABEL,"Not found class UIElementInfoInner");
167             out.exception_ = ApiCallErr(ERR_INTERNAL, "FindClass UIElementInfoInner fail");
168             vm->DetachCurrentThread();
169             return;
170         }
171         ani_method method;
172         if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", ":V", &method)) {
173             HiLog::Error(LABEL,"Not found method of UIElementInfoInner");
174             vm->DetachCurrentThread();
175             return;
176         }
177         ani_object obj;
178         if (ANI_OK != env->Object_New(cls, method, &obj)) {
179             HiLog::Error(LABEL,"Object_New UIElementInfoInner fail");
180             vm->DetachCurrentThread();
181             return;
182         }
183         if (ANI_OK != env->Object_SetPropertyByName_Ref(obj, "bundleName", reinterpret_cast<ani_ref>(strBundleName))) {
184             HiLog::Error(LABEL,"SetProperty  bundleName fail");
185             vm->DetachCurrentThread();
186             return;
187         }
188         if (ANI_OK != env->Object_SetPropertyByName_Ref(obj, "type", reinterpret_cast<ani_ref>(strType))) {
189             HiLog::Error(LABEL,"SetProperty  type fail");
190             vm->DetachCurrentThread();
191             return;
192         }
193         if (ANI_OK != env->Object_SetPropertyByName_Ref(obj, "text", reinterpret_cast<ani_ref>(strText))) {
194             HiLog::Error(LABEL,"SetProperty  bundleName fail");
195             vm->DetachCurrentThread();
196             return;
197         }
198         ani_ref argvRef = static_cast<ani_ref>(obj);
199         std::vector<ani_ref> tmp = {argvRef};
200         ani_ref result;
201         if (ANI_OK != env->FunctionalObject_Call(reinterpret_cast<ani_fn_object>(context->callbackRef), tmp.size(),
202             tmp.data(), &result)) {
203             HiLog::Error(LABEL,"HandleEventCallback fail");
204             vm->DetachCurrentThread();
205             return;
206         }
207         if (context->releaseObserver) {
208             HiLog::Debug(LABEL,"Unref jsObserver: %{public}s", context->observerId.c_str());
209             env->GlobalReference_Delete(context->observerRef);
210             g_jsRefs.erase(context->observerId);
211         }
212         if (context->releaseCallback) {
213             HiLog::Debug(LABEL,"Unref jsCallback: %{public}s", context->callbackId.c_str());
214             env->GlobalReference_Delete(context->callbackRef);
215             g_jsRefs.erase(context->callbackId);
216         }
217         delete context;
218         vm->DetachCurrentThread();
219     }
220 }
221