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 #include "bluetooth_utils.h"
17 #include "napi_bluetooth_ble_utils.h"
18 #include "napi_bluetooth_event.h"
19 
20 namespace OHOS {
21 namespace Bluetooth {
EventNotify(AsyncEventData *asyncEvent)22 void NapiEvent::EventNotify(AsyncEventData *asyncEvent)
23 {
24     uv_loop_s *loop = nullptr;
25     napi_get_uv_event_loop(asyncEvent->env_, &loop);
26 
27     uv_work_t *work = new uv_work_t;
28     if (work == nullptr) {
29         HILOGI("uv_work_t work is null.");
30         delete asyncEvent;
31         asyncEvent = nullptr;
32         return;
33     }
34     work->data = asyncEvent;
35 
36     uv_queue_work(
37         loop,
38         work,
39         [](uv_work_t *work) {},
40         [](uv_work_t *work, int status) {
41             AsyncEventData *callbackInfo = static_cast<AsyncEventData*>(work->data);
42             napi_value callback = nullptr;
43             napi_status ret = napi_get_reference_value(callbackInfo->env_, callbackInfo->callback_, &callback);
44             if (ret == napi_ok && callback != nullptr) {
45                 napi_value result = nullptr;
46                 napi_value undefined = nullptr;
47                 napi_value callResult = nullptr;
48                 if (napi_get_undefined(callbackInfo->env_, &undefined) == napi_ok) {
49                     result = callbackInfo->packResult();
50                 }
51                 if (result != nullptr) {
52                     napi_call_function(callbackInfo->env_, undefined, callback, ARGS_SIZE_ONE,
53                         &result, &callResult);
54                 }
55             }
56             delete callbackInfo;
57             delete work;
58             work = nullptr;
59         }
60     );
61 }
62 
CreateResult(const std::shared_ptr<BluetoothCallbackInfo> &cb, int value)63 napi_value NapiEvent::CreateResult(const std::shared_ptr<BluetoothCallbackInfo> &cb, int value)
64 {
65     napi_value result = nullptr;
66     if (cb == nullptr) {
67         HILOGE("CreateResult cb is null!");
68         return result;
69     }
70     napi_create_object(cb->env_, &result);
71     ConvertStateChangeParamToJS(cb->env_, result, cb->deviceId_, value,
72         static_cast<int>(ConnChangeCause::CONNECT_CHANGE_COMMON_CAUSE));
73     return result;
74 }
75 
CreateResult(const std::shared_ptr<BluetoothCallbackInfo> &cb, BluetoothOppTransferInformation &information)76 napi_value NapiEvent::CreateResult(const std::shared_ptr<BluetoothCallbackInfo> &cb,
77     BluetoothOppTransferInformation &information)
78 {
79     napi_value result = nullptr;
80     napi_create_object(cb->env_, &result);
81     ConvertOppTransferInformationToJS(cb->env_, result, information);
82     return result;
83 }
84 
85 // if callbackInfos contains specific type, new callbackInfo will cover the old.
86 // If exist, covered event happen, this function will clear rest reference of old callbackInfo in napi framework.
UpdateCallbackInfo(std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> callbackInfos, const std::string &type)87 void UpdateCallbackInfo(std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> callbackInfos,
88     const std::string &type)
89 {
90     auto it = callbackInfos.find(type);
91     if (it != callbackInfos.end() && it->second != nullptr) {
92         HILOGD("repetition type %{public}s is register, old callbackInfo will be deleted.", type.c_str());
93         // as long as the type is same, callbackInfo will be covered.
94         uint32_t refCount = INVALID_REF_COUNT;
95         napi_value handlerTemp = nullptr;
96         napi_status status = napi_get_reference_value(it->second->env_, it->second->callback_, &handlerTemp);
97         if (status != napi_ok) {
98             HILOGE("napi_get_reference_value failed. napi status is %{public}d", status);
99             return;
100         }
101         // if handlerTemp exist clear it. no exist, memory leak safe
102         if (handlerTemp != nullptr) {
103             HILOGD("napi_get_reference_value succeed");
104             napi_reference_unref(it->second->env_, it->second->callback_, &refCount);
105             HILOGD("decrements the refernce count, refCount: %{public}d", refCount);
106             // other place like EventNotify before use will add refCount, happen refCount != 0,
107             // ensure other place copy the prepare covered callbackInfo、add refCount and unref and delete refCount
108             if (refCount == 0) {
109                 HILOGD("delete the reference");
110                 napi_delete_reference(it->second->env_, it->second->callback_);
111             }
112             HILOGD("old %{public}s is deleted", type.c_str());
113         } else {
114             HILOGI("napi_get_reference_value is nullptr");
115         }
116     }
117 }
118 
OnEvent(napi_env env, napi_callback_info info, std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> &callbackInfos)119 napi_value NapiEvent::OnEvent(napi_env env, napi_callback_info info,
120     std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> &callbackInfos)
121 {
122     size_t expectedArgsCount = ARGS_SIZE_TWO;
123     size_t argc = expectedArgsCount;
124     napi_value argv[ARGS_SIZE_TWO] = {0};
125     napi_value thisVar = nullptr;
126 
127     napi_value ret = nullptr;
128     napi_get_undefined(env, &ret);
129 
130     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
131     if (argc != expectedArgsCount) {
132         HILOGE("Requires 2 argument.");
133         return ret;
134     }
135     std::string type;
136     if (!ParseString(env, type, argv[PARAM0])) {
137         HILOGE("string expected.");
138         return ret;
139     }
140     std::shared_ptr<BluetoothCallbackInfo> callbackInfo = std::make_shared<BluetoothCallbackInfo>();
141     callbackInfo->env_ = env;
142 
143     napi_valuetype valueType = napi_undefined;
144     napi_typeof(env, argv[PARAM1], &valueType);
145     if (valueType != napi_function) {
146         HILOGE("Wrong argument type. Function expected.");
147         return ret;
148     }
149 
150     UpdateCallbackInfo(callbackInfos, type);
151     napi_create_reference(env, argv[PARAM1], 1, &callbackInfo->callback_);
152     callbackInfos[type] = callbackInfo;
153     HILOGD("%{public}s is registered", type.c_str());
154     return ret;
155 }
156 
OffEvent(napi_env env, napi_callback_info info, std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> &callbackInfos)157 napi_value NapiEvent::OffEvent(napi_env env, napi_callback_info info,
158     std::map<std::string, std::shared_ptr<BluetoothCallbackInfo>> &callbackInfos)
159 {
160     size_t argc = ARGS_SIZE_ONE;
161     napi_value argv[ARGS_SIZE_TWO] = {0};
162     napi_value thisVar = nullptr;
163 
164     napi_value ret = nullptr;
165     napi_get_undefined(env, &ret);
166 
167     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
168     if (argc != ARGS_SIZE_ONE && argc != ARGS_SIZE_TWO) {
169         HILOGE("Requires 1 or 2 argument.");
170         return ret;
171     }
172     std::string type;
173     if (!ParseString(env, type, argv[PARAM0])) {
174         HILOGE("string expected.");
175         return ret;
176     }
177     auto it = callbackInfos.find(type);
178     if (it == callbackInfos.end() || it->second == nullptr) {
179         HILOGE("type %{public}s callbackInfos isn't exist.", type.c_str());
180         return ret;
181     }
182     if (env != it->second->env_) {
183         HILOGE("env doesn't match, please check.");
184         return ret;
185     }
186     napi_delete_reference(env, it->second->callback_);
187     it->second = nullptr;
188     HILOGI("%{public}s is unregistered", type.c_str());
189     return ret;
190 }
191 }  // namespace Bluetooth
192 }  // namespace OHOS