• 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 #include <iostream>
18 #include <memory>
19 #include <mutex>
20 #include <string>
21 #include <thread>
22 #include <vector>
23 
24 #include "node_api.h"
25 #include "update_client.h"
26 #include "update_helper.h"
27 #include "package/package.h"
28 #include "securec.h"
29 
30 using namespace std;
31 using namespace OHOS::update_engine;
32 
33 namespace updateClient {
34 const int32_t RESULT_ARGC = 2;
35 
36 uint32_t g_sessionId = 0;
UpdateSession(UpdateClient * client,int32_t type,size_t argc,size_t callbackNumber)37 UpdateSession::UpdateSession(UpdateClient *client, int32_t type, size_t argc, size_t callbackNumber)
38     : sessionId(++g_sessionId), client_(client), type_(type), totalArgc_(argc), callbackNumber_(callbackNumber) {}
39 
CreateReference(napi_env env,napi_value arg,uint32_t refcount,napi_ref & reference) const40 int32_t UpdateSession::CreateReference(napi_env env, napi_value arg, uint32_t refcount,
41     napi_ref &reference) const
42 {
43     napi_valuetype valuetype;
44     napi_status status = napi_typeof(env, arg, &valuetype);
45     CLIENT_CHECK(status == napi_ok, return status, "Failed to napi_typeof");
46     CLIENT_CHECK(valuetype == napi_function, return -1, "Invalid callback type");
47 
48     status = napi_create_reference(env, arg, refcount, &reference);
49     CLIENT_CHECK(status == napi_ok, return status, "Failed to create reference");
50     return status;
51 }
52 
CreateWorkerName(napi_env env) const53 napi_value UpdateSession::CreateWorkerName(napi_env env) const
54 {
55     napi_value workName;
56     std::string name = "Async Work" + std::to_string(sessionId);
57     napi_status status = napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &workName);
58     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to worker name");
59     return workName;
60 }
61 
StartWork(napi_env env,size_t startIndex,const napi_value * args,UpdateClient::DoWorkFunction worker,void * context)62 napi_value UpdateSession::StartWork(napi_env env,
63     size_t startIndex, const napi_value *args, UpdateClient::DoWorkFunction worker, void *context)
64 {
65     static std::string sessName[SESSION_MAX] = {
66         "check version", "download", "upgrade", "set policy", "get policy",
67         "get new version", "get upgrade status", "subscribe", "unsubscribe", "get update",
68         "apply new version", "reboot and clean", "verify package", "Cancel Upgrade"
69     };
70 
71     CLIENT_LOGI("StartWork type: %s", sessName[type_].c_str());
72     doWorker_ = worker;
73     context_ = context;
74     return StartWork(env, startIndex, args);
75 }
76 
ExecuteWork(napi_env env)77 void UpdateSession::ExecuteWork(napi_env env)
78 {
79     if (doWorker_ != nullptr) {
80 #ifndef UPDATER_UT
81         int32_t ret = doWorker_(type_, context_);
82         CLIENT_CHECK_NAPI_CALL(env, ret == 0, return, "execute work");
83 #else
84         doWorker_(type_, context_);
85 #endif
86     }
87     return;
88 }
89 
StartWork(napi_env env,size_t startIndex,const napi_value * args)90 napi_value UpdateAsyncession::StartWork(napi_env env, size_t startIndex, const napi_value *args)
91 {
92     CLIENT_LOGI("UpdateAsyncession::StartWork startIndex: %zu", startIndex);
93     CLIENT_LOGI("UpdateAsyncession::totalArgc_ %zu callbackNumber_: %zu", totalArgc_, callbackNumber_);
94     CLIENT_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ >= startIndex, return nullptr, "Invalid para");
95     napi_value workName = CreateWorkerName(env);
96     CLIENT_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name");
97 
98     // Check whether a callback exists. Only one callback is allowed.
99     for (size_t i = 0; (i < (totalArgc_ - startIndex)) && (i < callbackNumber_); i++) {
100         CLIENT_LOGI("CreateReference index:%u", static_cast<unsigned int>(i + startIndex));
101         int32_t ret = CreateReference(env, args[i + startIndex], 1, callbackRef_[i]);
102         CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to create reference");
103     }
104 
105     // Create an asynchronous call.
106     napi_status status = napi_create_async_work(env, nullptr, workName, UpdateSession::ExecuteWork,
107         UpdateSession::CompleteWork, this, &(worker_));
108     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to create worker");
109 
110     // Put the thread in the task execution queue.
111     status = napi_queue_async_work(env, worker_);
112     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to queue worker");
113     napi_value result;
114     napi_create_int32(env, 0, &result);
115     return result;
116 }
117 
CompleteWork(napi_env env,napi_status status)118 void UpdateAsyncession::CompleteWork(napi_env env, napi_status status)
119 {
120     CLIENT_LOGI("UpdateAsyncession::CompleteWork callbackNumber_: %d", static_cast<int32_t>(callbackNumber_));
121     napi_value callback;
122     napi_value undefined;
123     napi_value callResult;
124     napi_get_undefined(env, &undefined);
125     napi_value retArgs[RESULT_ARGC] = { 0 };
126 
127     UpdateResult result;
128     int32_t fail = 0;
129     client_->GetUpdateResult(type_, result, fail);
130     int ret = UpdateClient::BuildErrorResult(env, retArgs[0], fail);
131     ret |= result.buildJSObject(env, retArgs[1], result);
132     CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json");
133 
134     status = napi_get_reference_value(env, callbackRef_[0], &callback);
135     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return, "Failed to get reference");
136     const int callBackNumber = 2;
137     status = napi_call_function(env, undefined, callback, callBackNumber, retArgs, &callResult);
138     // Release resources.
139     for (size_t i = 0; i < callbackNumber_; i++) {
140         napi_delete_reference(env, callbackRef_[i]);
141         callbackRef_[i] = nullptr;
142     }
143     napi_delete_async_work(env, worker_);
144     worker_ = nullptr;
145 }
146 
CompleteWork(napi_env env,napi_status status)147 void UpdateAsyncessionNoCallback::CompleteWork(napi_env env, napi_status status)
148 {
149     CLIENT_LOGI("UpdateAsyncessionNoCallback::CompleteWork callbackNumber_: %d",
150         static_cast<int32_t>(callbackNumber_));
151 }
152 
StartWork(napi_env env,size_t startIndex,const napi_value * args)153 napi_value UpdatePromiseSession::StartWork(napi_env env, size_t startIndex, const napi_value *args)
154 {
155     CLIENT_LOGI("UpdatePromiseSession::StartWork");
156     CLIENT_CHECK_NAPI_CALL(env, args != nullptr, return nullptr, "Invalid para");
157     napi_value workName = CreateWorkerName(env);
158     CLIENT_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name");
159 
160     napi_value promise;
161     napi_status status = napi_create_promise(env, &deferred_, &promise);
162     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_promise");
163 
164     // Create an asynchronous call.
165     status = napi_create_async_work(env, nullptr, workName, UpdateSession::ExecuteWork,
166         UpdateSession::CompleteWork, this, &(worker_));
167     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_async_work");
168     // Put the thread in the task execution queue.
169     status = napi_queue_async_work(env, worker_);
170     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_queue_async_work");
171     return promise;
172 }
173 
CompleteWork(napi_env env,napi_status status)174 void UpdatePromiseSession::CompleteWork(napi_env env, napi_status status)
175 {
176     CLIENT_LOGI("UpdatePromiseSession::CompleteWork status: %d", static_cast<int32_t>(status));
177     // Get the return result.
178     napi_value processResult;
179     UpdateResult result;
180     int32_t fail = 0;
181     client_->GetUpdateResult(type_, result, fail);
182     if (fail == 0) {
183         result.buildJSObject(env, processResult, result);
184         napi_resolve_deferred(env, deferred_, processResult);
185     } else {
186         UpdateClient::BuildErrorResult(env, processResult, fail);
187         napi_reject_deferred(env, deferred_, processResult);
188     }
189     napi_delete_async_work(env, worker_);
190     worker_ = nullptr;
191 }
192 
StartWork(napi_env env,size_t startIndex,const napi_value * args)193 napi_value UpdateListener::StartWork(napi_env env, size_t startIndex, const napi_value *args)
194 {
195     CLIENT_LOGI("UpdateListener::StartWork");
196     CLIENT_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ > startIndex, return nullptr, "Invalid para");
197     int ret = UpdateClient::GetStringValue(env, args[0], eventType_);
198     CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to get event type");
199 
200     // reference count is 1
201     ret = CreateReference(env, args[startIndex], 1, handlerRef_);
202     CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to create reference");
203     napi_value result;
204     napi_create_int32(env, 0, &result);
205     return result;
206 }
207 
NotifyJS(napi_env env,napi_value thisVar,int32_t retcode,const UpdateResult & result)208 void UpdateListener::NotifyJS(napi_env env, napi_value thisVar, int32_t retcode, const UpdateResult &result)
209 {
210     CLIENT_LOGI("NotifyJS retcode:%d", retcode);
211     napi_value jsEvent;
212     napi_value handler = nullptr;
213     napi_value callResult;
214     int32_t ret = result.buildJSObject(env, jsEvent, result);
215     CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json");
216     {
217         std::unique_lock<std::mutex> lock(mutex_);
218         CLIENT_CHECK_NAPI_CALL(env, handlerRef_ != nullptr, return, "handlerRef_ has beed freed");
219         napi_status status = napi_get_reference_value(env, handlerRef_, &handler);
220         CLIENT_CHECK_NAPI_CALL(env, status == napi_ok && handler != nullptr, return, "Failed to get reference");
221     }
222     CLIENT_CHECK_NAPI_CALL(env, handler != nullptr, return, "handlerRef_ has beed freed");
223     napi_call_function(env, thisVar, handler, 1, &jsEvent, &callResult);
224 }
225 
CheckEqual(napi_env env,napi_value handler,const std::string & type)226 bool UpdateListener::CheckEqual(napi_env env, napi_value handler, const std::string &type)
227 {
228     std::unique_lock<std::mutex> lock(mutex_);
229     bool isEquals = false;
230     napi_value handlerTemp = nullptr;
231     napi_status status = napi_get_reference_value(env, handlerRef_, &handlerTemp);
232     CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return false, "Failed to get reference");
233     napi_strict_equals(env, handler, handlerTemp, &isEquals);
234     return isEquals && (type.compare(eventType_) == 0);
235 }
236 
RemoveHandlerRef(napi_env env)237 void UpdateListener::RemoveHandlerRef(napi_env env)
238 {
239     std::unique_lock<std::mutex> lock(mutex_);
240     CLIENT_LOGI("RemoveHandlerRef handlerRef_:%{public}p %{public}u", handlerRef_, GetSessionId());
241     napi_delete_reference(env, handlerRef_);
242     handlerRef_ = nullptr;
243 }
244 } // namespace updateClient
245