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