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_startup_task.h"
17
18 #include "event_report.h"
19 #include "hilog_tag_wrapper.h"
20 #include "js_runtime_utils.h"
21 #include "js_startup_task_executor.h"
22
23 namespace OHOS {
24 namespace AbilityRuntime {
25 namespace {
26 constexpr size_t ARGC_TWO = 2;
27 constexpr int32_t INDEX_ZERO = 0;
28 constexpr int32_t INDEX_ONE = 1;
29
30 class AsyncTaskCallBack {
31 public:
32 AsyncTaskCallBack() = default;
33 ~AsyncTaskCallBack() = default;
34
AsyncTaskCompleted(napi_env env,napi_callback_info info)35 static napi_value AsyncTaskCompleted(napi_env env, napi_callback_info info)
36 {
37 TAG_LOGD(AAFwkTag::STARTUP, "called");
38 size_t argc = ARGC_TWO;
39 napi_value argv[ARGC_TWO] = { nullptr };
40 napi_value thisVar = nullptr;
41 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
42
43 std::string startupName;
44 if (!ConvertFromJsValue(env, argv[INDEX_ZERO], startupName)) {
45 TAG_LOGE(AAFwkTag::STARTUP, "Convert error");
46 return CreateJsUndefined(env);
47 }
48
49 napi_value resultJs = argv[INDEX_ONE];
50 napi_ref resultRef = nullptr;
51 napi_create_reference(env, resultJs, INDEX_ONE, &resultRef);
52 std::shared_ptr<NativeReference> result(reinterpret_cast<NativeReference*>(resultRef));
53 std::shared_ptr<StartupTaskResult> callbackResult = std::make_shared<JsStartupTaskResult>(result);
54
55 std::shared_ptr<JsStartupTask> jsStartupTask;
56 for (const auto& iter : jsStartupTaskObjects_) {
57 if (iter.first == startupName) {
58 jsStartupTask = iter.second.lock();
59 }
60 }
61
62 if (jsStartupTask != nullptr) {
63 jsStartupTask->OnAsyncTaskCompleted(callbackResult);
64 jsStartupTaskObjects_.erase(startupName);
65 }
66
67 return CreateJsUndefined(env);
68 }
69
Constructor(napi_env env,napi_callback_info cbinfo)70 static napi_value Constructor(napi_env env, napi_callback_info cbinfo)
71 {
72 TAG_LOGD(AAFwkTag::STARTUP, "called");
73 return CreateJsUndefined(env);
74 }
75
76 static std::map<std::string, std::weak_ptr<JsStartupTask>> jsStartupTaskObjects_;
77 };
78 std::map<std::string, std::weak_ptr<JsStartupTask>> AsyncTaskCallBack::jsStartupTaskObjects_;
79 }
80
81 const std::string JsStartupTask::TASK_TYPE = "Js";
82
JsStartupTask(const std::string & name,JsRuntime & jsRuntime,const StartupTaskInfo & info,const std::shared_ptr<NativeReference> & contextJsRef)83 JsStartupTask::JsStartupTask(const std::string &name, JsRuntime &jsRuntime, const StartupTaskInfo &info,
84 const std::shared_ptr<NativeReference> &contextJsRef)
85 : AppStartupTask(name), jsRuntime_(jsRuntime), startupJsRef_(nullptr), contextJsRef_(contextJsRef),
86 srcEntry_(info.srcEntry), ohmUrl_(info.ohmUrl), hapPath_(info.hapPath), esModule_(info.esModule)
87 {
88 SetModuleName(info.moduleName);
89 }
90
JsStartupTask(const std::string & name,JsRuntime & jsRuntime,std::unique_ptr<NativeReference> & startupJsRef,std::shared_ptr<NativeReference> & contextJsRef)91 JsStartupTask::JsStartupTask(const std::string& name, JsRuntime& jsRuntime,
92 std::unique_ptr<NativeReference>& startupJsRef, std::shared_ptr<NativeReference>& contextJsRef)
93 : AppStartupTask(name), jsRuntime_(jsRuntime), startupJsRef_(std::move(startupJsRef)), contextJsRef_(contextJsRef)
94 {
95 }
96
97 JsStartupTask::~JsStartupTask() = default;
98
GetType() const99 const std::string &JsStartupTask::GetType() const
100 {
101 return TASK_TYPE;
102 }
103
RunTaskInit(std::unique_ptr<StartupTaskResultCallback> callback)104 int32_t JsStartupTask::RunTaskInit(std::unique_ptr<StartupTaskResultCallback> callback)
105 {
106 TAG_LOGI(AAFwkTag::STARTUP, "task: %{public}s init", GetName().c_str());
107 if (startupJsRef_ == nullptr) {
108 int32_t result = LoadJsOhmUrl();
109 if (result != ERR_OK) {
110 return result;
111 }
112 }
113 if (callCreateOnMainThread_) {
114 return JsStartupTaskExecutor::RunOnMainThread(jsRuntime_, startupJsRef_, contextJsRef_, std::move(callback));
115 }
116 AAFwk::EventInfo eventInfo;
117 if (LoadJsAsyncTaskExecutor() != ERR_OK) {
118 TAG_LOGE(AAFwkTag::STARTUP, "LoadJsAsyncTaskExecutor failed");
119 eventInfo.errCode = NAPI_CREATE_OBJECT_FAILED;
120 eventInfo.errReason = "LoadJsAsyncTaskExecutor failed";
121 AAFwk::EventReport::SendLaunchFrameworkEvent(
122 AAFwk::EventName::STARTUP_TASK_ERROR, HiSysEventType::FAULT, eventInfo);
123 return ERR_STARTUP_INTERNAL_ERROR;
124 }
125 LoadJsAsyncTaskCallback();
126
127 startupTaskResultCallback_ = std::move(callback);
128
129 auto result = JsStartupTaskExecutor::RunOnTaskPool(
130 jsRuntime_, startupJsRef_, contextJsRef_, AsyncTaskExecutorJsRef_, AsyncTaskExecutorCallbackJsRef_, name_);
131 if (result == ERR_OK) {
132 AsyncTaskCallBack::jsStartupTaskObjects_.emplace(name_,
133 std::static_pointer_cast<JsStartupTask>(shared_from_this()));
134 }
135 return result;
136 }
137
LoadJsOhmUrl()138 int32_t JsStartupTask::LoadJsOhmUrl()
139 {
140 TAG_LOGD(AAFwkTag::STARTUP, "call");
141 if (srcEntry_.empty() && ohmUrl_.empty()) {
142 TAG_LOGE(AAFwkTag::STARTUP, "srcEntry and ohmUrl empty");
143 return ERR_STARTUP_INTERNAL_ERROR;
144 }
145
146 std::string moduleNameWithStartupTask = moduleName_ + "::startupTask";
147 std::string srcPath(moduleName_ + "/" + srcEntry_);
148 auto pos = srcPath.rfind('.');
149 if (pos == std::string::npos) {
150 TAG_LOGE(AAFwkTag::STARTUP, "invalid srcEntry");
151 return ERR_STARTUP_INTERNAL_ERROR;
152 }
153 srcPath.erase(pos);
154 srcPath.append(".abc");
155 startupJsRef_ = jsRuntime_.LoadModule(moduleNameWithStartupTask, srcPath, hapPath_, esModule_, false, ohmUrl_);
156 if (startupJsRef_ == nullptr) {
157 TAG_LOGE(AAFwkTag::STARTUP, "startup task null");
158 return ERR_STARTUP_INTERNAL_ERROR;
159 }
160 return ERR_OK;
161 }
162
LoadJsAsyncTaskExecutor()163 int32_t JsStartupTask::LoadJsAsyncTaskExecutor()
164 {
165 TAG_LOGD(AAFwkTag::STARTUP, "called");
166 HandleScope handleScope(jsRuntime_);
167 auto env = jsRuntime_.GetNapiEnv();
168
169 napi_value object = nullptr;
170 napi_create_object(env, &object);
171 if (object == nullptr) {
172 TAG_LOGE(AAFwkTag::STARTUP, "null object");
173 return ERR_STARTUP_INTERNAL_ERROR;
174 }
175
176 AsyncTaskExecutorJsRef_ =
177 JsRuntime::LoadSystemModuleByEngine(env, "app.appstartup.AsyncTaskExcutor", &object, 1);
178 return ERR_OK;
179 }
180
LoadJsAsyncTaskCallback()181 void JsStartupTask::LoadJsAsyncTaskCallback()
182 {
183 TAG_LOGD(AAFwkTag::STARTUP, "called");
184 HandleScope handleScope(jsRuntime_);
185 auto env = jsRuntime_.GetNapiEnv();
186
187 napi_value config;
188 std::string value = "This is callback value";
189 NAPI_CALL_RETURN_VOID(
190 env, napi_create_string_utf8(env, value.c_str(), value.length(), &config));
191
192 napi_property_descriptor props[] = {
193 DECLARE_NAPI_STATIC_FUNCTION("onAsyncTaskCompleted", AsyncTaskCallBack::AsyncTaskCompleted),
194 DECLARE_NAPI_INSTANCE_PROPERTY("config", config),
195 };
196 napi_value asyncTaskCallbackClass = nullptr;
197 napi_define_sendable_class(env, "AsyncTaskCallback", NAPI_AUTO_LENGTH, AsyncTaskCallBack::Constructor,
198 nullptr, sizeof(props) / sizeof(props[0]), props, nullptr, &asyncTaskCallbackClass);
199 AsyncTaskExecutorCallbackJsRef_ =
200 JsRuntime::LoadSystemModuleByEngine(env, "app.appstartup.AsyncTaskCallback", &asyncTaskCallbackClass, 1);
201 }
202
OnAsyncTaskCompleted(const std::shared_ptr<StartupTaskResult> & result)203 void JsStartupTask::OnAsyncTaskCompleted(const std::shared_ptr<StartupTaskResult> &result)
204 {
205 TAG_LOGD(AAFwkTag::STARTUP, "called");
206 if (startupTaskResultCallback_ == nullptr) {
207 TAG_LOGE(AAFwkTag::STARTUP, "null startupTaskResultCallback");
208 return;
209 }
210 startupTaskResultCallback_->Call(result);
211 }
212
UpdateContextRef(std::shared_ptr<NativeReference> & contextJsRef)213 void JsStartupTask::UpdateContextRef(std::shared_ptr<NativeReference> &contextJsRef)
214 {
215 contextJsRef_ = contextJsRef;
216 }
217
RunTaskOnDependencyCompleted(const std::string & dependencyName,const std::shared_ptr<StartupTaskResult> & result)218 int32_t JsStartupTask::RunTaskOnDependencyCompleted(const std::string &dependencyName,
219 const std::shared_ptr<StartupTaskResult> &result)
220 {
221 HandleScope handleScope(jsRuntime_);
222 auto env = jsRuntime_.GetNapiEnv();
223
224 if (startupJsRef_ == nullptr) {
225 int32_t result = LoadJsOhmUrl();
226 if (result != ERR_OK) {
227 return result;
228 }
229 }
230 napi_value startupValue = startupJsRef_->GetNapiValue();
231 if (!CheckTypeForNapiValue(env, startupValue, napi_object)) {
232 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, not napi object", name_.c_str());
233 return ERR_STARTUP_INTERNAL_ERROR;
234 }
235 napi_value startupOnDepCompleted = nullptr;
236 napi_get_named_property(env, startupValue, "onDependencyCompleted", &startupOnDepCompleted);
237 if (startupOnDepCompleted == nullptr) {
238 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, get onDependencyCompleted failed", name_.c_str());
239 return ERR_STARTUP_FAILED_TO_EXECUTE_STARTUP;
240 }
241 bool isCallable = false;
242 napi_is_callable(env, startupOnDepCompleted, &isCallable);
243 if (!isCallable) {
244 TAG_LOGW(AAFwkTag::STARTUP, "onDependencyCompleted not callable:%{public}s", name_.c_str());
245 return ERR_STARTUP_FAILED_TO_EXECUTE_STARTUP;
246 }
247
248 napi_value jsResult = GetDependencyResult(env, dependencyName, result);
249 napi_value dependency = CreateJsValue(env, dependencyName);
250 constexpr size_t argc = 2;
251 napi_value argv[argc] = { dependency, jsResult };
252 napi_call_function(env, startupValue, startupOnDepCompleted, argc, argv, nullptr);
253 return ERR_OK;
254 }
255
GetDependencyResult(napi_env env,const std::string & dependencyName,const std::shared_ptr<StartupTaskResult> & result)256 napi_value JsStartupTask::GetDependencyResult(napi_env env, const std::string &dependencyName,
257 const std::shared_ptr<StartupTaskResult> &result)
258 {
259 if (result == nullptr || result->GetResultType() != StartupTaskResult::ResultType::JS) {
260 return CreateJsUndefined(env);
261 } else {
262 std::shared_ptr<JsStartupTaskResult> jsResultPtr = std::static_pointer_cast<JsStartupTaskResult>(result);
263 if (jsResultPtr == nullptr) {
264 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, convert failed", dependencyName.c_str());
265 return CreateJsUndefined(env);
266 }
267 std::shared_ptr<NativeReference> jsResultRef = jsResultPtr->GetJsStartupResultRef();
268 if (jsResultRef == nullptr) {
269 return CreateJsUndefined(env);
270 }
271 return jsResultRef->GetNapiValue();
272 }
273 }
274 } // namespace AbilityRuntime
275 } // namespace OHOS
276