1 /*
2 * Copyright (c) 2022 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 #include <uv.h>
16
17 #include "event_listener.h"
18
19 #include "app_log_wrapper.h"
20 #include "common_func.h"
21 #include "napi/native_common.h"
22 #include "napi_constants.h"
23
24 namespace OHOS {
25 namespace AppExecFwk {
26 namespace {
27 constexpr const char* RESOURCE_NAME = "bmsMonitor";
28 };
29
HandleEnvCleanup(void * data)30 void HandleEnvCleanup(void *data)
31 {
32 APP_LOGI("env clean");
33 if (data != nullptr) {
34 EventListener *evtListener = static_cast<EventListener *>(data);
35 evtListener->SetValid(false);
36 }
37 }
38
JsCallback(napi_env env,napi_value jsCb,void * context,void * data)39 void JsCallback(napi_env env, napi_value jsCb, void *context, void *data)
40 {
41 APP_LOGI("JsCallback");
42 AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
43 if (asyncCallbackInfo == nullptr) {
44 APP_LOGE("call back is null");
45 return;
46 }
47 std::unique_ptr<AsyncCallbackInfo> callbackPtr {asyncCallbackInfo};
48 napi_handle_scope scope = nullptr;
49 napi_status status = napi_open_handle_scope(env, &scope);
50 do {
51 if (status != napi_ok || scope == nullptr) {
52 APP_LOGE("napi open scope %{public}d", status);
53 break;
54 }
55 napi_value result[ARGS_SIZE_ONE] = { 0 };
56 napi_value placeHolder = nullptr;
57 status = napi_create_object(env, &result[ARGS_POS_ZERO]);
58 if (status != napi_ok) {
59 APP_LOGE("napi create obj %{public}d", status);
60 break;
61 }
62 CommonFunc::ConvertBundleChangeInfo(env, asyncCallbackInfo->bundleName,
63 asyncCallbackInfo->userId, asyncCallbackInfo->appIndex, result[0]);
64 status = napi_call_function(env, nullptr,
65 jsCb, sizeof(result) / sizeof(result[0]), result, &placeHolder);
66 if (status != napi_ok) {
67 APP_LOGE("napi call %{public}d", status);
68 break;
69 }
70 } while (false);
71 napi_release_threadsafe_function(asyncCallbackInfo->tsfn,
72 napi_threadsafe_function_release_mode::napi_tsfn_release);
73 napi_close_handle_scope(env, scope);
74 APP_LOGD("JsCallback OK");
75 }
76
EventListener(napi_env env,const std::string & type)77 EventListener::EventListener(napi_env env, const std::string& type) : env_(env), type_(type)
78 {
79 napi_status status = napi_add_env_cleanup_hook(env_, HandleEnvCleanup, this);
80 APP_LOGI("EventListener() %{public}d", status);
81 }
82
~EventListener()83 EventListener::~EventListener()
84 {
85 napi_status status = napi_remove_env_cleanup_hook(env_, HandleEnvCleanup, this);
86 APP_LOGI("~EventListener() %{public}d", status);
87 }
88
Add(napi_env env,napi_value handler)89 void EventListener::Add(napi_env env, napi_value handler)
90 {
91 APP_LOGD("Add Init");
92 if (!HasSameEnv(env) || Find(handler)) {
93 return;
94 }
95 napi_ref callbackRef = nullptr;
96 NAPI_CALL_RETURN_VOID(env, napi_create_reference(env_, handler, 1, &callbackRef));
97 napi_threadsafe_function tsfn = nullptr;
98 napi_value resName = nullptr;
99 NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, RESOURCE_NAME, NAPI_AUTO_LENGTH, &resName));
100 NAPI_CALL_RETURN_VOID(env, napi_create_threadsafe_function(env, handler,
101 nullptr, resName, 0, 1, nullptr, nullptr, nullptr, JsCallback, &tsfn));
102
103 std::lock_guard<std::mutex> refsLock(callbackRefsMutex_);
104 callbackRefs_.push_back(std::make_pair(callbackRef, tsfn));
105 }
106
Delete(napi_env env,napi_value handler)107 void EventListener::Delete(napi_env env, napi_value handler)
108 {
109 APP_LOGD("Delete Init");
110 if (!HasSameEnv(env)) {
111 return;
112 }
113 std::lock_guard<std::mutex> refsLock(callbackRefsMutex_);
114 for (auto it = callbackRefs_.begin(); it != callbackRefs_.end();) {
115 napi_value callback = nullptr;
116 napi_get_reference_value(env_, (*it).first, &callback);
117 bool isEquals = false;
118 napi_strict_equals(env_, handler, callback, &isEquals);
119 if (isEquals) {
120 napi_delete_reference(env_, (*it).first);
121 napi_release_threadsafe_function((*it).second, napi_threadsafe_function_release_mode::napi_tsfn_release);
122 it = callbackRefs_.erase(it);
123 } else {
124 ++it;
125 }
126 }
127 }
128
DeleteAll()129 void EventListener::DeleteAll()
130 {
131 std::lock_guard<std::mutex> refsLock(callbackRefsMutex_);
132 for (const auto &item : callbackRefs_) {
133 napi_delete_reference(env_, item.first);
134 napi_release_threadsafe_function(item.second, napi_threadsafe_function_release_mode::napi_tsfn_release);
135 }
136 callbackRefs_.clear();
137 }
138
Find(napi_value handler)139 bool EventListener::Find(napi_value handler)
140 {
141 std::lock_guard<std::mutex> refsLock(callbackRefsMutex_);
142 for (const auto &callbackRef : callbackRefs_) {
143 napi_value callback = nullptr;
144 napi_get_reference_value(env_, callbackRef.first, &callback);
145 bool isEquals = false;
146 napi_strict_equals(env_, handler, callback, &isEquals);
147 if (isEquals) {
148 return true;
149 }
150 }
151 return false;
152 }
153
154 // operator on js thread
Emit(std::string & bundleName,int32_t userId,int32_t appIndex)155 void EventListener::Emit(std::string &bundleName, int32_t userId, int32_t appIndex)
156 {
157 APP_LOGD("EventListener Emit Init callback size is %{publuic}d",
158 static_cast<int32_t>(callbackRefs_.size()));
159 std::lock_guard<std::mutex> lock(validMutex_);
160 if (!valid_) {
161 APP_LOGE("env is invalid");
162 return;
163 }
164 std::lock_guard<std::mutex> refsLock(callbackRefsMutex_);
165 for (const auto &callbackRef : callbackRefs_) {
166 EmitOnUV(bundleName, userId, appIndex, callbackRef);
167 }
168 }
169
EmitOnUV(const std::string & bundleName,int32_t userId,int32_t appIndex,std::pair<napi_ref,napi_threadsafe_function> callbackRef)170 void EventListener::EmitOnUV(const std::string &bundleName, int32_t userId, int32_t appIndex,
171 std::pair<napi_ref, napi_threadsafe_function> callbackRef)
172 {
173 NAPI_CALL_RETURN_VOID(env_, napi_acquire_threadsafe_function(callbackRef.second));
174 AsyncCallbackInfo *asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo {
175 .bundleName = bundleName,
176 .userId = userId,
177 .appIndex = appIndex,
178 .tsfn = callbackRef.second,
179 };
180 if (asyncCallbackInfo == nullptr) {
181 napi_release_threadsafe_function(callbackRef.second,
182 napi_threadsafe_function_release_mode::napi_tsfn_release);
183 return;
184 }
185 napi_status status = napi_call_threadsafe_function(callbackRef.second, asyncCallbackInfo,
186 napi_threadsafe_function_call_mode::napi_tsfn_nonblocking);
187 if (status != napi_ok) {
188 APP_LOGE("napi call safe %{public}d", status);
189 delete asyncCallbackInfo;
190 napi_release_threadsafe_function(callbackRef.second,
191 napi_threadsafe_function_release_mode::napi_tsfn_release);
192 }
193 }
194
HasSameEnv(napi_env env) const195 bool EventListener::HasSameEnv(napi_env env) const
196 {
197 return env_ == env;
198 }
199
SetValid(bool valid)200 void EventListener::SetValid(bool valid)
201 {
202 std::lock_guard<std::mutex> lock(validMutex_);
203 valid_ = valid;
204 if (!valid) {
205 env_ = nullptr;
206 }
207 }
208 }
209 }