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