1 /*
2 * Copyright (c) 2021 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 "update_session.h"
17
18 #include "client_helper.h"
19 #include "napi_util.h"
20 #include "node_api.h"
21 #include "securec.h"
22 #include "update_helper.h"
23
24 using namespace std;
25
26 namespace OHOS {
27 namespace UpdateEngine {
28 const int32_t RESULT_ARGC = 2;
29
30 uint32_t g_sessionId = 0;
UpdateSession(IUpdater * client,SessionParams & sessionParams,size_t argc,size_t callbackNumber)31 UpdateSession::UpdateSession(IUpdater *client, SessionParams &sessionParams, size_t argc, size_t callbackNumber)
32 : sessionId(++g_sessionId), client_(client), sessionParams_(sessionParams), totalArgc_(argc),
33 callbackNumber_(callbackNumber) {}
34
CreateWorkerName(napi_env env) const35 napi_value UpdateSession::CreateWorkerName(napi_env env) const
36 {
37 napi_value workName;
38 std::string name = "Async Work" + std::to_string(sessionId);
39 napi_status status = napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &workName);
40 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to worker name");
41 return workName;
42 }
43
StartWork(napi_env env,const napi_value * args,DoWorkFunction worker,void * context)44 napi_value UpdateSession::StartWork(napi_env env, const napi_value *args, DoWorkFunction worker, void *context)
45 {
46 CLIENT_LOGI("StartWork type: %{public}d", CAST_INT(sessionParams_.type));
47 doWorker_ = worker;
48 context_ = context;
49 return StartWork(env, sessionParams_.callbackStartIndex, args);
50 }
51
ExecuteWork(napi_env env)52 void UpdateSession::ExecuteWork(napi_env env)
53 {
54 if (doWorker_ != nullptr) {
55 #ifndef UPDATER_UT
56 if (sessionParams_.isNeedBusinessError) {
57 workResult_ = doWorker_(sessionParams_.type, &businessError_);
58 } else {
59 workResult_ = doWorker_(sessionParams_.type, context_);
60 }
61 CLIENT_LOGI("UpdateSession::ExecuteWork workResult : %{public}d", workResult_);
62 if (IsAsyncCompleteWork() && !IsWorkExecuteSuccess()) {
63 CLIENT_LOGE("UpdateSession::ExecuteWork CompleteWork when error");
64 CompleteWork(env, napi_ok, this);
65 }
66 #else
67 doWorker_(sessionParams_.type, context_);
68 #endif
69 }
70 }
71
72 // JS thread, which is used to notify the JS page upon completion of the operation.
CompleteWork(napi_env env,napi_status status,void * data)73 void UpdateSession::CompleteWork(napi_env env, napi_status status, void *data)
74 {
75 auto sess = reinterpret_cast<UpdateSession*>(data);
76 PARAM_CHECK(sess != nullptr && sess->GetUpdateClient() != nullptr, return, "Session is null pointer");
77 sess->CompleteWork(env, status);
78 // If the share ptr is used, you can directly remove the share ptr.
79 IUpdater *client = sess->GetUpdateClient();
80 if (client != nullptr && !sess->IsNeedWaitAsyncCallback()) {
81 client->RemoveSession(sess->GetSessionId());
82 }
83 }
84
85 // The C++ thread executes the synchronization operation. After the synchronization is complete,
86 // the CompleteWork is called to notify the JS page of the completion of the operation.
ExecuteWork(napi_env env,void * data)87 void UpdateSession::ExecuteWork(napi_env env, void *data)
88 {
89 auto sess = reinterpret_cast<UpdateSession*>(data);
90 PARAM_CHECK(sess != nullptr, return, "sess is null");
91 sess->ExecuteWork(env);
92 }
93
StartWork(napi_env env,size_t startIndex,const napi_value * args)94 napi_value UpdateAsyncSession::StartWork(napi_env env, size_t startIndex, const napi_value *args)
95 {
96 CLIENT_LOGI("UpdateAsyncSession::StartWork startIndex: %{public}zu", startIndex);
97 CLIENT_LOGI("UpdateAsyncSession::totalArgc_ %{public}zu callbackNumber_: %{public}zu", totalArgc_, callbackNumber_);
98 PARAM_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ >= startIndex, return nullptr, "Invalid para");
99 napi_value workName = CreateWorkerName(env);
100 PARAM_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name");
101
102 // Check whether a callback exists. Only one callback is allowed.
103 for (size_t i = 0; (i < (totalArgc_ - startIndex)) && (i < callbackNumber_); i++) {
104 CLIENT_LOGI("CreateReference index:%u", static_cast<unsigned int>(i + startIndex));
105 ClientStatus ret = NapiUtil::IsTypeOf(env, args[i + startIndex], napi_function);
106 std::vector<std::string> paramNames;
107 paramNames.push_back("callback");
108 std::vector<std::string> paramTypes;
109 paramTypes.push_back("napi_function");
110 PARAM_CHECK_NAPI_CALL(env, ret == ClientStatus::CLIENT_SUCCESS,
111 ClientHelper::NapiThrowParamError(env, paramNames, paramTypes);
112 return nullptr, "invalid type");
113 ret = NapiUtil::CreateReference(env, args[i + startIndex], 1, callbackRef_[i]);
114 PARAM_CHECK_NAPI_CALL(env, ret == ClientStatus::CLIENT_SUCCESS, return nullptr, "Failed to create reference");
115 }
116
117 napi_status status = napi_ok;
118 // Create an asynchronous call.
119 if (!IsAsyncCompleteWork()) {
120 status = napi_create_async_work(
121 env, nullptr, workName, UpdateSession::ExecuteWork, UpdateSession::CompleteWork, this, &(worker_));
122 } else {
123 status = napi_create_async_work(
124 env,
125 nullptr,
126 workName,
127 UpdateSession::ExecuteWork,
128 [](napi_env env, napi_status status, void *data) {},
129 this,
130 &(worker_));
131 }
132
133 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to create worker");
134
135 // Put the thread in the task execution queue.
136 status = napi_queue_async_work(env, worker_);
137 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to queue worker");
138 napi_value result;
139 napi_create_int32(env, 0, &result);
140 return result;
141 }
142
NotifyJS(napi_env env,napi_value thisVar,const UpdateResult & result)143 void UpdateAsyncSession::NotifyJS(napi_env env, napi_value thisVar, const UpdateResult &result)
144 {
145 CLIENT_LOGI("UpdateAsyncSession::NotifyJS callbackNumber_: %{public}d", static_cast<int32_t>(callbackNumber_));
146
147 napi_value callback;
148 napi_value undefined;
149 napi_value callResult;
150 napi_get_undefined(env, &undefined);
151 napi_value retArgs[RESULT_ARGC] = { 0 };
152
153 BusinessError businessError;
154 GetBusinessError(businessError, result);
155 uint32_t ret;
156 if (UpdateHelper::IsErrorExist(businessError)) {
157 CLIENT_LOGI("UpdateAsyncSession::NotifyJS error exist");
158 ret = static_cast<uint32_t>(ClientHelper::BuildBusinessError(env, retArgs[0], businessError));
159 } else {
160 ret = static_cast<uint32_t>(result.buildJSObject(env, retArgs[1], result));
161 }
162 PARAM_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json");
163
164 napi_status retStatus = napi_get_reference_value(env, callbackRef_[0], &callback);
165 PARAM_CHECK_NAPI_CALL(env, retStatus == napi_ok, return, "Failed to get reference");
166 const int callBackNumber = 2;
167 retStatus = napi_call_function(env, undefined, callback, callBackNumber, retArgs, &callResult);
168 // Release resources.
169 for (size_t i = 0; i < callbackNumber_; i++) {
170 napi_delete_reference(env, callbackRef_[i]);
171 callbackRef_[i] = nullptr;
172 }
173 napi_delete_async_work(env, worker_);
174 worker_ = nullptr;
175 }
176
CompleteWork(napi_env env,napi_status status)177 void UpdateAsyncSession::CompleteWork(napi_env env, napi_status status)
178 {
179 CLIENT_LOGI("UpdateAsyncSession::CompleteWork callbackNumber_: %{public}d, %{public}d",
180 static_cast<int32_t>(callbackNumber_),
181 sessionParams_.type);
182 if (IsNeedWaitAsyncCallback()) {
183 return;
184 }
185 UpdateResult result;
186 GetUpdateResult(result);
187 NotifyJS(env, NULL, result);
188 }
189
CompleteWork(napi_env env,napi_status status)190 void UpdateAsyncSessionNoCallback::CompleteWork(napi_env env, napi_status status)
191 {
192 CLIENT_LOGI("UpdateAsyncSessionNoCallback::CompleteWork callbackNumber_: %d",
193 static_cast<int32_t>(callbackNumber_));
194 }
195
StartWork(napi_env env,size_t startIndex,const napi_value * args)196 napi_value UpdatePromiseSession::StartWork(napi_env env, size_t startIndex, const napi_value *args)
197 {
198 CLIENT_LOGI("UpdatePromiseSession::StartWork");
199 PARAM_CHECK_NAPI_CALL(env, args != nullptr, return nullptr, "Invalid para");
200 napi_value workName = CreateWorkerName(env);
201 PARAM_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name");
202
203 napi_value promise;
204 napi_status status = napi_create_promise(env, &deferred_, &promise);
205 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_promise");
206
207 // Create an asynchronous call.
208 status = napi_create_async_work(env, nullptr, workName, UpdateSession::ExecuteWork,
209 UpdateSession::CompleteWork, this, &(worker_));
210 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_async_work");
211 // Put the thread in the task execution queue.
212 status = napi_queue_async_work(env, worker_);
213 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_queue_async_work");
214 return promise;
215 }
216
NotifyJS(napi_env env,napi_value thisVar,const UpdateResult & result)217 void UpdatePromiseSession::NotifyJS(napi_env env, napi_value thisVar, const UpdateResult &result)
218 {
219 int32_t errorNum = static_cast<int32_t>(result.businessError.errorNum);
220 CLIENT_LOGI("UpdatePromiseSession NotifyJS errorNum:%{public}d", errorNum);
221
222 // Get the return result.
223 napi_value processResult = nullptr;
224 BusinessError businessError;
225 GetBusinessError(businessError, result);
226 if (!UpdateHelper::IsErrorExist(businessError)) {
227 result.buildJSObject(env, processResult, result);
228 napi_resolve_deferred(env, deferred_, processResult);
229 } else {
230 ClientHelper::BuildBusinessError(env, processResult, businessError);
231 napi_reject_deferred(env, deferred_, processResult);
232 }
233 napi_delete_async_work(env, worker_);
234 worker_ = nullptr;
235 }
236
CompleteWork(napi_env env,napi_status status)237 void UpdatePromiseSession::CompleteWork(napi_env env, napi_status status)
238 {
239 CLIENT_LOGI("UpdatePromiseSession::CompleteWork status: %d", static_cast<int32_t>(status));
240 if (IsNeedWaitAsyncCallback()) {
241 return;
242 }
243 UpdateResult result;
244 GetUpdateResult(result);
245 NotifyJS(env, NULL, result);
246 }
247
StartWork(napi_env env,size_t startIndex,const napi_value * args)248 napi_value UpdateListener::StartWork(napi_env env, size_t startIndex, const napi_value *args)
249 {
250 CLIENT_LOGI("UpdateListener::StartWork");
251 PARAM_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ > startIndex, return nullptr, "Invalid para");
252
253 if (NapiUtil::IsTypeOf(env, args[0], napi_string) == ClientStatus::CLIENT_SUCCESS) {
254 int ret = NapiUtil::GetString(env, args[0], eventType_);
255 PARAM_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to get string event type");
256 } else {
257 ClientStatus ret = ClientHelper::GetEventClassifyInfoFromArg(env, args[0], eventClassifyInfo_);
258 PARAM_CHECK_NAPI_CALL(env, ret == ClientStatus::CLIENT_SUCCESS, return nullptr, "Failed to get obj event type");
259 }
260
261 PARAM_CHECK_NAPI_CALL(env, NapiUtil::IsTypeOf(env, args[startIndex], napi_function) == ClientStatus::CLIENT_SUCCESS,
262 return nullptr, "Invalid callback type");
263 ClientStatus ret = NapiUtil::CreateReference(env, args[startIndex], 1, handlerRef_);
264 PARAM_CHECK_NAPI_CALL(env, ret == ClientStatus::CLIENT_SUCCESS, return nullptr, "Failed to create reference");
265
266 napi_value result;
267 napi_create_int32(env, 0, &result);
268 return result;
269 }
270
NotifyJS(napi_env env,napi_value thisVar,const UpdateResult & result)271 void UpdateListener::NotifyJS(napi_env env, napi_value thisVar, const UpdateResult &result)
272 {
273 CLIENT_LOGI("NotifyJS");
274 napi_value jsEvent;
275 napi_value handler = nullptr;
276 napi_value callResult;
277 int32_t ret = result.buildJSObject(env, jsEvent, result);
278 PARAM_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json");
279 {
280 std::lock_guard<std::mutex> lock(mutex_);
281 PARAM_CHECK_NAPI_CALL(env, handlerRef_ != nullptr, return, "handlerRef_ has beed freed");
282 napi_status status = napi_get_reference_value(env, handlerRef_, &handler);
283 PARAM_CHECK_NAPI_CALL(env, status == napi_ok && handler != nullptr, return, "Failed to get reference");
284 }
285 PARAM_CHECK_NAPI_CALL(env, handler != nullptr, return, "handlerRef_ has beed freed");
286 napi_call_function(env, thisVar, handler, 1, &jsEvent, &callResult);
287 }
288
NotifyJS(napi_env env,napi_value thisVar,const EventInfo & eventInfo)289 void UpdateListener::NotifyJS(napi_env env, napi_value thisVar, const EventInfo &eventInfo)
290 {
291 CLIENT_LOGI("NotifyJS, eventId:%{public}d", eventInfo.eventId);
292 napi_value jsEvent = nullptr;
293 ClientStatus ret = ClientHelper::BuildEventInfo(env, jsEvent, eventInfo);
294 PARAM_CHECK_NAPI_CALL(env, ret == ClientStatus::CLIENT_SUCCESS, return, "Failed to build event info");
295
296 std::lock_guard<std::mutex> lock(mutex_);
297 PARAM_CHECK_NAPI_CALL(env, handlerRef_ != nullptr, return, "handlerRef_ has beed freed");
298 napi_value handler = nullptr;
299 napi_status status = napi_get_reference_value(env, handlerRef_, &handler);
300 PARAM_CHECK_NAPI_CALL(env, status == napi_ok && handler != nullptr, return, "Failed to get reference");
301
302 napi_value callResult = nullptr;
303 status = napi_call_function(env, thisVar, handler, 1, &jsEvent, &callResult);
304 if (status != napi_ok) {
305 CLIENT_LOGE("NotifyJS error, napi_call_function fail");
306 }
307 }
308
CheckEqual(napi_env env,napi_value handler,const std::string & type)309 bool UpdateListener::CheckEqual(napi_env env, napi_value handler, const std::string &type)
310 {
311 std::lock_guard<std::mutex> lock(mutex_);
312 bool isEquals = false;
313 napi_value handlerTemp = nullptr;
314 napi_status status = napi_get_reference_value(env, handlerRef_, &handlerTemp);
315 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return false, "Failed to get reference");
316 napi_strict_equals(env, handler, handlerTemp, &isEquals);
317 return isEquals && (type.compare(eventType_) == 0);
318 }
319
IsSameListener(napi_env env,const EventClassifyInfo & eventClassifyInfo,napi_value handler)320 bool UpdateListener::IsSameListener(napi_env env, const EventClassifyInfo &eventClassifyInfo, napi_value handler)
321 {
322 if (eventClassifyInfo_.eventClassify != eventClassifyInfo.eventClassify) {
323 CLIENT_LOGI("not same listener, different event classify, 0x%{public}x, 0x%{public}x",
324 eventClassifyInfo_.eventClassify, eventClassifyInfo.eventClassify);
325 return false;
326 }
327
328 napi_value currentHandler = nullptr;
329 napi_status status = napi_get_reference_value(env, handlerRef_, ¤tHandler);
330 PARAM_CHECK_NAPI_CALL(env, status == napi_ok, return false, "Failed to get current handle");
331
332 bool isEquals = false;
333 status = napi_strict_equals(env, handler, currentHandler, &isEquals);
334 return status == napi_ok && isEquals;
335 }
336
RemoveHandlerRef(napi_env env)337 void UpdateListener::RemoveHandlerRef(napi_env env)
338 {
339 std::lock_guard<std::mutex> lock(mutex_);
340 CLIENT_LOGI("RemoveHandlerRef handlerRef_:%{public}p %{public}u", handlerRef_, GetSessionId());
341 napi_delete_reference(env, handlerRef_);
342 handlerRef_ = nullptr;
343 }
344 } // namespace UpdateEngine
345 } // namespace OHOS
346