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(moduleName,
137 srcPath,
138 abilityInfo_->hapPath,
139 abilityInfo_->compileMode == CompileMode::ES_MODULE,
140 false,
141 abilityInfo_->srcEntrance);
142 if (jsObj_ == nullptr) {
143 LBSLOGE(FENCE_EXTENSION, "Failed to load ability module");
144 return;
145 }
146 napi_value obj = jsObj_->GetNapiValue();
147 if (obj == nullptr) {
148 LBSLOGE(FENCE_EXTENSION, "Failed to get extension object");
149 return;
150 }
151 napi_env env = jsRuntime_.GetNapiEnv();
152
153 LBSLOGI(FENCE_EXTENSION, "Init end");
154 BindContext(env, obj);
155 }
156
BindContext(const::napi_env & env,const::napi_value & obj)157 void JsFenceExtension::BindContext(const ::napi_env &env, const ::napi_value &obj)
158 {
159 LBSLOGI(FENCE_EXTENSION, "BindContext start");
160 auto context = GetContext();
161 if (context == nullptr) {
162 LBSLOGE(FENCE_EXTENSION, "Failed to get context");
163 return;
164 }
165 napi_value contextObj = JsFenceExtensionContext::CreateJsFenceExtensionContext(env, context);
166 auto shellContextRef_ =
167 AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env, CONTEXT_MODULE_PATH, &contextObj, ARGC_ONE);
168 if (shellContextRef_ == nullptr) {
169 LBSLOGE(FENCE_EXTENSION, "Failed to get shell context from system module");
170 return;
171 }
172 napi_value nativeObj = shellContextRef_->GetNapiValue();
173 if (nativeObj == nullptr) {
174 LBSLOGE(FENCE_EXTENSION, "Failed to load context module");
175 return;
176 }
177
178 auto workContext = new (std::nothrow) std::weak_ptr<FenceExtensionContext>(context);
179 if (workContext == nullptr) {
180 LBSLOGE(FENCE_EXTENSION, "invalid extension context");
181 return;
182 }
183 napi_coerce_to_native_binding_object(
184 env, nativeObj, AbilityRuntime::DetachCallbackFunc, AttachFenceExtensionContext, workContext, nullptr);
185 context->Bind(jsRuntime_, shellContextRef_.release());
186 napi_set_named_property(env, obj, "context", nativeObj);
187
188 napi_wrap(
189 env,
190 nativeObj,
191 workContext,
192 [](napi_env, void *data, void *) {
193 LBSLOGI(FENCE_EXTENSION, "Finalizer for weak_ptr extension context is called");
194 delete static_cast<std::weak_ptr<FenceExtensionContext> *>(data);
195 },
196 nullptr,
197 nullptr);
198 LBSLOGI(FENCE_EXTENSION, "BindContext end");
199 }
OnStart(const AAFwk::Want & want)200 void JsFenceExtension::OnStart(const AAFwk::Want &want)
201 {
202 AbilityRuntime::Extension::OnStart(want);
203 }
204
OnStop()205 void JsFenceExtension::OnStop()
206 {
207 LBSLOGI(FENCE_EXTENSION, "OnStop called");
208 AbilityRuntime::HandleScope handleScope(jsRuntime_);
209 if (jsObj_ == nullptr) {
210 LBSLOGE(FENCE_EXTENSION, "js fence extension obj is null");
211 return;
212 }
213 ::napi_value method = GetMethod(jsRuntime_, jsObj_, "onDestroy");
214 if (method == nullptr) {
215 LBSLOGE(FENCE_EXTENSION, "Call function method is null");
216 return;
217 }
218 napi_value argv[0];
219 ::napi_value undefined;
220 ::napi_env env = jsRuntime_.GetNapiEnv();
221 ::napi_value value = jsObj_->GetNapiValue();
222 ::napi_status retStatus = ::napi_call_function(env, value, method, 0, argv, &undefined);
223 if (retStatus != ::napi_status::napi_ok) {
224 LBSLOGE(FENCE_EXTENSION, "Call function error");
225 }
226 LBSLOGI(FENCE_EXTENSION, "Call onDestroy end");
227 AbilityRuntime::Extension::OnStop();
228 }
229
OnConnect(const AAFwk::Want & want)230 sptr<IRemoteObject> JsFenceExtension::OnConnect(const AAFwk::Want &want)
231 {
232 LBSLOGI(FENCE_EXTENSION, "OnConnect");
233 AbilityRuntime::Extension::OnConnect(want);
234 sptr<FenceExtensionStubImpl> remoteObject =
235 new (std::nothrow) FenceExtensionStubImpl(std::static_pointer_cast<JsFenceExtension>(shared_from_this()));
236 if (remoteObject == nullptr) {
237 LBSLOGE(FENCE_EXTENSION, "failed to create FenceExtensionStubImpl");
238 return nullptr;
239 }
240 return remoteObject->AsObject();
241 }
242
OnDisconnect(const AAFwk::Want & want)243 void JsFenceExtension::OnDisconnect(const AAFwk::Want &want)
244 {
245 LBSLOGI(FENCE_EXTENSION, "OnDisconnect");
246 Extension::OnDisconnect(want);
247 }
248
OnFenceStatusChange(std::map<std::string,std::string> extraData)249 FenceExtensionErrCode JsFenceExtension::OnFenceStatusChange(std::map<std::string, std::string> extraData)
250 {
251 LBSLOGI(FENCE_EXTENSION, "js fence extension:OnFenceStatusChange");
252 if (jsObj_ == nullptr) {
253 LBSLOGE(FENCE_EXTENSION, "js fence extension obj is null");
254 return FenceExtensionErrCode::EXTENSION_JS_OBJ_IS_NULL;
255 }
256
257 auto task = [=]() {
258 LBSLOGI(FENCE_EXTENSION, "call js function start");
259 JsFenceExtension::CallToUiThread(extraData);
260 };
261 if (handler_ == nullptr) {
262 LBSLOGE(FENCE_EXTENSION, "PostTask call js function start");
263 return FenceExtensionErrCode::EXTENSION_JS_CALL_FAILED;
264 }
265 handler_->PostTask(task, "FenceExtension OnFenceStatusChange Task");
266 LBSLOGI(FENCE_EXTENSION, "PostTask call js function start");
267 return FenceExtensionErrCode::EXTENSION_SUCCESS;
268 }
269
GetSrcPath(std::string & srcPath)270 void JsFenceExtension::GetSrcPath(std::string &srcPath)
271 {
272 if (!Extension::abilityInfo_->srcEntrance.empty()) {
273 srcPath.append(Extension::abilityInfo_->moduleName);
274 srcPath.append("/");
275 srcPath.append(Extension::abilityInfo_->srcEntrance);
276 srcPath.erase(srcPath.rfind('.'));
277 srcPath.append(".abc");
278 }
279 }
280
CallToUiThread(std::map<std::string,std::string> extraData)281 FenceExtensionErrCode JsFenceExtension::CallToUiThread(std::map<std::string, std::string> extraData)
282 {
283 LBSLOGI(FENCE_EXTENSION, "js fence extension:OnFenceStatusChange");
284 if (jsObj_ == nullptr) {
285 return FenceExtensionErrCode::EXTENSION_JS_OBJ_IS_NULL;
286 }
287 AbilityRuntime::HandleScope handleScope(jsRuntime_);
288 ::napi_value method = GetMethod(jsRuntime_, jsObj_, "onFenceStatusChange");
289 if (method == nullptr) {
290 return FenceExtensionErrCode::EXTENSION_JS_NOT_FOUND_METHOD;
291 }
292 auto fenceId = std::atoi(GetAndDeleteFromMap(extraData, EXTENSION_PARAM_KEY_FENCE_ID).c_str());
293 auto fenceEvent = std::atoi(GetAndDeleteFromMap(extraData, EXTENSION_PARAM_KEY_FENCE_EVENT).c_str());
294 ::napi_value transitionObj;
295 ::napi_env env = jsRuntime_.GetNapiEnv();
296 napi_status retTransition = ::napi_create_object(env, &transitionObj);
297 if (retTransition != napi_ok) {
298 return FenceExtensionErrCode::EXTENSION_JS_CREATE_PARAM_ERROR;
299 }
300 SetValueInt32(env, "geofenceId", fenceId, transitionObj);
301 SetValueInt32(env, "transitionEvent", static_cast<int>(fenceEvent), transitionObj);
302 ::napi_value addtionsRecord;
303 if (extraData.size() > 0) {
304 ::napi_status status = napi_create_object(env, &addtionsRecord);
305 for (const auto &pair : extraData) {
306 napi_value key, value;
307 status = napi_create_string_utf8(env, pair.first.c_str(), NAPI_AUTO_LENGTH, &key);
308 if (status != napi_ok) {
309 break;
310 }
311 status = napi_create_string_utf8(env, pair.second.c_str(), NAPI_AUTO_LENGTH, &value);
312 if (status != napi_ok) {
313 break;
314 }
315 status = napi_set_property(env, addtionsRecord, key, value);
316 if (status != napi_ok) {
317 break;
318 }
319 }
320 } else {
321 ::napi_status status = napi_get_undefined(env, &addtionsRecord);
322 }
323 ::napi_value argv[PARAM2];
324 argv[PARAM0] = transitionObj;
325 argv[PARAM1] = addtionsRecord;
326 ::napi_value abilityObj = jsObj_->GetNapiValue();
327 ::napi_value undefined;
328 ::napi_status callStatus = ::napi_call_function(env, abilityObj, method, ARGC_TWO, argv, &undefined);
329 return FenceExtensionErrCode::EXTENSION_SUCCESS;
330 }
331
SetValueUtf8String(const napi_env & env,const char * fieldStr,const char * str,napi_value & result)332 napi_status JsFenceExtension::SetValueUtf8String(
333 const napi_env &env, const char *fieldStr, const char *str, napi_value &result)
334 {
335 napi_value value = nullptr;
336 NAPI_CALL_BASE(env, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &value), napi_generic_failure);
337 NAPI_CALL_BASE(env, napi_set_named_property(env, result, fieldStr, value), napi_generic_failure);
338 return napi_ok;
339 }
SetValueInt32(const napi_env & env,const char * fieldStr,const int intValue,napi_value & result)340 napi_status JsFenceExtension::SetValueInt32(
341 const napi_env &env, const char *fieldStr, const int intValue, napi_value &result)
342 {
343 napi_value value = nullptr;
344 NAPI_CALL_BASE(env, napi_create_int32(env, intValue, &value), napi_generic_failure);
345 NAPI_CALL_BASE(env, napi_set_named_property(env, result, fieldStr, value), napi_generic_failure);
346 return napi_ok;
347 }
GetAndDeleteFromMap(std::map<std::string,std::string> & param,std::string key)348 std::string JsFenceExtension::GetAndDeleteFromMap(std::map<std::string, std::string> ¶m, std::string key)
349 {
350 auto mapItr = param.find(key);
351 std::string value;
352 if (mapItr != param.end()) {
353 value = mapItr->second;
354 param.erase(key);
355 }
356 return value;
357 }
358
GetMethod(AbilityRuntime::JsRuntime & jsRuntime,const std::unique_ptr<NativeReference> & jsObj,const std::string & name)359 ::napi_value JsFenceExtension::GetMethod(
360 AbilityRuntime::JsRuntime &jsRuntime, const std::unique_ptr<NativeReference> &jsObj, const std::string &name)
361 {
362 if (!jsObj) {
363 LBSLOGE(FENCE_EXTENSION, "Not found Extension.js");
364 return nullptr;
365 }
366
367 ::napi_value obj = jsObj->GetNapiValue();
368 if (obj == nullptr) {
369 LBSLOGE(FENCE_EXTENSION, "Failed to get Extension object");
370 return nullptr;
371 }
372
373 ::napi_env env = jsRuntime.GetNapiEnv();
374 ::napi_value method;
375 ::napi_status ret = ::napi_get_named_property(env, obj, name.c_str(), &method);
376 if (ret != ::napi_status::napi_ok) {
377 LBSLOGE(FENCE_EXTENSION, "napi get name fail(%{public}d)", ret);
378 return nullptr;
379 }
380 ::napi_valuetype type = ::napi_valuetype::napi_undefined;
381 ::napi_valuetype functionType = ::napi_valuetype::napi_function;
382 ::napi_status retType = ::napi_typeof(env, method, &functionType);
383 if (retType != ::napi_status::napi_ok) {
384 LBSLOGE(FENCE_EXTENSION, "Parse napi object type fail(%{public}d)", retType);
385 return nullptr;
386 }
387 return method;
388 }
389 } // namespace Location
390 } // namespace OHOS