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 "js_fence_extension.h"
17 #include "js_fence_extension_context.h"
18 #include "fence_extension_stub_impl.h"
19 
20 #include "ability_handler.h"
21 #include "ability_info.h"
22 #include "hilog_wrapper.h"
23 #include "js_extension_common.h"
24 #include "js_extension_context.h"
25 #include "runtime.h"
26 #include "js_runtime.h"
27 #include "js_runtime_utils.h"
28 #include "display_manager.h"
29 #include "napi/native_api.h"
30 #include "napi/native_node_api.h"
31 #include "napi_common_configuration.h"
32 #include "napi_common_want.h"
33 #include "napi_remote_object.h"
34 #include "location_log.h"
35 #include "geofence_definition.h"
36 #include <thread>
37 #include "napi/native_api.h"
38 #include "uv.h"
39 
40 using namespace OHOS::AbilityRuntime;
41 namespace OHOS {
42 namespace Location {
43 const size_t ARGC_ONE = 1;
44 const size_t ARGC_TWO = 2;
45 const size_t LOAD_SYSTEM_MODULE_ARGC = 1;
46 const std::string CONTEXT_MODULE_PATH = "app.ability.FenceExtensionContext";
47 using namespace OHOS::AppExecFwk;
48 
AttachFenceExtensionContext(napi_env env,void * value,void *)49 napi_value AttachFenceExtensionContext(napi_env env, void *value, void *)
50 {
51     LBSLOGI(FENCE_EXTENSION, "AttachFenceExtensionContext");
52     if (value == nullptr) {
53         LBSLOGE(FENCE_EXTENSION, "invalid parameter");
54         return nullptr;
55     }
56 
57     auto ptr = reinterpret_cast<std::weak_ptr<FenceExtensionContext> *>(value)->lock();
58     if (ptr == nullptr) {
59         LBSLOGE(FENCE_EXTENSION, "invalid context");
60         return nullptr;
61     }
62 
63     napi_value object = JsFenceExtensionContext::CreateJsFenceExtensionContext(env, ptr);
64     auto napiContextObj =
65         AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env, CONTEXT_MODULE_PATH, &object, LOAD_SYSTEM_MODULE_ARGC)
66             ->GetNapiValue();
67 
68     napi_coerce_to_native_binding_object(
69         env, napiContextObj, AbilityRuntime::DetachCallbackFunc, AttachFenceExtensionContext, value, nullptr);
70     auto workContext = new (std::nothrow) std::weak_ptr<FenceExtensionContext>(ptr);
71     if (workContext == nullptr) {
72         LBSLOGE(FENCE_EXTENSION, "invalid extension context");
73         return nullptr;
74     }
75     auto retStatus = ::napi_wrap(
76         env,
77         napiContextObj,
78         workContext,
79         [](napi_env, void *data, void *) {
80             LBSLOGI(FENCE_EXTENSION, "Finalizer for weak_ptr extension context is called");
81             delete static_cast<std::weak_ptr<FenceExtensionContext> *>(data);
82         },
83         nullptr,
84         nullptr);
85     if (retStatus != ::napi_status::napi_ok) {
86         LBSLOGE(FENCE_EXTENSION, "Napi wrap context error");
87         return nullptr;
88     }
89     LBSLOGI(FENCE_EXTENSION, "AttachFenceExtensionContext end");
90     return napiContextObj;
91 }
92 
Create(const std::unique_ptr<AbilityRuntime::Runtime> & runtime)93 JsFenceExtension *JsFenceExtension::Create(const std::unique_ptr<AbilityRuntime::Runtime> &runtime)
94 {
95     return new (std::nothrow) JsFenceExtension(static_cast<AbilityRuntime::JsRuntime &>(*runtime));
96 }
97 
JsFenceExtension(AbilityRuntime::JsRuntime & jsRuntime)98 JsFenceExtension::JsFenceExtension(AbilityRuntime::JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
99 {}
100 
~JsFenceExtension()101 JsFenceExtension::~JsFenceExtension()
102 {
103     LBSLOGD(FENCE_EXTENSION, "Js extension destructor");
104     auto context = GetContext();
105     if (context) {
106         context->Unbind();
107     }
108     jsRuntime_.FreeNativeReference(std::move(jsObj_));
109     jsRuntime_.FreeNativeReference(std::move(shellContextRef_));
110 }
111 
Init(const std::shared_ptr<AppExecFwk::AbilityLocalRecord> & record,const std::shared_ptr<AppExecFwk::OHOSApplication> & application,std::shared_ptr<AppExecFwk::AbilityHandler> & handler,const sptr<IRemoteObject> & token)112 void JsFenceExtension::Init(const std::shared_ptr<AppExecFwk::AbilityLocalRecord> &record,
113     const std::shared_ptr<AppExecFwk::OHOSApplication> &application,
114     std::shared_ptr<AppExecFwk::AbilityHandler> &handler, const sptr<IRemoteObject> &token)
115 {
116     FenceExtension::Init(record, application, handler, token);
117     if (Extension::abilityInfo_->srcEntrance.empty()) {
118         LBSLOGE(FENCE_EXTENSION, "srcEntrance of abilityInfo is empty");
119         return;
120     }
121     std::string srcPath = "";
122     GetSrcPath(srcPath);
123     if (srcPath.empty()) {
124         LBSLOGE(FENCE_EXTENSION, "Failed to get srcPath");
125         return;
126     }
127 
128     std::string moduleName(Extension::abilityInfo_->moduleName);
129     moduleName.append("::").append(abilityInfo_->name);
130     LBSLOGW(FENCE_EXTENSION,
131         "JsFenceExtension::Init module:%{public}s,srcPath:%{public}s",
132         moduleName.c_str(),
133         srcPath.c_str());
134     AbilityRuntime::HandleScope handleScope(jsRuntime_);
135 
136     jsObj_ = jsRuntime_.LoadModule(
137         moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE);
138     if (jsObj_ == nullptr) {
139         LBSLOGE(FENCE_EXTENSION, "Failed to load ability module");
140         return;
141     }
142     napi_value obj = jsObj_->GetNapiValue();
143     if (obj == nullptr) {
144         LBSLOGE(FENCE_EXTENSION, "Failed to get extension object");
145         return;
146     }
147     napi_env env = jsRuntime_.GetNapiEnv();
148 
149     LBSLOGI(FENCE_EXTENSION, "Init end");
150     BindContext(env, obj);
151 }
152 
BindContext(const::napi_env & env,const::napi_value & obj)153 void JsFenceExtension::BindContext(const ::napi_env &env, const ::napi_value &obj)
154 {
155     LBSLOGI(FENCE_EXTENSION, "BindContext start");
156     auto context = GetContext();
157     if (context == nullptr) {
158         LBSLOGE(FENCE_EXTENSION, "Failed to get context");
159         return;
160     }
161     napi_value contextObj = JsFenceExtensionContext::CreateJsFenceExtensionContext(env, context);
162     auto shellContextRef_ =
163         AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env, CONTEXT_MODULE_PATH, &contextObj, ARGC_ONE);
164     if (shellContextRef_ == nullptr) {
165         LBSLOGE(FENCE_EXTENSION, "Failed to get shell context from system module");
166         return;
167     }
168     napi_value nativeObj = shellContextRef_->GetNapiValue();
169     if (nativeObj == nullptr) {
170         LBSLOGE(FENCE_EXTENSION, "Failed to load context module");
171         return;
172     }
173 
174     auto workContext = new (std::nothrow) std::weak_ptr<FenceExtensionContext>(context);
175     if (workContext == nullptr) {
176         LBSLOGE(FENCE_EXTENSION, "invalid extension context");
177         return;
178     }
179     napi_coerce_to_native_binding_object(
180         env, nativeObj, AbilityRuntime::DetachCallbackFunc, AttachFenceExtensionContext, workContext, nullptr);
181     context->Bind(jsRuntime_, shellContextRef_.release());
182     napi_set_named_property(env, obj, "context", nativeObj);
183 
184     napi_wrap(
185         env,
186         nativeObj,
187         workContext,
188         [](napi_env, void *data, void *) {
189             LBSLOGI(FENCE_EXTENSION, "Finalizer for weak_ptr extension context is called");
190             delete static_cast<std::weak_ptr<FenceExtensionContext> *>(data);
191         },
192         nullptr,
193         nullptr);
194     LBSLOGI(FENCE_EXTENSION, "BindContext end");
195 }
OnStart(const AAFwk::Want & want)196 void JsFenceExtension::OnStart(const AAFwk::Want &want)
197 {
198     AbilityRuntime::Extension::OnStart(want);
199 }
200 
OnStop()201 void JsFenceExtension::OnStop()
202 {
203     LBSLOGI(FENCE_EXTENSION, "OnStop called");
204     AbilityRuntime::HandleScope handleScope(jsRuntime_);
205     if (jsObj_ == nullptr) {
206         LBSLOGE(FENCE_EXTENSION, "js fence extension obj is null");
207         return;
208     }
209     ::napi_value method = GetMethod(jsRuntime_, jsObj_, "onDestroy");
210     if (method == nullptr) {
211         LBSLOGE(FENCE_EXTENSION, "Call function method is null");
212         return;
213     }
214     napi_value argv[0];
215     ::napi_value undefined;
216     ::napi_env env = jsRuntime_.GetNapiEnv();
217     ::napi_value value = jsObj_->GetNapiValue();
218     ::napi_status retStatus = ::napi_call_function(env, value, method, 0, argv, &undefined);
219     if (retStatus != ::napi_status::napi_ok) {
220         LBSLOGE(FENCE_EXTENSION, "Call function error");
221     }
222     LBSLOGI(FENCE_EXTENSION, "Call onDestroy end");
223     AbilityRuntime::Extension::OnStop();
224 }
225 
OnConnect(const AAFwk::Want & want)226 sptr<IRemoteObject> JsFenceExtension::OnConnect(const AAFwk::Want &want)
227 {
228     LBSLOGI(FENCE_EXTENSION, "OnConnect");
229     AbilityRuntime::Extension::OnConnect(want);
230     sptr<FenceExtensionStubImpl> remoteObject =
231         new (std::nothrow) FenceExtensionStubImpl(std::static_pointer_cast<JsFenceExtension>(shared_from_this()));
232     if (remoteObject == nullptr) {
233         LBSLOGE(FENCE_EXTENSION, "failed to create FenceExtensionStubImpl");
234         return nullptr;
235     }
236     return remoteObject->AsObject();
237 }
238 
OnDisconnect(const AAFwk::Want & want)239 void JsFenceExtension::OnDisconnect(const AAFwk::Want &want)
240 {
241     LBSLOGI(FENCE_EXTENSION, "OnDisconnect");
242     Extension::OnDisconnect(want);
243 }
244 
OnFenceStatusChange(std::map<std::string,std::string> extraData)245 FenceExtensionErrCode JsFenceExtension::OnFenceStatusChange(std::map<std::string, std::string> extraData)
246 {
247     LBSLOGI(FENCE_EXTENSION, "js fence extension:OnFenceStatusChange");
248     if (jsObj_ == nullptr) {
249         LBSLOGE(FENCE_EXTENSION, "js fence extension obj is null");
250         return FenceExtensionErrCode::EXTENSION_JS_OBJ_IS_NULL;
251     }
252 
253     auto task = [=]() {
254         LBSLOGI(FENCE_EXTENSION, "call js function start");
255         JsFenceExtension::CallToUiThread(extraData);
256     };
257     if (handler_ == nullptr) {
258         LBSLOGE(FENCE_EXTENSION, "PostTask call js function start");
259         return FenceExtensionErrCode::EXTENSION_JS_CALL_FAILED;
260     }
261     handler_->PostTask(task, "FenceExtension OnFenceStatusChange Task");
262     LBSLOGI(FENCE_EXTENSION, "PostTask call js function start");
263     return FenceExtensionErrCode::EXTENSION_SUCCESS;
264 }
265 
GetSrcPath(std::string & srcPath)266 void JsFenceExtension::GetSrcPath(std::string &srcPath)
267 {
268     if (!Extension::abilityInfo_->srcEntrance.empty()) {
269         srcPath.append(Extension::abilityInfo_->moduleName);
270         srcPath.append("/");
271         srcPath.append(Extension::abilityInfo_->srcEntrance);
272         srcPath.erase(srcPath.rfind('.'));
273         srcPath.append(".abc");
274     }
275 }
276 
CallToUiThread(std::map<std::string,std::string> extraData)277 FenceExtensionErrCode JsFenceExtension::CallToUiThread(std::map<std::string, std::string> extraData)
278 {
279     LBSLOGI(FENCE_EXTENSION, "js fence extension:OnFenceStatusChange");
280     if (jsObj_ == nullptr) {
281         return FenceExtensionErrCode::EXTENSION_JS_OBJ_IS_NULL;
282     }
283     AbilityRuntime::HandleScope handleScope(jsRuntime_);
284     ::napi_value method = GetMethod(jsRuntime_, jsObj_, "onFenceStatusChange");
285     if (method == nullptr) {
286         return FenceExtensionErrCode::EXTENSION_JS_NOT_FOUND_METHOD;
287     }
288     auto fenceId = std::atoi(GetAndDeleteFromMap(extraData, EXTENSION_PARAM_KEY_FENCE_ID).c_str());
289     auto fenceEvent = std::atoi(GetAndDeleteFromMap(extraData, EXTENSION_PARAM_KEY_FENCE_EVENT).c_str());
290     ::napi_value transitionObj;
291     ::napi_env env = jsRuntime_.GetNapiEnv();
292     napi_status retTransition = ::napi_create_object(env, &transitionObj);
293     if (retTransition != napi_ok) {
294         return FenceExtensionErrCode::EXTENSION_JS_CREATE_PARAM_ERROR;
295     }
296     SetValueInt32(env, "geofenceId", fenceId, transitionObj);
297     SetValueInt32(env, "transitionEvent", static_cast<int>(fenceEvent), transitionObj);
298     ::napi_value addtionsRecord;
299     if (extraData.size() > 0) {
300         ::napi_status status = napi_create_object(env, &addtionsRecord);
301         for (const auto &pair : extraData) {
302             napi_value key, value;
303             status = napi_create_string_utf8(env, pair.first.c_str(), NAPI_AUTO_LENGTH, &key);
304             if (status != napi_ok) {
305                 break;
306             }
307             status = napi_create_string_utf8(env, pair.second.c_str(), NAPI_AUTO_LENGTH, &value);
308             if (status != napi_ok) {
309                 break;
310             }
311             status = napi_set_property(env, addtionsRecord, key, value);
312             if (status != napi_ok) {
313                 break;
314             }
315         }
316     } else {
317         ::napi_status status = napi_get_undefined(env, &addtionsRecord);
318     }
319     ::napi_value argv[PARAM2];
320     argv[PARAM0] = transitionObj;
321     argv[PARAM1] = addtionsRecord;
322     ::napi_value abilityObj = jsObj_->GetNapiValue();
323     ::napi_value undefined;
324     ::napi_status callStatus = ::napi_call_function(env, abilityObj, method, ARGC_TWO, argv, &undefined);
325     return FenceExtensionErrCode::EXTENSION_SUCCESS;
326 }
327 
SetValueUtf8String(const napi_env & env,const char * fieldStr,const char * str,napi_value & result)328 napi_status JsFenceExtension::SetValueUtf8String(
329     const napi_env &env, const char *fieldStr, const char *str, napi_value &result)
330 {
331     napi_value value = nullptr;
332     NAPI_CALL_BASE(env, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &value), napi_generic_failure);
333     NAPI_CALL_BASE(env, napi_set_named_property(env, result, fieldStr, value), napi_generic_failure);
334     return napi_ok;
335 }
SetValueInt32(const napi_env & env,const char * fieldStr,const int intValue,napi_value & result)336 napi_status JsFenceExtension::SetValueInt32(
337     const napi_env &env, const char *fieldStr, const int intValue, napi_value &result)
338 {
339     napi_value value = nullptr;
340     NAPI_CALL_BASE(env, napi_create_int32(env, intValue, &value), napi_generic_failure);
341     NAPI_CALL_BASE(env, napi_set_named_property(env, result, fieldStr, value), napi_generic_failure);
342     return napi_ok;
343 }
GetAndDeleteFromMap(std::map<std::string,std::string> & param,std::string key)344 std::string JsFenceExtension::GetAndDeleteFromMap(std::map<std::string, std::string> &param, std::string key)
345 {
346     auto mapItr = param.find(key);
347     std::string value;
348     if (mapItr != param.end()) {
349         value = mapItr->second;
350         param.erase(key);
351     }
352     return value;
353 }
354 
GetMethod(AbilityRuntime::JsRuntime & jsRuntime,const std::unique_ptr<NativeReference> & jsObj,const std::string & name)355 ::napi_value JsFenceExtension::GetMethod(
356     AbilityRuntime::JsRuntime &jsRuntime, const std::unique_ptr<NativeReference> &jsObj, const std::string &name)
357 {
358     if (!jsObj) {
359         LBSLOGE(FENCE_EXTENSION, "Not found Extension.js");
360         return nullptr;
361     }
362 
363     ::napi_value obj = jsObj->GetNapiValue();
364     if (obj == nullptr) {
365         LBSLOGE(FENCE_EXTENSION, "Failed to get Extension object");
366         return nullptr;
367     }
368 
369     ::napi_env env = jsRuntime.GetNapiEnv();
370     ::napi_value method;
371     ::napi_status ret = ::napi_get_named_property(env, obj, name.c_str(), &method);
372     if (ret != ::napi_status::napi_ok) {
373         LBSLOGE(FENCE_EXTENSION, "napi get name fail(%{public}d)", ret);
374         return nullptr;
375     }
376     ::napi_valuetype type = ::napi_valuetype::napi_undefined;
377     ::napi_valuetype functionType = ::napi_valuetype::napi_function;
378     ::napi_status retType = ::napi_typeof(env, method, &functionType);
379     if (retType != ::napi_status::napi_ok) {
380         LBSLOGE(FENCE_EXTENSION, "Parse napi object type fail(%{public}d)", retType);
381         return nullptr;
382     }
383     return method;
384 }
385 }  // namespace Location
386 }  // namespace OHOS