• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_, &currentHandler);
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