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 "location_gnss_geofence_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 #include "geofence_napi.h"
24 #include "location_async_context.h"
25 #include "geofence_async_context.h"
26 
27 namespace OHOS {
28 namespace Location {
29 static std::mutex g_regCallbackMutex;
30 static std::vector<napi_ref> g_registerCallbacks;
LocationGnssGeofenceCallbackNapi()31 LocationGnssGeofenceCallbackNapi::LocationGnssGeofenceCallbackNapi()
32 {
33     env_ = nullptr;
34     handlerCb_ = nullptr;
35     remoteDied_ = false;
36     fenceId_ = -1;
37     type_ = GNSS_GEOFENCE_OPT_TYPE_ADD;
38     result_ = GNSS_GEOFENCE_OPERATION_SUCCESS;
39     InitLatch();
40 }
41 
~LocationGnssGeofenceCallbackNapi()42 LocationGnssGeofenceCallbackNapi::~LocationGnssGeofenceCallbackNapi()
43 {
44     if (latch_ != nullptr) {
45         delete latch_;
46         latch_ = nullptr;
47     }
48 }
49 
InitLatch()50 void LocationGnssGeofenceCallbackNapi::InitLatch()
51 {
52     latch_ = new CountDownLatch();
53     latch_->SetCount(1);
54 }
55 
OnRemoteRequest(uint32_t code,MessageParcel & data,MessageParcel & reply,MessageOption & option)56 int LocationGnssGeofenceCallbackNapi::OnRemoteRequest(
57     uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option)
58 {
59     LBSLOGD(LOCATION_GNSS_GEOFENCE_CALLBACK, "OnRemoteRequest enter");
60     if (data.ReadInterfaceToken() != GetDescriptor()) {
61         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "invalid token.");
62         return -1;
63     }
64     if (remoteDied_) {
65         LBSLOGD(LOCATION_GNSS_GEOFENCE_CALLBACK, "Failed to `%{public}s`,Remote service is died!", __func__);
66         return -1;
67     }
68     switch (code) {
69         case RECEIVE_TRANSITION_STATUS_EVENT: {
70             GeofenceTransition transition;
71             transition.fenceId = data.ReadInt32();
72             transition.event =
73                 static_cast<GeofenceTransitionEvent>(data.ReadInt32());
74             OnTransitionStatusChange(transition);
75             break;
76         }
77         case REPORT_OPERATION_RESULT_EVENT: {
78             int fenceId = data.ReadInt32();
79             int type = data.ReadInt32();
80             int result = data.ReadInt32();
81             OnReportOperationResult(fenceId, type, result);
82             CountDown();
83             break;
84         }
85         default: {
86             IPCObjectStub::OnRemoteRequest(code, data, reply, option);
87             break;
88         }
89     }
90     return 0;
91 }
92 
GetEnv()93 napi_env LocationGnssGeofenceCallbackNapi::GetEnv()
94 {
95     std::unique_lock<std::mutex> guard(mutex_);
96     return env_;
97 }
98 
SetEnv(const napi_env & env)99 void LocationGnssGeofenceCallbackNapi::SetEnv(const napi_env& env)
100 {
101     std::unique_lock<std::mutex> guard(mutex_);
102     env_ = env;
103 }
104 
GetHandleCb()105 napi_ref LocationGnssGeofenceCallbackNapi::GetHandleCb()
106 {
107     std::unique_lock<std::mutex> guard(mutex_);
108     return handlerCb_;
109 }
110 
SetHandleCb(const napi_ref & handlerCb)111 void LocationGnssGeofenceCallbackNapi::SetHandleCb(const napi_ref& handlerCb)
112 {
113     {
114         std::unique_lock<std::mutex> guard(mutex_);
115         handlerCb_ = handlerCb;
116     }
117     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
118     g_registerCallbacks.emplace_back(handlerCb);
119 }
120 
FindGeofenceRegCallback(napi_ref cb)121 bool FindGeofenceRegCallback(napi_ref cb)
122 {
123     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
124     auto iter = std::find(g_registerCallbacks.begin(), g_registerCallbacks.end(), cb);
125     if (iter == g_registerCallbacks.end()) {
126         return false;
127     }
128     return true;
129 }
130 
DeleteGeofenceRegCallback(napi_ref cb)131 void DeleteGeofenceRegCallback(napi_ref cb)
132 {
133     std::unique_lock<std::mutex> guard(g_regCallbackMutex);
134     for (auto iter = g_registerCallbacks.begin(); iter != g_registerCallbacks.end(); iter++) {
135         if (*iter == cb) {
136             iter = g_registerCallbacks.erase(iter);
137             break;
138         }
139     }
140 }
141 
OnTransitionStatusChange(GeofenceTransition transition)142 void LocationGnssGeofenceCallbackNapi::OnTransitionStatusChange(
143     GeofenceTransition transition)
144 {
145     std::unique_lock<std::mutex> guard(mutex_);
146     uv_loop_s *loop = nullptr;
147     NAPI_CALL_RETURN_VOID(env_, napi_get_uv_event_loop(env_, &loop));
148     if (loop == nullptr) {
149         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "loop == nullptr.");
150         return;
151     }
152     if (handlerCb_ == nullptr) {
153         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "handler is nullptr.");
154         return;
155     }
156     uv_work_t *work = new (std::nothrow) uv_work_t;
157     if (work == nullptr) {
158         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "work == nullptr.");
159         return;
160     }
161     GnssGeofenceAsyncContext *context = new (std::nothrow) GnssGeofenceAsyncContext(env_);
162     if (context == nullptr) {
163         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "context == nullptr.");
164         delete work;
165         return;
166     }
167     if (!InitContext(context)) {
168         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "InitContext fail");
169         return;
170     }
171     context->transition_ = transition;
172     work->data = context;
173     UvQueueWork(loop, work);
174 }
175 
OnReportOperationResult(int fenceId,int type,int result)176 void LocationGnssGeofenceCallbackNapi::OnReportOperationResult(int fenceId, int type, int result)
177 {
178     int addValue = static_cast<int>(GnssGeofenceOperateType::GNSS_GEOFENCE_OPT_TYPE_ADD);
179     if ((type != addValue && fenceId == GetFenceId()) ||
180         (type == addValue)) {
181         GnssGeofenceOperateResult optResult = static_cast<GnssGeofenceOperateResult>(result);
182         GnssGeofenceOperateType optType = static_cast<GnssGeofenceOperateType>(type);
183         if (result == GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_SUCCESS &&
184             optType == GnssGeofenceOperateType::GNSS_GEOFENCE_OPT_TYPE_ADD) {
185             SetFenceId(fenceId);
186         }
187         SetGeofenceOperationType(optType);
188         SetGeofenceOperationResult(optResult);
189     }
190 }
191 
IsRemoteDied()192 bool LocationGnssGeofenceCallbackNapi::IsRemoteDied()
193 {
194     return remoteDied_;
195 }
196 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)197 void LocationGnssGeofenceCallbackNapi::UvQueueWork(uv_loop_s* loop, uv_work_t* work)
198 {
199     uv_queue_work(
200         loop, work, [](uv_work_t *work) {}, [](uv_work_t *work, int status) {
201             GnssGeofenceAsyncContext *context = nullptr;
202             napi_handle_scope scope = nullptr;
203             if (work == nullptr) {
204                 LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "work is nullptr");
205                 return;
206             }
207             context = static_cast<GnssGeofenceAsyncContext *>(work->data);
208             if (context == nullptr || context->env == nullptr) {
209                 LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "context is nullptr");
210                 delete work;
211                 return;
212             }
213             if (!FindGeofenceRegCallback(context->callback[0])) {
214                 LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "no valid callback");
215                 delete context;
216                 delete work;
217                 return;
218             }
219             NAPI_CALL_RETURN_VOID(context->env, napi_open_handle_scope(context->env, &scope));
220             if (scope == nullptr) {
221                 LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "scope is nullptr");
222                 delete context;
223                 delete work;
224                 return;
225             }
226             napi_value jsEvent[PARAM2];
227             CHK_NAPI_ERR_CLOSE_SCOPE(context->env, napi_create_object(context->env, &jsEvent[PARAM1]),
228                 scope, context, work);
229             CHK_NAPI_ERR_CLOSE_SCOPE(context->env, napi_get_undefined(context->env, &jsEvent[PARAM0]),
230                 scope, context, work);
231             GeofenceTransitionToJs(context->env, context->transition_, jsEvent[PARAM1]);
232             if (context->callback[SUCCESS_CALLBACK] != nullptr) {
233                 napi_value undefine;
234                 napi_value handler = nullptr;
235                 CHK_NAPI_ERR_CLOSE_SCOPE(context->env, napi_get_undefined(context->env, &undefine),
236                     scope, context, work);
237                 CHK_NAPI_ERR_CLOSE_SCOPE(context->env,
238                     napi_get_reference_value(context->env, context->callback[SUCCESS_CALLBACK], &handler),
239                     scope, context, work);
240                 if (napi_call_function(context->env, nullptr, handler, RESULT_SIZE, jsEvent, &undefine) != napi_ok) {
241                     LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "Report event failed");
242                 }
243             }
244             NAPI_CALL_RETURN_VOID(context->env, napi_close_handle_scope(context->env, scope));
245             delete context;
246             delete work;
247     });
248 }
249 
DeleteHandler()250 void LocationGnssGeofenceCallbackNapi::DeleteHandler()
251 {
252     std::unique_lock<std::mutex> guard(mutex_);
253     if (handlerCb_ == nullptr || env_ == nullptr) {
254         LBSLOGE(LOCATION_GNSS_GEOFENCE_CALLBACK, "handler or env is nullptr.");
255         return;
256     }
257     DeleteGeofenceRegCallback(handlerCb_);
258     NAPI_CALL_RETURN_VOID(env_, napi_delete_reference(env_, handlerCb_));
259     handlerCb_ = nullptr;
260 }
261 
CountDown()262 void LocationGnssGeofenceCallbackNapi::CountDown()
263 {
264     if (latch_ != nullptr) {
265         latch_->CountDown();
266     }
267 }
268 
Wait(int time)269 void LocationGnssGeofenceCallbackNapi::Wait(int time)
270 {
271     if (latch_ != nullptr) {
272         latch_->Wait(time);
273     }
274 }
275 
GetCount()276 int LocationGnssGeofenceCallbackNapi::GetCount()
277 {
278     if (latch_ != nullptr) {
279         return latch_->GetCount();
280     }
281     return 0;
282 }
283 
SetCount(int count)284 void LocationGnssGeofenceCallbackNapi::SetCount(int count)
285 {
286     if (latch_ != nullptr) {
287         return latch_->SetCount(count);
288     }
289 }
290 
ClearFenceId()291 void LocationGnssGeofenceCallbackNapi::ClearFenceId()
292 {
293     std::unique_lock<std::mutex> guard(operationResultMutex_);
294     fenceId_ = -1;
295 }
296 
GetFenceId()297 int LocationGnssGeofenceCallbackNapi::GetFenceId()
298 {
299     std::unique_lock<std::mutex> guard(operationResultMutex_);
300     return fenceId_;
301 }
302 
SetFenceId(int fenceId)303 void LocationGnssGeofenceCallbackNapi::SetFenceId(int fenceId)
304 {
305     std::unique_lock<std::mutex> guard(operationResultMutex_);
306     fenceId_ = fenceId;
307 }
308 
GetGeofenceOperationType()309 GnssGeofenceOperateType LocationGnssGeofenceCallbackNapi::GetGeofenceOperationType()
310 {
311     std::unique_lock<std::mutex> guard(operationResultMutex_);
312     return type_;
313 }
314 
SetGeofenceOperationType(GnssGeofenceOperateType type)315 void LocationGnssGeofenceCallbackNapi::SetGeofenceOperationType(GnssGeofenceOperateType type)
316 {
317     std::unique_lock<std::mutex> guard(operationResultMutex_);
318     type_ = type;
319 }
320 
GetGeofenceOperationResult()321 GnssGeofenceOperateResult LocationGnssGeofenceCallbackNapi::GetGeofenceOperationResult()
322 {
323     std::unique_lock<std::mutex> guard(operationResultMutex_);
324     return result_;
325 }
326 
SetGeofenceOperationResult(GnssGeofenceOperateResult result)327 void LocationGnssGeofenceCallbackNapi::SetGeofenceOperationResult(GnssGeofenceOperateResult result)
328 {
329     std::unique_lock<std::mutex> guard(operationResultMutex_);
330     result_ = result;
331 }
332 
DealGeofenceOperationResult()333 LocationErrCode LocationGnssGeofenceCallbackNapi::DealGeofenceOperationResult()
334 {
335     std::unique_lock<std::mutex> guard(operationResultMutex_);
336     LocationErrCode errCode = ERRCODE_SUCCESS;
337     GnssGeofenceOperateResult result = result_;
338     switch (result) {
339         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_SUCCESS:
340             errCode = ERRCODE_SUCCESS;
341             break;
342         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_ERROR_UNKNOWN:
343             errCode = ERRCODE_SERVICE_UNAVAILABLE;
344             break;
345         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_ERROR_TOO_MANY_GEOFENCES:
346             errCode = ERRCODE_GEOFENCE_EXCEED_MAXIMUM;
347             break;
348         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_ERROR_GEOFENCE_ID_EXISTS:
349             errCode = ERRCODE_SERVICE_UNAVAILABLE;
350             break;
351         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_ERROR_PARAMS_INVALID:
352             errCode = ERRCODE_SERVICE_UNAVAILABLE;
353             break;
354         case GnssGeofenceOperateResult::GNSS_GEOFENCE_OPERATION_ERROR_GEOFENCE_ID_UNKNOWN:
355             errCode = ERRCODE_GEOFENCE_INCORRECT_ID;
356             break;
357         default:
358             break;
359     }
360     return errCode;
361 }
362 }  // namespace Location
363 }  // namespace OHOS
364