• 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 "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_, &currentHandler);
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