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