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