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 "hilog_tag_wrapper.h"
19 #include "js_runtime_utils.h"
20 #include "js_startup_task_executor.h"
21
22 namespace OHOS {
23 namespace AbilityRuntime {
24 namespace {
25 constexpr size_t ARGC_TWO = 2;
26 constexpr int32_t INDEX_ZERO = 0;
27 constexpr int32_t INDEX_ONE = 1;
28
29 class AsyncTaskCallBack {
30 public:
31 AsyncTaskCallBack() = default;
32 ~AsyncTaskCallBack() = default;
33
AsyncTaskCompleted(napi_env env,napi_callback_info info)34 static napi_value AsyncTaskCompleted(napi_env env, napi_callback_info info)
35 {
36 TAG_LOGD(AAFwkTag::STARTUP, "called");
37 size_t argc = ARGC_TWO;
38 napi_value argv[ARGC_TWO] = { nullptr };
39 napi_value thisVar = nullptr;
40 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
41
42 std::string startupName;
43 if (!ConvertFromJsValue(env, argv[INDEX_ZERO], startupName)) {
44 TAG_LOGE(AAFwkTag::STARTUP, "Convert error");
45 return CreateJsUndefined(env);
46 }
47
48 napi_value resultJs = argv[INDEX_ONE];
49 napi_ref resultRef = nullptr;
50 napi_create_reference(env, resultJs, INDEX_ONE, &resultRef);
51 std::shared_ptr<NativeReference> result(reinterpret_cast<NativeReference*>(resultRef));
52 std::shared_ptr<StartupTaskResult> callbackResult = std::make_shared<JsStartupTaskResult>(result);
53
54 std::shared_ptr<JsStartupTask> jsStartupTask;
55 for (const auto& iter : jsStartupTaskObjects_) {
56 if (iter.first == startupName) {
57 jsStartupTask = iter.second.lock();
58 }
59 }
60
61 if (jsStartupTask != nullptr) {
62 jsStartupTask->OnAsyncTaskCompleted(callbackResult);
63 jsStartupTaskObjects_.erase(startupName);
64 }
65
66 return CreateJsUndefined(env);
67 }
68
Constructor(napi_env env,napi_callback_info cbinfo)69 static napi_value Constructor(napi_env env, napi_callback_info cbinfo)
70 {
71 TAG_LOGD(AAFwkTag::STARTUP, "called");
72 return CreateJsUndefined(env);
73 }
74
75 static std::map<std::string, std::weak_ptr<JsStartupTask>> jsStartupTaskObjects_;
76 };
77 std::map<std::string, std::weak_ptr<JsStartupTask>> AsyncTaskCallBack::jsStartupTaskObjects_;
78 }
79
80 const std::string JsStartupTask::TASK_TYPE = "Js";
81
JsStartupTask(const std::string & name,JsRuntime & jsRuntime,std::unique_ptr<NativeReference> & startupJsRef,std::shared_ptr<NativeReference> & contextJsRef)82 JsStartupTask::JsStartupTask(const std::string& name, JsRuntime& jsRuntime,
83 std::unique_ptr<NativeReference>& startupJsRef, std::shared_ptr<NativeReference>& contextJsRef)
84 : AppStartupTask(name), jsRuntime_(jsRuntime), startupJsRef_(std::move(startupJsRef)), contextJsRef_(contextJsRef)
85 {
86 }
87
88 JsStartupTask::~JsStartupTask() = default;
89
GetType() const90 const std::string &JsStartupTask::GetType() const
91 {
92 return TASK_TYPE;
93 }
94
RunTaskInit(std::unique_ptr<StartupTaskResultCallback> callback)95 int32_t JsStartupTask::RunTaskInit(std::unique_ptr<StartupTaskResultCallback> callback)
96 {
97 TAG_LOGI(AAFwkTag::STARTUP, "task: %{public}s init", GetName().c_str());
98 if (callCreateOnMainThread_) {
99 return JsStartupTaskExecutor::RunOnMainThread(jsRuntime_, startupJsRef_, contextJsRef_, std::move(callback));
100 }
101
102 if (LoadJsAsyncTaskExecutor() != ERR_OK) {
103 TAG_LOGE(AAFwkTag::STARTUP, "LoadJsAsyncTaskExecutor failed");
104 return ERR_STARTUP_INTERNAL_ERROR;
105 }
106 LoadJsAsyncTaskCallback();
107
108 startupTaskResultCallback_ = std::move(callback);
109
110 auto result = JsStartupTaskExecutor::RunOnTaskPool(
111 jsRuntime_, startupJsRef_, contextJsRef_, AsyncTaskExecutorJsRef_, AsyncTaskExecutorCallbackJsRef_, name_);
112 if (result == ERR_OK) {
113 AsyncTaskCallBack::jsStartupTaskObjects_.emplace(name_,
114 std::static_pointer_cast<JsStartupTask>(shared_from_this()));
115 }
116 return result;
117 }
118
LoadJsAsyncTaskExecutor()119 int32_t JsStartupTask::LoadJsAsyncTaskExecutor()
120 {
121 TAG_LOGD(AAFwkTag::STARTUP, "called");
122 HandleScope handleScope(jsRuntime_);
123 auto env = jsRuntime_.GetNapiEnv();
124
125 napi_value object = nullptr;
126 napi_create_object(env, &object);
127 if (object == nullptr) {
128 TAG_LOGE(AAFwkTag::STARTUP, "null object");
129 return ERR_STARTUP_INTERNAL_ERROR;
130 }
131
132 AsyncTaskExecutorJsRef_ =
133 JsRuntime::LoadSystemModuleByEngine(env, "app.appstartup.AsyncTaskExcutor", &object, 1);
134 return ERR_OK;
135 }
136
LoadJsAsyncTaskCallback()137 void JsStartupTask::LoadJsAsyncTaskCallback()
138 {
139 TAG_LOGD(AAFwkTag::STARTUP, "called");
140 HandleScope handleScope(jsRuntime_);
141 auto env = jsRuntime_.GetNapiEnv();
142
143 napi_value config;
144 std::string value = "This is callback value";
145 NAPI_CALL_RETURN_VOID(
146 env, napi_create_string_utf8(env, value.c_str(), value.length(), &config));
147
148 napi_property_descriptor props[] = {
149 DECLARE_NAPI_STATIC_FUNCTION("onAsyncTaskCompleted", AsyncTaskCallBack::AsyncTaskCompleted),
150 DECLARE_NAPI_INSTANCE_PROPERTY("config", config),
151 };
152 napi_value asyncTaskCallbackClass = nullptr;
153 napi_define_sendable_class(env, "AsyncTaskCallback", NAPI_AUTO_LENGTH, AsyncTaskCallBack::Constructor,
154 nullptr, sizeof(props) / sizeof(props[0]), props, nullptr, &asyncTaskCallbackClass);
155 AsyncTaskExecutorCallbackJsRef_ =
156 JsRuntime::LoadSystemModuleByEngine(env, "app.appstartup.AsyncTaskCallback", &asyncTaskCallbackClass, 1);
157 }
158
OnAsyncTaskCompleted(const std::shared_ptr<StartupTaskResult> & result)159 void JsStartupTask::OnAsyncTaskCompleted(const std::shared_ptr<StartupTaskResult> &result)
160 {
161 TAG_LOGD(AAFwkTag::STARTUP, "called");
162 if (startupTaskResultCallback_ == nullptr) {
163 TAG_LOGE(AAFwkTag::STARTUP, "null startupTaskResultCallback");
164 return;
165 }
166 startupTaskResultCallback_->Call(result);
167 }
168
RunTaskOnDependencyCompleted(const std::string & dependencyName,const std::shared_ptr<StartupTaskResult> & result)169 int32_t JsStartupTask::RunTaskOnDependencyCompleted(const std::string &dependencyName,
170 const std::shared_ptr<StartupTaskResult> &result)
171 {
172 HandleScope handleScope(jsRuntime_);
173 auto env = jsRuntime_.GetNapiEnv();
174
175 if (startupJsRef_ == nullptr) {
176 TAG_LOGE(AAFwkTag::STARTUP, "null ref_:%{public}s", name_.c_str());
177 return ERR_STARTUP_INTERNAL_ERROR;
178 }
179 napi_value startupValue = startupJsRef_->GetNapiValue();
180 if (!CheckTypeForNapiValue(env, startupValue, napi_object)) {
181 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, not napi object", name_.c_str());
182 return ERR_STARTUP_INTERNAL_ERROR;
183 }
184 napi_value startupOnDepCompleted = nullptr;
185 napi_get_named_property(env, startupValue, "onDependencyCompleted", &startupOnDepCompleted);
186 if (startupOnDepCompleted == nullptr) {
187 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, get onDependencyCompleted failed", name_.c_str());
188 return ERR_STARTUP_FAILED_TO_EXECUTE_STARTUP;
189 }
190 bool isCallable = false;
191 napi_is_callable(env, startupOnDepCompleted, &isCallable);
192 if (!isCallable) {
193 TAG_LOGE(AAFwkTag::STARTUP, "onDependencyCompleted not callable:%{public}s", name_.c_str());
194 return ERR_STARTUP_FAILED_TO_EXECUTE_STARTUP;
195 }
196
197 napi_value jsResult = GetDependencyResult(env, dependencyName, result);
198 napi_value dependency = CreateJsValue(env, dependencyName);
199 constexpr size_t argc = 2;
200 napi_value argv[argc] = { dependency, jsResult };
201 napi_call_function(env, startupValue, startupOnDepCompleted, argc, argv, nullptr);
202 return ERR_OK;
203 }
204
GetDependencyResult(napi_env env,const std::string & dependencyName,const std::shared_ptr<StartupTaskResult> & result)205 napi_value JsStartupTask::GetDependencyResult(napi_env env, const std::string &dependencyName,
206 const std::shared_ptr<StartupTaskResult> &result)
207 {
208 if (result == nullptr || result->GetResultType() != StartupTaskResult::ResultType::JS) {
209 return CreateJsUndefined(env);
210 } else {
211 std::shared_ptr<JsStartupTaskResult> jsResultPtr = std::static_pointer_cast<JsStartupTaskResult>(result);
212 if (jsResultPtr == nullptr) {
213 TAG_LOGE(AAFwkTag::STARTUP, "%{public}s, convert failed", dependencyName.c_str());
214 return CreateJsUndefined(env);
215 }
216 std::shared_ptr<NativeReference> jsResultRef = jsResultPtr->GetJsStartupResultRef();
217 if (jsResultRef == nullptr) {
218 return CreateJsUndefined(env);
219 }
220 return jsResultRef->GetNapiValue();
221 }
222 }
223 } // namespace AbilityRuntime
224 } // namespace OHOS
225