1 /*
2 * Copyright (c) 2024 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 "js_rss_session.h"
17
18 #include <js_runtime_utils.h>
19
20 #include "js_scene_utils.h"
21 #ifdef RESOURCE_SCHEDULE_SERVICE_ENABLE
22 #include "res_sched_client.h"
23 #include "res_type.h"
24 #endif
25 #include "window_manager_hilog.h"
26
27 namespace OHOS::Rosen {
28 using namespace AbilityRuntime;
29 using namespace ResourceSchedule;
30 namespace {
31 constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, HILOG_DOMAIN_WINDOW, "JsRssSession" };
32 constexpr size_t ARG_COUNT_ONE = 1;
33 constexpr size_t ARG_COUNT_TWO = 2;
34 constexpr int32_t INDENT = -1;
35 } // namespace
36
37 struct CallBackContext {
38 napi_env env = nullptr;
39 std::shared_ptr<NativeReference> callbackRef = nullptr;
40 OnRssEventCb eventCb = nullptr;
41 uint32_t eventType = 0;
42 std::unordered_map<std::string, std::string> extraInfo;
43 };
44
45 #ifdef RESOURCE_SCHEDULE_SERVICE_ENABLE
RssEventListener(napi_env env,napi_value callbackObj,OnRssEventCb callback)46 RssEventListener::RssEventListener(napi_env env, napi_value callbackObj, OnRssEventCb callback)
47 : napiEnv_(env), eventCb_(std::move(callback))
48 {
49 napi_ref objRef = nullptr;
50 napi_create_reference(napiEnv_, callbackObj, 1, &objRef);
51 callbackRef_.reset(reinterpret_cast<NativeReference*>(objRef));
52 napi_value callbackWorkName = nullptr;
53 napi_create_string_utf8(env, "ThreadSafeFunction in SystemloadListener", NAPI_AUTO_LENGTH, &callbackWorkName);
54 napi_create_threadsafe_function(env, nullptr, nullptr, callbackWorkName, 0, 1, nullptr, nullptr, nullptr,
55 ThreadSafeCallBack, &threadSafeFunction_);
56 }
57
ThreadSafeCallBack(napi_env ThreadSafeEnv,napi_value js_cb,void * context,void * data)58 void RssEventListener::ThreadSafeCallBack(napi_env ThreadSafeEnv, napi_value js_cb, void* context, void* data)
59 {
60 WLOGFI("start");
61 CallBackContext* callBackContext = reinterpret_cast<CallBackContext*>(data);
62 callBackContext->eventCb(callBackContext->env,
63 callBackContext->callbackRef->GetNapiValue(), callBackContext->eventType,
64 std::move(callBackContext->extraInfo));
65 delete callBackContext;
66 }
67
OnReceiveEvent(uint32_t eventType,uint32_t eventValue,std::unordered_map<std::string,std::string> extraInfo)68 void RssEventListener::OnReceiveEvent(uint32_t eventType, uint32_t eventValue,
69 std::unordered_map<std::string, std::string> extraInfo)
70 {
71 CallBackContext* callBackContext = new CallBackContext();
72 callBackContext->env = napiEnv_;
73 callBackContext->callbackRef = callbackRef_;
74 callBackContext->eventType = eventType;
75 callBackContext->extraInfo = std::move(extraInfo);
76 callBackContext->eventCb = eventCb_;
77 napi_acquire_threadsafe_function(threadSafeFunction_);
78 napi_call_threadsafe_function(threadSafeFunction_, callBackContext, napi_tsfn_blocking);
79 }
80
GetInstance()81 RssSession& RssSession::GetInstance()
82 {
83 static RssSession session;
84 return session;
85 }
86
RegisterRssData(napi_env env,napi_callback_info info)87 napi_value RssSession::RegisterRssData(napi_env env, napi_callback_info info)
88 {
89 return GetInstance().RegisterRssDataCallback(env, info);
90 }
91
UnregisterRssData(napi_env env,napi_callback_info info)92 napi_value RssSession::UnregisterRssData(napi_env env, napi_callback_info info)
93 {
94 return GetInstance().UnRegisterRssDataCallback(env, info);
95 }
96
97 template<typename T>
SetMapValue(napi_env env,const std::string & key,const T & value,napi_value & object)98 void SetMapValue(napi_env env, const std::string& key, const T& value, napi_value& object)
99 {
100 napi_value keyInfo = nullptr;
101 napi_create_string_utf8(env, key.c_str(), NAPI_AUTO_LENGTH, &keyInfo);
102 napi_value valueInfo = nullptr;
103 if constexpr (std::is_same_v<T, bool>) {
104 napi_get_boolean(env, value, &valueInfo);
105 } else if constexpr (std::is_same_v<T, std::string>) {
106 napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &valueInfo);
107 }
108 napi_set_property(env, object, keyInfo, valueInfo);
109 }
110
DealRssReply(napi_env env,const nlohmann::json & payload,const nlohmann::json & reply)111 napi_value RssSession::DealRssReply(napi_env env, const nlohmann::json& payload, const nlohmann::json& reply)
112 {
113 WLOGFI("[NAPI]");
114 napi_value objValue = nullptr;
115 napi_create_object(env, &objValue);
116 if (objValue == nullptr) {
117 WLOGFE("[NAPI]Object is null!");
118 return NapiGetUndefined(env);
119 }
120
121 if (!reply.contains("result") || !reply["result"].is_string() || reply.at("result") == "") {
122 WLOGFE("[NAPI]Reply not find result key!");
123 return NapiGetUndefined(env);
124 }
125 std::string result = reply["result"].get<std::string>();
126 bool resultVal = result == std::to_string(ResType::HeavyLoadMutexAddReasons::HeavyLoadMutexStatusAddFailByMutex);
127 SetMapValue(env, "result", resultVal, objValue);
128
129 std::string detail = "{}";
130 if (!reply.contains("mutex") || !reply["mutex"].is_string() || reply.at("mutex") == "") {
131 SetMapValue(env, "details", detail, objValue);
132 } else {
133 ParseMutex(reply["mutex"].get<std::string>(), payload, detail);
134 SetMapValue(env, "details", detail, objValue);
135 }
136 return objValue;
137 }
138
ParseMutex(const std::string & mutexStr,const nlohmann::json & payload,std::string & detailStr)139 void RssSession::ParseMutex(const std::string& mutexStr, const nlohmann::json& payload, std::string& detailStr)
140 {
141 nlohmann::json root = nlohmann::json::parse(mutexStr, nullptr, false);
142 if (root.is_discarded()) {
143 WLOGFE("[NAPI] Parse json data failed!");
144 return;
145 }
146 if (!root.is_array()) {
147 WLOGFE("[NAPI]Parse json data failed!");
148 return;
149 }
150
151 std::string bundleName;
152 if (payload.contains("bundleName") && payload["bundleName"].is_string()) {
153 bundleName = payload["bundleName"].get<std::string>();
154 }
155
156 nlohmann::json detail;
157 detail["appInfo"]["domain"] = "";
158 detail["appInfo"]["bundleName"] = std::move(bundleName);
159 detail["reason"] = nlohmann::json::array();
160 for (auto& item : root) {
161 nlohmann::json tmp;
162 if (item.contains("domain") && item["domain"].is_number()) {
163 tmp["domain"] = std::to_string(item["domain"].get<int32_t>());
164 } else {
165 tmp["domain"] = "";
166 }
167
168 if (item.contains("bundlename") && item["bundlename"].is_string()) {
169 tmp["bundleName"] = item["bundlename"].get<std::string>();
170 } else {
171 tmp["bundleName"] = "";
172 }
173 detail["reason"].push_back(std::move(tmp));
174 }
175 detailStr = detail.dump(INDENT, ' ', false, nlohmann::json::error_handler_t::replace);
176 }
177
ParseCallbackMutex(const std::string & mutexStr,std::string & bundleName)178 void RssSession::ParseCallbackMutex(const std::string& mutexStr, std::string& bundleName)
179 {
180 nlohmann::json root = nlohmann::json::parse(mutexStr, nullptr, false);
181 if (root.is_discarded()) {
182 WLOGFE("[NAPI] Parse json data failed!");
183 return;
184 }
185 if (!root.is_array()) {
186 WLOGFE("[NAPI]Parse json data failed!");
187 return;
188 }
189
190 for (auto& item : root) {
191 if (item.contains("bundlename") && item["bundlename"].is_string()) {
192 bundleName = item["bundlename"].get<std::string>();
193 break;
194 }
195 }
196 }
197
OnReceiveEvent(napi_env env,napi_value callbackObj,uint32_t eventType,const std::unordered_map<std::string,std::string> & extraInfo)198 void RssSession::OnReceiveEvent(napi_env env, napi_value callbackObj, uint32_t eventType,
199 const std::unordered_map<std::string, std::string>& extraInfo)
200 {
201 WLOGFI("asyncCallback.");
202 if (jsCallBackMap_.find(eventType) == jsCallBackMap_.end()) {
203 WLOGFE("cb type has not register yet.");
204 return;
205 }
206 bool isEqual = false;
207 auto& callbackList = jsCallBackMap_[eventType];
208 auto iter = callbackList.begin();
209 for (; iter != callbackList.end(); iter++) {
210 NAPI_CALL_RETURN_VOID(env, napi_strict_equals(env, callbackObj, iter->first->GetNapiValue(), &isEqual));
211 if (isEqual) {
212 break;
213 }
214 }
215 if (!isEqual) {
216 WLOGFE("level callback not found in registered array.");
217 return;
218 }
219 std::unique_ptr<RssSessionCbInfo> cbInfo = std::make_unique<RssSessionCbInfo>(env);
220 cbInfo->extraInfo_ = extraInfo;
221 napi_value resourceName = nullptr;
222 NAPI_CALL_RETURN_VOID(env,
223 napi_create_string_latin1(env, "OnReceiveEvent", NAPI_AUTO_LENGTH, &resourceName));
224 NAPI_CALL_RETURN_VOID(env,
225 napi_create_reference(env, iter->first->GetNapiValue(), 1, &cbInfo->callback_));
226
227 NAPI_CALL_RETURN_VOID(env, napi_create_async_work(env, nullptr, resourceName,
228 [](napi_env env, void* data) {},
229 CompleteCb,
230 static_cast<void*>(cbInfo.get()),
231 &cbInfo->asyncWork_));
232 NAPI_CALL_RETURN_VOID(env, napi_queue_async_work(env, cbInfo->asyncWork_));
233 cbInfo.release();
234 WLOGFI("asyncCallback end");
235 }
236
RegisterRssDataCallback(napi_env env,napi_callback_info info)237 napi_value RssSession::RegisterRssDataCallback(napi_env env, napi_callback_info info)
238 {
239 WLOGFD("start");
240 uint32_t eventType;
241 napi_value jsCallback = nullptr;
242 if (!CheckCallbackParam(env, info, eventType, &jsCallback)) {
243 WLOGFE("Register RssData Callback parameter error.");
244 return NapiGetUndefined(env);
245 }
246 napi_ref objRef = nullptr;
247 napi_create_reference(env, jsCallback, 1, &objRef);
248 std::unique_ptr<NativeReference> callbackRef;
249 callbackRef.reset(reinterpret_cast<NativeReference*>(objRef));
250 if (jsCallBackMap_.find(eventType) == jsCallBackMap_.end()) {
251 jsCallBackMap_[eventType] = std::list<CallBackPair>();
252 }
253 bool isEqual = false;
254 auto& callbackList = jsCallBackMap_[eventType];
255 for (auto iter = callbackList.begin(); iter != callbackList.end(); iter++) {
256 napi_strict_equals(env, jsCallback, iter->first->GetNapiValue(), &isEqual);
257 if (isEqual) {
258 WLOGFW("Register a exist callback type.");
259 return NapiGetUndefined(env);
260 }
261 }
262 auto rssDataCb = [](napi_env env, napi_value callbackObj, uint32_t eventType,
263 std::unordered_map<std::string, std::string>&& extraInfo) {
264 RssSession::GetInstance().OnReceiveEvent(env, callbackObj, eventType, extraInfo);
265 };
266 sptr<RssEventListener> eventListener =
267 sptr<RssEventListener>::MakeSptr(env, jsCallback, rssDataCb);
268 ResSchedClient::GetInstance().RegisterEventListener(eventListener, eventType);
269 callbackList.emplace_back(std::move(callbackRef), eventListener);
270 return NapiGetUndefined(env);
271 }
272
UnRegisterRssDataCallback(napi_env env,napi_callback_info info)273 napi_value RssSession::UnRegisterRssDataCallback(napi_env env, napi_callback_info info)
274 {
275 WLOGFD("start");
276 uint32_t eventType;
277 napi_value jsCallback = nullptr;
278 if (!CheckCallbackParam(env, info, eventType, &jsCallback)) {
279 WLOGFE("UnRegister RssData Callback parameter error.");
280 return NapiGetUndefined(env);
281 }
282 if (jsCallBackMap_.find(eventType) == jsCallBackMap_.end()) {
283 WLOGFE("unRegister eventType has not registered");
284 return NapiGetUndefined(env);
285 }
286 auto& callbackList = jsCallBackMap_[eventType];
287 for (auto iter = callbackList.begin(); iter != callbackList.end(); iter++) {
288 bool isEqual = false;
289 napi_strict_equals(env, jsCallback, iter->first->GetNapiValue(), &isEqual);
290 if (isEqual) {
291 ResSchedClient::GetInstance().UnRegisterEventListener(iter->second, eventType);
292 callbackList.erase(iter);
293 break;
294 }
295 }
296 return NapiGetUndefined(env);
297 }
298
CompleteCb(napi_env env,napi_status status,void * data)299 void RssSession::CompleteCb(napi_env env, napi_status status, void* data)
300 {
301 WLOGFI("start");
302 auto* info = static_cast<RssSessionCbInfo*>(data);
303 if (info == nullptr) {
304 WLOGFW("Complete cb info is nullptr.");
305 return;
306 }
307 std::unique_ptr<RssSessionCbInfo> cbInfo(info);
308 napi_value undefined = nullptr;
309 NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
310 napi_value callback = nullptr;
311 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, cbInfo->callback_, &callback));
312 napi_value result = nullptr;
313 napi_create_object(env, &result);
314 SetMapValue(env, "appInfo", cbInfo->extraInfo_["selfBundleName"], result);
315 std::string reason;
316 ParseCallbackMutex(cbInfo->extraInfo_["mutex"], reason);
317 SetMapValue(env, "reason", reason, result);
318
319 // call js callback
320 napi_value callResult = nullptr;
321 NAPI_CALL_RETURN_VOID(env, napi_call_function(env, undefined, callback, 1, &result, &callResult));
322 // delete resources
323 NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, cbInfo->asyncWork_));
324 cbInfo->asyncWork_ = nullptr;
325 if (cbInfo->callback_ != nullptr) {
326 NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, cbInfo->callback_));
327 cbInfo->callback_ = nullptr;
328 }
329 WLOGFI("end");
330 }
331
CheckCallbackParam(napi_env env,napi_callback_info info,uint32_t & eventType,napi_value * jsCallback)332 bool RssSession::CheckCallbackParam(napi_env env, napi_callback_info info,
333 uint32_t& eventType, napi_value* jsCallback)
334 {
335 if (jsCallback == nullptr) {
336 WLOGFE("Input callback is nullptr.");
337 return false;
338 }
339 size_t argc = ARG_COUNT_TWO;
340 napi_value argv[ARG_COUNT_TWO] = { 0 };
341 NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr), false);
342 if (argc != ARG_COUNT_TWO) {
343 WLOGFE("Parameter error. The type of \"number of parameters\" must be 2");
344 return false;
345 }
346 if (!ConvertFromJsValue(env, argv[0], eventType)) {
347 WLOGFE("Parameter error. The type of \"type\" must be string");
348 return false;
349 }
350 *jsCallback = argv[ARG_COUNT_ONE];
351 if (*jsCallback == nullptr) {
352 WLOGFE("listenerObj is nullptr");
353 return false;
354 }
355 bool isCallable = false;
356 napi_is_callable(env, *jsCallback, &isCallable);
357 if (!isCallable) {
358 WLOGFE("Parameter error. The type of \"callback\" must be Callback");
359 return false;
360 }
361 return true;
362 }
363 #endif
364 } // OHOS