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 "country_code_callback_napi.h"
17 
18 #include "common_utils.h"
19 #include "ipc_skeleton.h"
20 #include "location_log.h"
21 #include "napi/native_api.h"
22 #include "country_code.h"
23 #include "napi_util.h"
24 
25 namespace OHOS {
26 namespace Location {
27 static std::mutex g_regCallbackMutex;
28 static std::vector<napi_ref> g_registerCallbacks;
CountryCodeCallbackNapi()29 CountryCodeCallbackNapi::CountryCodeCallbackNapi()
30 {
31     env_ = nullptr;
32     handlerCb_ = nullptr;
33 }
34 
~CountryCodeCallbackNapi()35 CountryCodeCallbackNapi::~CountryCodeCallbackNapi()
36 {
37 }
38 
OnRemoteRequest(uint32_t code,MessageParcel & data,MessageParcel & reply,MessageOption & option)39 int CountryCodeCallbackNapi::OnRemoteRequest(
40     uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option)
41 {
42     LBSLOGD(COUNTRY_CODE_CALLBACK, "CountryCodeCallbackNapi::OnRemoteRequest!");
43     if (data.ReadInterfaceToken() != GetDescriptor()) {
44         LBSLOGE(COUNTRY_CODE_CALLBACK, "invalid token.");
45         return -1;
46     }
47 
48     switch (code) {
49         case COUNTRY_CODE_CHANGE_EVENT: {
50             auto countryCodePtr = CountryCode::Unmarshalling(data);
51             OnCountryCodeChange(countryCodePtr);
52             break;
53         }
54         default: {
55             IPCObjectStub::OnRemoteRequest(code, data, reply, option);
56             break;
57         }
58     }
59     return 0;
60 }
61 
FindCountryCodeCallback(napi_ref cb)62 bool FindCountryCodeCallback(napi_ref cb)
63 {
64     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
65     auto iter = std::find(g_registerCallbacks.begin(), g_registerCallbacks.end(), cb);
66     if (iter == g_registerCallbacks.end()) {
67         return false;
68     }
69     return true;
70 }
71 
DeleteCountryCodeCallback(napi_ref cb)72 void DeleteCountryCodeCallback(napi_ref cb)
73 {
74     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
75     for (auto iter = g_registerCallbacks.begin(); iter != g_registerCallbacks.end(); iter++) {
76         if (*iter == cb) {
77             iter = g_registerCallbacks.erase(iter);
78             break;
79         }
80     }
81 }
82 
Send(const std::shared_ptr<CountryCode> & country)83 bool CountryCodeCallbackNapi::Send(const std::shared_ptr<CountryCode>& country)
84 {
85     std::unique_lock<std::mutex> guard(mutex_);
86     uv_loop_s *loop = nullptr;
87     if (env_ == nullptr) {
88         LBSLOGE(COUNTRY_CODE_CALLBACK, "env_ == nullptr.");
89         return false;
90     }
91     if (handlerCb_ == nullptr) {
92         LBSLOGE(COUNTRY_CODE_CALLBACK, "handler is nullptr.");
93         return false;
94     }
95     if (country == nullptr) {
96         LBSLOGE(COUNTRY_CODE_CALLBACK, "country == nullptr.");
97         return false;
98     }
99     NAPI_CALL_BASE(env_, napi_get_uv_event_loop(env_, &loop), false);
100     if (loop == nullptr) {
101         LBSLOGE(COUNTRY_CODE_CALLBACK, "loop == nullptr.");
102         return false;
103     }
104     uv_work_t *work = new (std::nothrow) uv_work_t;
105     if (work == nullptr) {
106         LBSLOGE(COUNTRY_CODE_CALLBACK, "work == nullptr.");
107         return false;
108     }
109     auto context = new (std::nothrow) CountryCodeContext(env_);
110     if (context == nullptr) {
111         LBSLOGE(COUNTRY_CODE_CALLBACK, "context == nullptr.");
112         delete work;
113         return false;
114     }
115     if (!InitContext(context)) {
116         LBSLOGE(COUNTRY_CODE_CALLBACK, "InitContext fail");
117         return false;
118     }
119     context->country = country;
120     work->data = context;
121     UvQueueWork(loop, work);
122     return true;
123 }
124 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)125 void CountryCodeCallbackNapi::UvQueueWork(uv_loop_s* loop, uv_work_t* work)
126 {
127     uv_queue_work(
128         loop,
129         work,
130         [](uv_work_t *work) {},
131         [](uv_work_t *work, int status) {
132             CountryCodeContext *context = nullptr;
133             napi_handle_scope scope = nullptr;
134             if (work == nullptr) {
135                 LBSLOGE(COUNTRY_CODE_CALLBACK, "work is nullptr!");
136                 return;
137             }
138             context = static_cast<CountryCodeContext *>(work->data);
139             if (context == nullptr || context->env == nullptr) {
140                 LBSLOGE(COUNTRY_CODE_CALLBACK, "context is nullptr!");
141                 delete work;
142                 return;
143             }
144             if (!FindCountryCodeCallback(context->callback[0])) {
145                 LBSLOGE(COUNTRY_CODE_CALLBACK, "no valid callback");
146                 delete context;
147                 delete work;
148                 return;
149             }
150             NAPI_CALL_RETURN_VOID(context->env, napi_open_handle_scope(context->env, &scope));
151             if (scope == nullptr) {
152                 LBSLOGE(COUNTRY_CODE_CALLBACK, "scope is nullptr");
153                 delete context;
154                 delete work;
155                 return;
156             }
157             napi_value jsEvent;
158             CHK_NAPI_ERR_CLOSE_SCOPE(context->env, napi_create_object(context->env, &jsEvent),
159                 scope, context, work);
160             if (context->country) {
161                 CountryCodeToJs(context->env, context->country, jsEvent);
162             } else {
163                 LBSLOGE(LOCATOR_STANDARD, "country is nullptr!");
164             }
165             if (context->callback[0] != nullptr) {
166                 napi_value undefine;
167                 napi_value handler = nullptr;
168                 CHK_NAPI_ERR_CLOSE_SCOPE(context->env,
169                     napi_get_undefined(context->env, &undefine), scope, context, work);
170                 CHK_NAPI_ERR_CLOSE_SCOPE(context->env,
171                     napi_get_reference_value(context->env, context->callback[SUCCESS_CALLBACK], &handler),
172                     scope, context, work);
173                 if (napi_call_function(context->env, nullptr, handler, 1, &jsEvent, &undefine) != napi_ok) {
174                     LBSLOGE(COUNTRY_CODE_CALLBACK, "Report event failed");
175                 }
176             }
177             NAPI_CALL_RETURN_VOID(context->env, napi_close_handle_scope(context->env, scope));
178             delete context;
179             delete work;
180     });
181 }
182 
OnCountryCodeChange(const std::shared_ptr<CountryCode> & country)183 void CountryCodeCallbackNapi::OnCountryCodeChange(const std::shared_ptr<CountryCode>& country)
184 {
185     LBSLOGD(COUNTRY_CODE_CALLBACK, "CountryCodeCallbackNapi::OnCountryCodeChange");
186     Send(country);
187 }
188 
SetEnv(napi_env env)189 void CountryCodeCallbackNapi::SetEnv(napi_env env)
190 {
191     std::unique_lock<std::mutex> guard(mutex_);
192     env_ = env;
193 }
194 
SetCallback(napi_ref cb)195 void CountryCodeCallbackNapi::SetCallback(napi_ref cb)
196 {
197     {
198         std::unique_lock<std::mutex> guard(mutex_);
199         handlerCb_ = cb;
200     }
201     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
202     g_registerCallbacks.emplace_back(cb);
203 }
204 
DeleteHandler()205 void CountryCodeCallbackNapi::DeleteHandler()
206 {
207     std::unique_lock<std::mutex> guard(mutex_);
208     if (handlerCb_ == nullptr || env_ == nullptr) {
209         LBSLOGE(COUNTRY_CODE_CALLBACK, "handler or env is nullptr.");
210         return;
211     }
212     DeleteCountryCodeCallback(handlerCb_);
213     NAPI_CALL_RETURN_VOID(env_, napi_delete_reference(env_, handlerCb_));
214     handlerCb_ = nullptr;
215 }
216 }  // namespace Location
217 }  // namespace OHOS
218