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(), &parametersValue);
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