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> ¶m, 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