1 /*
2 * Copyright (c) 2022 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_work_scheduler_extension.h"
17
18 #include <string>
19
20 #include "runtime.h"
21 #include "js_runtime.h"
22 #include "js_runtime_utils.h"
23 #include "work_scheduler_extension.h"
24 #include "js_work_scheduler_extension_context.h"
25 #include "work_scheduler_stub_imp.h"
26
27 namespace OHOS {
28 namespace WorkScheduler {
29 const int32_t INVALID_VALUE = -1;
30
Create(const std::unique_ptr<AbilityRuntime::Runtime> & runtime)31 JsWorkSchedulerExtension* JsWorkSchedulerExtension::Create(const std::unique_ptr<AbilityRuntime::Runtime>& runtime)
32 {
33 return new JsWorkSchedulerExtension(static_cast<AbilityRuntime::JsRuntime&>(*runtime));
34 }
35
JsWorkSchedulerExtension(AbilityRuntime::JsRuntime & jsRuntime)36 JsWorkSchedulerExtension::JsWorkSchedulerExtension(AbilityRuntime::JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {}
~JsWorkSchedulerExtension()37 JsWorkSchedulerExtension::~JsWorkSchedulerExtension()
38 {
39 WS_HILOGD("Js WorkScheduler extension destructor.");
40 auto context = GetContext();
41 if (context) {
42 context->Unbind();
43 }
44
45 jsRuntime_.FreeNativeReference(std::move(jsObj_));
46 jsRuntime_.FreeNativeReference(std::move(shellContextRef_));
47 }
48
DetachCallbackFunc(napi_env env,void * value,void *)49 inline void *DetachCallbackFunc(napi_env env, void *value, void *)
50 {
51 return value;
52 }
53
AttachWorkSchedulerExtensionContext(napi_env env,void * value,void *)54 napi_value AttachWorkSchedulerExtensionContext(napi_env env, void *value, void *)
55 {
56 WS_HILOGI("AttachWorkSchedulerExtensionContext");
57 if (value == nullptr) {
58 WS_HILOGE("invalid parameter.");
59 return nullptr;
60 }
61 auto ptr = reinterpret_cast<std::weak_ptr<WorkSchedulerExtensionContext> *>(value)->lock();
62 if (ptr == nullptr) {
63 WS_HILOGE("invalid context.");
64 return nullptr;
65 }
66 napi_value object = CreateJsWorkSchedulerExtensionContext(env, ptr);
67 napi_value contextObj = AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env,
68 "application.WorkSchedulerExtensionContext", &object, 1)->GetNapiValue();
69 napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc,
70 AttachWorkSchedulerExtensionContext, value, nullptr);
71 auto workContext = new (std::nothrow) std::weak_ptr<WorkSchedulerExtensionContext>(ptr);
72 if (workContext == nullptr) {
73 WS_HILOGE("init WorkSchedulerExtensionContext failed.");
74 return nullptr;
75 }
76 napi_status status = napi_wrap(env, contextObj, workContext,
77 [](napi_env env, void *data, void *) {
78 WS_HILOGI("Finalizer for weak_ptr WorkSchedulerExtensionContext is called");
79 delete static_cast<std::weak_ptr<WorkSchedulerExtensionContext> *>(data);
80 }, nullptr, nullptr);
81 if (status != napi_ok) {
82 WS_HILOGE("WorkSchedulerExtension failed to wrap the context");
83 return nullptr;
84 }
85
86 return contextObj;
87 }
88
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)89 void JsWorkSchedulerExtension::Init(const std::shared_ptr<AppExecFwk::AbilityLocalRecord>& record,
90 const std::shared_ptr<AppExecFwk::OHOSApplication>& application,
91 std::shared_ptr<AppExecFwk::AbilityHandler>& handler,
92 const sptr<IRemoteObject>& token)
93 {
94 WS_HILOGD("enter");
95 WorkSchedulerExtension::Init(record, application, handler, token);
96 std::string srcPath = "";
97 GetSrcPath(srcPath);
98 if (srcPath.empty()) {
99 WS_HILOGE("JsWorkSchedulerExtension Failed to get srcPath");
100 return;
101 }
102
103 std::string moduleName(Extension::abilityInfo_->moduleName);
104 moduleName.append("::").append(abilityInfo_->name);
105 WS_HILOGD("moduleName:%{public}s, srcPath:%{private}s.", moduleName.c_str(), srcPath.c_str());
106 AbilityRuntime::HandleScope handleScope(jsRuntime_);
107 napi_env env = jsRuntime_.GetNapiEnv();
108
109 jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath,
110 abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
111 if (jsObj_ == nullptr) {
112 WS_HILOGE("WorkSchedulerExtension Failed to get jsObj_");
113 return;
114 }
115 napi_value obj = jsObj_->GetNapiValue();
116 if (obj == nullptr) {
117 WS_HILOGE("WorkSchedulerExtension Failed to get JsWorkSchedulerExtension object");
118 return;
119 }
120 BindContext(env, obj);
121
122 WS_HILOGD("end.");
123 }
124
BindContext(napi_env env,napi_value obj)125 void JsWorkSchedulerExtension::BindContext(napi_env env, napi_value obj)
126 {
127 auto context = GetContext();
128 if (context == nullptr) {
129 WS_HILOGE("WorkSchedulerExtension Failed to get context");
130 return;
131 }
132 napi_value contextObj = CreateJsWorkSchedulerExtensionContext(env, context);
133 shellContextRef_ = jsRuntime_.LoadSystemModule("application.WorkSchedulerExtensionContext",
134 &contextObj, 1);
135 if (shellContextRef_ == nullptr) {
136 WS_HILOGE("WorkSchedulerExtension Failed to get shellContextRef_");
137 return;
138 }
139 contextObj = shellContextRef_->GetNapiValue();
140
141 auto workContext = new (std::nothrow) std::weak_ptr<WorkSchedulerExtensionContext>(context);
142 if (workContext == nullptr) {
143 WS_HILOGE("init WorkSchedulerExtensionContext failed.");
144 return;
145 }
146 napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc,
147 AttachWorkSchedulerExtensionContext, workContext, nullptr);
148 WS_HILOGI("JsWorkSchedulerExtension init bind and set property.");
149 context->Bind(jsRuntime_, shellContextRef_.get());
150 napi_set_named_property(env, obj, "context", contextObj);
151 WS_HILOGI("Set JsWorkSchedulerExtension context pointer is nullptr or not:%{public}d",
152 context.get() == nullptr);
153
154 napi_status status = napi_wrap(env, contextObj, workContext,
155 [](napi_env env, void* data, void*) {
156 WS_HILOGI("Finalizer for weak_ptr WorkSchedulerExtensionContext is called");
157 delete static_cast<std::weak_ptr<WorkSchedulerExtensionContext> *>(data);
158 }, nullptr, nullptr);
159 if (status != napi_ok) {
160 WS_HILOGE("WorkSchedulerExtension failed to wrap the context");
161 }
162 }
163
OnStart(const AAFwk::Want & want)164 void JsWorkSchedulerExtension::OnStart(const AAFwk::Want& want)
165 {
166 WS_HILOGD("begin");
167 AbilityRuntime::Extension::OnStart(want);
168 }
169
OnStop()170 void JsWorkSchedulerExtension::OnStop()
171 {
172 AbilityRuntime::Extension::OnStop();
173 WS_HILOGD("end.");
174 }
175
OnConnect(const AAFwk::Want & want)176 __attribute__((no_sanitize("cfi"))) sptr<IRemoteObject> JsWorkSchedulerExtension::OnConnect(const AAFwk::Want& want)
177 {
178 AbilityRuntime::Extension::OnConnect(want);
179 WS_HILOGD("begin.");
180 sptr<WorkSchedulerStubImp> remoteObject = new (std::nothrow) WorkSchedulerStubImp(
181 std::static_pointer_cast<JsWorkSchedulerExtension>(shared_from_this()));
182 if (remoteObject == nullptr) {
183 WS_HILOGE("OnConnect get null");
184 return remoteObject;
185 }
186 WS_HILOGD("end.");
187 return remoteObject->AsObject();
188 }
189
OnDisconnect(const AAFwk::Want & want)190 void JsWorkSchedulerExtension::OnDisconnect(const AAFwk::Want& want)
191 {
192 WS_HILOGD("begin.");
193 AbilityRuntime::Extension::OnDisconnect(want);
194 }
195
SetCommonInfo(napi_env env,napi_value workInfoData,int32_t workId,const std::string & bundleName,const std::string & abilityName)196 void SetCommonInfo(napi_env env, napi_value workInfoData, int32_t workId,
197 const std::string& bundleName, const std::string& abilityName)
198 {
199 napi_value workIdValue;
200 napi_create_int32(env, workId, &workIdValue);
201 napi_set_named_property(env, workInfoData, "workId", workIdValue);
202
203 napi_value bundleNameValue;
204 napi_create_string_utf8(env, bundleName.c_str(), bundleName.size(), &bundleNameValue);
205 napi_set_named_property(env, workInfoData, "bundleName", bundleNameValue);
206
207 napi_value abilityNameValue;
208 napi_create_string_utf8(env, abilityName.c_str(), abilityName.size(), &abilityNameValue);
209 napi_set_named_property(env, workInfoData, "abilityName", abilityNameValue);
210 }
211
SetPersistedInfo(napi_env env,napi_value workInfoData,bool isPersisted)212 void SetPersistedInfo(napi_env env, napi_value workInfoData, bool isPersisted)
213 {
214 napi_value isPersistedValue;
215 napi_get_boolean(env, isPersisted, &isPersistedValue);
216 napi_set_named_property(env, workInfoData, "isPersisted", isPersistedValue);
217 }
218
SetExtrasInfo(napi_env env,napi_value workInfoData,bool getExtrasRet,const std::string & extrasStr)219 void SetExtrasInfo(napi_env env, napi_value workInfoData, bool getExtrasRet, const std::string& extrasStr)
220 {
221 if (getExtrasRet) {
222 napi_value parametersValue;
223 napi_create_string_utf8(env, extrasStr.c_str(), extrasStr.size(), ¶metersValue);
224 napi_set_named_property(env, workInfoData, "parameters", parametersValue);
225 }
226 }
227
SetNetWorkInfo(napi_env env,napi_value workInfoData,WorkCondition::Network networkType)228 void SetNetWorkInfo(napi_env env, napi_value workInfoData, WorkCondition::Network networkType)
229 {
230 if (networkType != WorkCondition::Network::NETWORK_UNKNOWN) {
231 napi_value networkTypeValue;
232 napi_create_int32(env, networkType, &networkTypeValue);
233 napi_set_named_property(env, workInfoData, "networkType", networkTypeValue);
234 }
235 }
236
SetChargerTypeInfo(napi_env env,napi_value workInfoData,WorkCondition::Charger charger)237 void SetChargerTypeInfo(napi_env env, napi_value workInfoData, WorkCondition::Charger charger)
238 {
239 if (charger != WorkCondition::Charger::CHARGING_UNKNOWN) {
240 if (charger == WorkCondition::Charger::CHARGING_UNPLUGGED) {
241 napi_value isChargingValue;
242 napi_get_boolean(env, false, &isChargingValue);
243 napi_set_named_property(env, workInfoData, "isCharging", isChargingValue);
244 } else {
245 napi_value isChargingValue;
246 napi_get_boolean(env, true, &isChargingValue);
247 napi_set_named_property(env, workInfoData, "isCharging", isChargingValue);
248
249 napi_value chargerTypeValue;
250 napi_create_int32(env, charger, &chargerTypeValue);
251 napi_set_named_property(env, workInfoData, "chargerType", chargerTypeValue);
252 }
253 }
254 }
255
SetBatteryInfo(napi_env env,napi_value workInfoData,int32_t batteryLevel,WorkCondition::BatteryStatus batteryStatus)256 void SetBatteryInfo(napi_env env, napi_value workInfoData, int32_t batteryLevel,
257 WorkCondition::BatteryStatus batteryStatus)
258 {
259 if (batteryLevel != INVALID_VALUE) {
260 napi_value batteryLevelValue;
261 napi_create_int32(env, batteryLevel, &batteryLevelValue);
262 napi_set_named_property(env, workInfoData, "batteryLevel", batteryLevelValue);
263 }
264 if (batteryStatus != WorkCondition::BatteryStatus::BATTERY_UNKNOWN) {
265 napi_value batteryStatusValue;
266 napi_create_int32(env, batteryStatus, &batteryStatusValue);
267 napi_set_named_property(env, workInfoData, "batteryStatus", batteryStatusValue);
268 }
269 }
270
SetStorageInfo(napi_env env,napi_value workInfoData,WorkCondition::Storage storageLevel)271 void SetStorageInfo(napi_env env, napi_value workInfoData, WorkCondition::Storage storageLevel)
272 {
273 if (storageLevel != WorkCondition::Storage::STORAGE_UNKNOWN) {
274 napi_value storageLevelValue;
275 napi_create_int32(env, storageLevel, &storageLevelValue);
276 napi_set_named_property(env, workInfoData, "storageRequest", storageLevelValue);
277 }
278 }
279
SetRepeatInfo(napi_env env,napi_value workInfoData,bool isRepeat,uint32_t timeInterval,int32_t cycleCount)280 void SetRepeatInfo(napi_env env, napi_value workInfoData, bool isRepeat,
281 uint32_t timeInterval, int32_t cycleCount)
282 {
283 if (isRepeat) {
284 napi_value isRepeatValue;
285 napi_get_boolean(env, true, &isRepeatValue);
286 napi_set_named_property(env, workInfoData, "isRepeat", isRepeatValue);
287
288 napi_value repeatCycleTimeValue;
289 napi_create_uint32(env, timeInterval, &repeatCycleTimeValue);
290 napi_set_named_property(env, workInfoData, "repeatCycleTime", repeatCycleTimeValue);
291 } else {
292 napi_value repeatCycleTimeValue;
293 napi_create_uint32(env, timeInterval, &repeatCycleTimeValue);
294 napi_set_named_property(env, workInfoData, "repeatCycleTime", repeatCycleTimeValue);
295
296 napi_value repeatCountValue;
297 napi_create_int32(env, cycleCount, &repeatCountValue);
298 napi_set_named_property(env, workInfoData, "repeatCount", repeatCountValue);
299 }
300 }
301
SetDeepIdleInfo(napi_env env,napi_value workInfoData,WorkCondition::DeepIdle value)302 void SetDeepIdleInfo(napi_env env, napi_value workInfoData, WorkCondition::DeepIdle value)
303 {
304 if (value == WorkCondition::DeepIdle::DEEP_IDLE_UNKNOWN) {
305 return;
306 }
307 napi_value isDeepIdle;
308 napi_get_boolean(env, (value == WorkCondition::DeepIdle::DEEP_IDLE_IN), &isDeepIdle);
309 napi_set_named_property(env, workInfoData, "isDeepIdle", isDeepIdle);
310 }
311
CallFuncation(napi_env env,napi_value workInfoData,std::unique_ptr<NativeReference> & jsObj_,const char * functionName)312 bool CallFuncation(napi_env env, napi_value workInfoData,
313 std::unique_ptr<NativeReference> &jsObj_, const char* functionName)
314 {
315 napi_value argv[] = {workInfoData};
316 if (!jsObj_) {
317 WS_HILOGE("WorkSchedulerExtension Not found js");
318 return false;
319 }
320
321 napi_value value = jsObj_->GetNapiValue();
322 if (value == nullptr) {
323 WS_HILOGE("WorkSchedulerExtension Failed to get WorkSchedulerExtension object");
324 return false;
325 }
326
327 napi_value method;
328 napi_get_named_property(env, value, functionName, &method);
329 if (method == nullptr) {
330 WS_HILOGE("WorkSchedulerExtension call function %{public}s error", functionName);
331 return false;
332 }
333
334 napi_value callFunctionResult;
335 if (napi_call_function(env, value, method, 1, argv, &callFunctionResult) != napi_ok) {
336 WS_HILOGE("WorkSchedulerExtension call funcation onWorkStart error");
337 return false;
338 }
339
340 return true;
341 }
342
OnWorkStart(WorkInfo & workInfo)343 void JsWorkSchedulerExtension::OnWorkStart(WorkInfo& workInfo)
344 {
345 if (handler_ == nullptr) {
346 return;
347 }
348 WS_HILOGD("begin.");
349 int32_t workId = workInfo.GetWorkId();
350 std::string bundleName = workInfo.GetBundleName();
351 std::string abilityName = workInfo.GetAbilityName();
352 bool isPersisted = workInfo.IsPersisted();
353 WorkCondition::Network networkType = workInfo.GetNetworkType();
354 WorkCondition::Charger charger = workInfo.GetChargerType();
355 int32_t batteryLevel = workInfo.GetBatteryLevel();
356 WorkCondition::BatteryStatus batteryStatus = workInfo.GetBatteryStatus();
357 WorkCondition::Storage storageLevel = workInfo.GetStorageLevel();
358 uint32_t timeInterval = workInfo.GetTimeInterval();
359 bool isRepeat = workInfo.IsRepeat();
360 int32_t cycleCount = workInfo.GetCycleCount();
361 WorkCondition::DeepIdle deepIdleValue = workInfo.GetDeepIdle();
362 std::string extrasStr;
363 bool getExtrasRet = GetExtrasJsonStr(workInfo, extrasStr);
364 WorkSchedulerExtension::OnWorkStart(workInfo);
365 auto task = [=]() {
366 AbilityRuntime::HandleScope handleScope(jsRuntime_);
367 napi_env env = jsRuntime_.GetNapiEnv();
368
369 napi_value workInfoData;
370 if (napi_create_object(env, &workInfoData) != napi_ok) {
371 WS_HILOGE("WorkSchedulerExtension failed to create workInfoData OnWorkStart");
372 return;
373 }
374
375 SetCommonInfo(env, workInfoData, workId, bundleName, abilityName);
376 SetExtrasInfo(env, workInfoData, getExtrasRet, extrasStr);
377 SetPersistedInfo(env, workInfoData, isPersisted);
378 SetNetWorkInfo(env, workInfoData, networkType);
379 SetChargerTypeInfo(env, workInfoData, charger);
380 SetBatteryInfo(env, workInfoData, batteryLevel, batteryStatus);
381 SetStorageInfo(env, workInfoData, storageLevel);
382 SetDeepIdleInfo(env, workInfoData, deepIdleValue);
383
384 if (timeInterval > 0) {
385 SetRepeatInfo(env, workInfoData, isRepeat, timeInterval, cycleCount);
386 }
387
388 if (!CallFuncation(env, workInfoData, jsObj_, "onWorkStart")) {
389 return;
390 }
391 };
392 handler_->PostTask(task);
393 }
394
OnWorkStop(WorkInfo & workInfo)395 void JsWorkSchedulerExtension::OnWorkStop(WorkInfo& workInfo)
396 {
397 if (handler_ == nullptr) {
398 return;
399 }
400 WS_HILOGD("begin.");
401 int32_t workId = workInfo.GetWorkId();
402 std::string bundleName = workInfo.GetBundleName();
403 std::string abilityName = workInfo.GetAbilityName();
404 bool isPersisted = workInfo.IsPersisted();
405 WorkCondition::Network networkType = workInfo.GetNetworkType();
406 WorkCondition::Charger charger = workInfo.GetChargerType();
407 int32_t batteryLevel = workInfo.GetBatteryLevel();
408 WorkCondition::BatteryStatus batteryStatus = workInfo.GetBatteryStatus();
409 WorkCondition::Storage storageLevel = workInfo.GetStorageLevel();
410 uint32_t timeInterval = workInfo.GetTimeInterval();
411 bool isRepeat = workInfo.IsRepeat();
412 int32_t cycleCount = workInfo.GetCycleCount();
413 WorkCondition::DeepIdle deepIdleValue = workInfo.GetDeepIdle();
414 std::string extrasStr;
415 bool getExtrasRet = GetExtrasJsonStr(workInfo, extrasStr);
416 WorkSchedulerExtension::OnWorkStop(workInfo);
417 auto task = [=]() {
418 AbilityRuntime::HandleScope handleScope(jsRuntime_);
419 napi_env env = jsRuntime_.GetNapiEnv();
420
421 napi_value workInfoData;
422 if (napi_create_object(env, &workInfoData) != napi_ok) {
423 WS_HILOGE("WorkSchedulerExtension failed to create workInfoData OnWorkStop");
424 return;
425 }
426
427 SetCommonInfo(env, workInfoData, workId, bundleName, abilityName);
428 SetExtrasInfo(env, workInfoData, getExtrasRet, extrasStr);
429 SetPersistedInfo(env, workInfoData, isPersisted);
430 SetNetWorkInfo(env, workInfoData, networkType);
431 SetChargerTypeInfo(env, workInfoData, charger);
432 SetBatteryInfo(env, workInfoData, batteryLevel, batteryStatus);
433 SetStorageInfo(env, workInfoData, storageLevel);
434 SetDeepIdleInfo(env, workInfoData, deepIdleValue);
435
436 if (timeInterval > 0) {
437 SetRepeatInfo(env, workInfoData, isRepeat, timeInterval, cycleCount);
438 }
439
440 if (!CallFuncation(env, workInfoData, jsObj_, "onWorkStop")) {
441 return;
442 }
443 };
444 handler_->PostTask(task);
445 }
446
GetSrcPath(std::string & srcPath)447 void JsWorkSchedulerExtension::GetSrcPath(std::string &srcPath)
448 {
449 if (!Extension::abilityInfo_->isStageBasedModel) {
450 /* temporary compatibility api8 + config.json */
451 srcPath.append(Extension::abilityInfo_->package);
452 srcPath.append("/assets/js/");
453 if (!Extension::abilityInfo_->srcPath.empty()) {
454 srcPath.append(Extension::abilityInfo_->srcPath);
455 }
456 srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
457 return;
458 }
459
460 if (!Extension::abilityInfo_->srcEntrance.empty()) {
461 srcPath.append(Extension::abilityInfo_->moduleName + "/");
462 srcPath.append(Extension::abilityInfo_->srcEntrance);
463 srcPath.erase(srcPath.rfind('.'));
464 srcPath.append(".abc");
465 }
466 }
467
GetExtrasJsonStr(const WorkInfo & workInfo,std::string & extrasStr)468 bool JsWorkSchedulerExtension::GetExtrasJsonStr(const WorkInfo& workInfo, std::string& extrasStr)
469 {
470 std::shared_ptr<AAFwk::WantParams> extras = workInfo.GetExtras();
471 Json::Value extrasJson;
472 if (!extras) {
473 WS_HILOGD("parameter is null.");
474 return false;
475 }
476 auto extrasMap = extras->GetParams();
477 int typeId = INVALID_VALUE;
478 for (auto it : extrasMap) {
479 typeId = AAFwk::WantParams::GetDataType(it.second);
480 if (typeId != INVALID_VALUE) {
481 std::string value = AAFwk::WantParams::GetStringByType(it.second, typeId);
482 extrasJson[it.first] = value;
483 } else {
484 WS_HILOGE("parameters type not supported.");
485 }
486 }
487 Json::StreamWriterBuilder builder;
488 extrasStr = Json::writeString(builder, extrasJson);
489 return true;
490 }
491 } // namespace WorkScheduler
492 } // namespace OHOS
493