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