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