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 #ifndef LOG_TAG
16 #define LOG_TAG "bt_napi_async_callback"
17 #endif
18
19 #include "napi_async_callback.h"
20 #include "bluetooth_errorcode.h"
21 #include "bluetooth_log.h"
22
23 namespace OHOS {
24 namespace Bluetooth {
CallFunction(int errCode,const std::shared_ptr<NapiNativeObject> & object)25 void NapiAsyncCallback::CallFunction(int errCode, const std::shared_ptr<NapiNativeObject> &object)
26 {
27 if (callback == nullptr && promise == nullptr) {
28 HILOGE("callback & promise is nullptr");
29 return;
30 }
31 if (object == nullptr) {
32 HILOGE("napi native object is nullptr");
33 return;
34 }
35
36 if (callback) {
37 callback->CallFunction(errCode, object);
38 return;
39 }
40 if (promise) {
41 promise->ResolveOrReject(errCode, object);
42 }
43 }
44
GetRet(void)45 napi_value NapiAsyncCallback::GetRet(void)
46 {
47 if (promise) {
48 return promise->GetPromise();
49 }
50 return NapiGetUndefinedRet(env);
51 }
52
53 /*************************** env_cleanup_hook ********************************/
NapiCallbackEnvCleanupHook(void * data)54 void NapiCallbackEnvCleanupHook(void *data)
55 {
56 CHECK_AND_RETURN_LOG(data, "data is nullptr");
57
58 NapiCallback *callback = static_cast<NapiCallback *>(data);
59 callback->SetNapiEnvValidity(false);
60 }
61 /*************************** env_cleanup_hook ********************************/
62
NapiCallback(napi_env env,napi_value callback)63 NapiCallback::NapiCallback(napi_env env, napi_value callback) : env_(env)
64 {
65 // Use ID to identify NAPI callback.
66 static int idCount = 0;
67 id_ = idCount++;
68
69 auto status = napi_create_reference(env, callback, 1, &callbackRef_);
70 if (status != napi_ok) {
71 HILOGE("napi_create_reference failed, status: %{public}d", status);
72 }
73 // Used to clean up resources when env exit
74 napi_add_env_cleanup_hook(env, NapiCallbackEnvCleanupHook, this);
75 }
~NapiCallback()76 NapiCallback::~NapiCallback()
77 {
78 if (!IsValidNapiEnv()) {
79 return;
80 }
81 auto status = napi_delete_reference(env_, callbackRef_);
82 if (status != napi_ok) {
83 HILOGE("napi_delete_reference failed, status: %{public}d", status);
84 }
85 napi_remove_env_cleanup_hook(env_, NapiCallbackEnvCleanupHook, this);
86 }
87
88 namespace {
NapiCallFunction(napi_env env,napi_ref callbackRef,napi_value * argv,size_t argc)89 void NapiCallFunction(napi_env env, napi_ref callbackRef, napi_value *argv, size_t argc)
90 {
91 napi_value undefined = nullptr;
92 napi_value callRet = nullptr;
93 napi_value callback = nullptr;
94 auto status = napi_get_reference_value(env, callbackRef, &callback);
95 if (status != napi_ok) {
96 HILOGE("napi_get_reference_value failed, status: %{public}d", status);
97 return;
98 }
99
100 status = napi_call_function(env, undefined, callback, argc, argv, &callRet);
101 if (status != napi_ok) {
102 HILOGE("napi_call_function failed, status: %{public}d", status);
103 }
104
105 // Check whether the JS application triggers an exception in callback. If it is, clear it.
106 bool isExist = false;
107 status = napi_is_exception_pending(env, &isExist);
108 HILOGD("napi_is_exception_pending status: %{public}d, isExist: %{public}d", status, isExist);
109 if (isExist) {
110 HILOGI("Clear JS application's exception");
111 napi_value exception = nullptr;
112 status = napi_get_and_clear_last_exception(env, &exception);
113 HILOGD("napi_get_and_clear_last_exception status: %{public}d", status);
114 }
115 }
116 } // namespace {}
117
CallFunction(const std::shared_ptr<NapiNativeObject> & object)118 void NapiCallback::CallFunction(const std::shared_ptr<NapiNativeObject> &object)
119 {
120 if (!IsValidNapiEnv()) {
121 HILOGW("napi env is exit");
122 return;
123 }
124 if (object == nullptr) {
125 HILOGE("napi native object is nullptr");
126 return;
127 }
128
129 NapiHandleScope scope(env_);
130 napi_value val = object->ToNapiValue(env_);
131 NapiCallFunction(env_, callbackRef_, &val, ARGS_SIZE_ONE);
132 }
133
CallFunction(int errCode,const std::shared_ptr<NapiNativeObject> & object)134 void NapiCallback::CallFunction(int errCode, const std::shared_ptr<NapiNativeObject> &object)
135 {
136 if (!IsValidNapiEnv()) {
137 HILOGW("napi env is exit");
138 return;
139 }
140 if (object == nullptr) {
141 HILOGE("napi native object is nullptr");
142 return;
143 }
144
145 NapiHandleScope scope(env_);
146 napi_value code = GetCallbackErrorValue(env_, errCode);
147 napi_value val = object->ToNapiValue(env_);
148 napi_value argv[ARGS_SIZE_TWO] = {code, val};
149 NapiCallFunction(env_, callbackRef_, argv, ARGS_SIZE_TWO);
150 }
151
GetNapiEnv(void)152 napi_env NapiCallback::GetNapiEnv(void)
153 {
154 return env_;
155 }
156
Equal(napi_env env,napi_value & callback) const157 bool NapiCallback::Equal(napi_env env, napi_value &callback) const
158 {
159 if (!IsValidNapiEnv()) {
160 HILOGW("napi env is exit");
161 return false;
162 }
163 if (env != env_) {
164 HILOGD("Callback is not in the same thread, not uqual");
165 return false;
166 }
167 NapiHandleScope scope(env_);
168 napi_value storedCallback = nullptr;
169 napi_get_reference_value(env_, callbackRef_, &storedCallback);
170
171 bool isEqual = false;
172 napi_strict_equals(env_, storedCallback, callback, &isEqual);
173 return isEqual;
174 }
175
ToLogString(void) const176 std::string NapiCallback::ToLogString(void) const
177 {
178 return "callbackId: " + std::to_string(id_);
179 }
180
181 /*************************** env_cleanup_hook ********************************/
NapiPromiseEnvCleanupHook(void * data)182 void NapiPromiseEnvCleanupHook(void *data)
183 {
184 CHECK_AND_RETURN_LOG(data, "data is nullptr");
185
186 NapiPromise *callback = static_cast<NapiPromise *>(data);
187 callback->SetNapiEnvValidity(false);
188 }
189 /*************************** env_cleanup_hook ********************************/
190
NapiPromise(napi_env env)191 NapiPromise::NapiPromise(napi_env env) : env_(env)
192 {
193 auto status = napi_create_promise(env, &deferred_, &promise_);
194 if (status != napi_ok) {
195 HILOGE("napi_create_promise failed, status: %{public}d", status);
196 }
197 // Used to clean up resources when env exit
198 napi_add_env_cleanup_hook(env, NapiPromiseEnvCleanupHook, this);
199 }
200
~NapiPromise()201 NapiPromise::~NapiPromise()
202 {
203 if (!IsValidNapiEnv()) {
204 return;
205 }
206 napi_remove_env_cleanup_hook(env_, NapiPromiseEnvCleanupHook, this);
207 }
208
ResolveOrReject(int errCode,const std::shared_ptr<NapiNativeObject> & object)209 void NapiPromise::ResolveOrReject(int errCode, const std::shared_ptr<NapiNativeObject> &object)
210 {
211 if (!IsValidNapiEnv()) {
212 HILOGW("napi env is exit");
213 return;
214 }
215 if (object == nullptr) {
216 HILOGE("napi native object is nullptr");
217 return;
218 }
219
220 if (isResolvedOrRejected_) {
221 HILOGE("napi Resolved Or Rejected");
222 return;
223 }
224
225 NapiHandleScope scope(env_);
226
227 if (errCode == BT_NO_ERROR) {
228 napi_value val = object->ToNapiValue(env_);
229 Resolve(val);
230 } else {
231 napi_value code = GetCallbackErrorValue(env_, errCode);
232 Reject(code);
233 }
234 isResolvedOrRejected_ = true;
235 }
236
Resolve(napi_value resolution)237 void NapiPromise::Resolve(napi_value resolution)
238 {
239 auto status = napi_resolve_deferred(env_, deferred_, resolution);
240 if (status != napi_ok) {
241 HILOGE("napi_resolve_deferred failed, status: %{public}d", status);
242 }
243 }
Reject(napi_value rejection)244 void NapiPromise::Reject(napi_value rejection)
245 {
246 auto status = napi_reject_deferred(env_, deferred_, rejection);
247 if (status != napi_ok) {
248 HILOGE("napi_reject_deferred failed, status: %{public}d", status);
249 }
250 }
GetPromise(void) const251 napi_value NapiPromise::GetPromise(void) const
252 {
253 return promise_;
254 }
255
NapiHandleScope(napi_env env)256 NapiHandleScope::NapiHandleScope(napi_env env) : env_(env)
257 {
258 napi_status status = napi_open_handle_scope(env_, &scope_);
259 if (status != napi_ok) {
260 HILOGE("napi_open_handle_scope failed, status(%{public}d)", status);
261 }
262 }
263
~NapiHandleScope()264 NapiHandleScope::~NapiHandleScope()
265 {
266 napi_status status = napi_close_handle_scope(env_, scope_);
267 if (status != napi_ok) {
268 HILOGE("napi_close_handle_scope failed, status(%{public}d)", status);
269 }
270 }
271
272 } // namespace Bluetooth
273 } // namespace OHOS
274