/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "update_session.h" #include <iostream> #include <memory> #include <mutex> #include <string> #include <thread> #include <vector> #include "node_api.h" #include "update_client.h" #include "update_helper.h" #include "package/package.h" #include "securec.h" using namespace std; using namespace OHOS::update_engine; namespace updateClient { const int32_t RESULT_ARGC = 2; uint32_t g_sessionId = 0; UpdateSession::UpdateSession(UpdateClient *client, int32_t type, size_t argc, size_t callbackNumber) : sessionId(++g_sessionId), client_(client), type_(type), totalArgc_(argc), callbackNumber_(callbackNumber) {} int32_t UpdateSession::CreateReference(napi_env env, napi_value arg, uint32_t refcount, napi_ref &reference) const { napi_valuetype valuetype; napi_status status = napi_typeof(env, arg, &valuetype); CLIENT_CHECK(status == napi_ok, return status, "Failed to napi_typeof"); CLIENT_CHECK(valuetype == napi_function, return -1, "Invalid callback type"); status = napi_create_reference(env, arg, refcount, &reference); CLIENT_CHECK(status == napi_ok, return status, "Failed to create reference"); return status; } napi_value UpdateSession::CreateWorkerName(napi_env env) const { napi_value workName; std::string name = "Async Work" + std::to_string(sessionId); napi_status status = napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &workName); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to worker name"); return workName; } napi_value UpdateSession::StartWork(napi_env env, size_t startIndex, const napi_value *args, UpdateClient::DoWorkFunction worker, void *context) { static std::string sessName[SESSION_MAX] = { "check version", "download", "upgrade", "set policy", "get policy", "get new version", "get upgrade status", "subscribe", "unsubscribe", "get update", "apply new version", "reboot and clean", "verify package", "Cancel Upgrade" }; CLIENT_LOGI("StartWork type: %s", sessName[type_].c_str()); doWorker_ = worker; context_ = context; return StartWork(env, startIndex, args); } void UpdateSession::ExecuteWork(napi_env env) { if (doWorker_ != nullptr) { #ifndef UPDATER_UT int32_t ret = doWorker_(type_, context_); CLIENT_CHECK_NAPI_CALL(env, ret == 0, return, "execute work"); #else doWorker_(type_, context_); #endif } return; } napi_value UpdateAsyncession::StartWork(napi_env env, size_t startIndex, const napi_value *args) { CLIENT_LOGI("UpdateAsyncession::StartWork startIndex: %zu", startIndex); CLIENT_LOGI("UpdateAsyncession::totalArgc_ %zu callbackNumber_: %zu", totalArgc_, callbackNumber_); CLIENT_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ >= startIndex, return nullptr, "Invalid para"); napi_value workName = CreateWorkerName(env); CLIENT_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name"); // Check whether a callback exists. Only one callback is allowed. for (size_t i = 0; (i < (totalArgc_ - startIndex)) && (i < callbackNumber_); i++) { CLIENT_LOGI("CreateReference index:%u", static_cast<unsigned int>(i + startIndex)); int32_t ret = CreateReference(env, args[i + startIndex], 1, callbackRef_[i]); CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to create reference"); } // Create an asynchronous call. napi_status status = napi_create_async_work(env, nullptr, workName, UpdateSession::ExecuteWork, UpdateSession::CompleteWork, this, &(worker_)); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to create worker"); // Put the thread in the task execution queue. status = napi_queue_async_work(env, worker_); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to queue worker"); napi_value result; napi_create_int32(env, 0, &result); return result; } void UpdateAsyncession::CompleteWork(napi_env env, napi_status status) { CLIENT_LOGI("UpdateAsyncession::CompleteWork callbackNumber_: %d", static_cast<int32_t>(callbackNumber_)); napi_value callback; napi_value undefined; napi_value callResult; napi_get_undefined(env, &undefined); napi_value retArgs[RESULT_ARGC] = { 0 }; UpdateResult result; int32_t fail = 0; client_->GetUpdateResult(type_, result, fail); int ret = UpdateClient::BuildErrorResult(env, retArgs[0], fail); ret |= result.buildJSObject(env, retArgs[1], result); CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json"); status = napi_get_reference_value(env, callbackRef_[0], &callback); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return, "Failed to get reference"); const int callBackNumber = 2; status = napi_call_function(env, undefined, callback, callBackNumber, retArgs, &callResult); // Release resources. for (size_t i = 0; i < callbackNumber_; i++) { napi_delete_reference(env, callbackRef_[i]); callbackRef_[i] = nullptr; } napi_delete_async_work(env, worker_); worker_ = nullptr; } void UpdateAsyncessionNoCallback::CompleteWork(napi_env env, napi_status status) { CLIENT_LOGI("UpdateAsyncessionNoCallback::CompleteWork callbackNumber_: %d", static_cast<int32_t>(callbackNumber_)); } napi_value UpdatePromiseSession::StartWork(napi_env env, size_t startIndex, const napi_value *args) { CLIENT_LOGI("UpdatePromiseSession::StartWork"); CLIENT_CHECK_NAPI_CALL(env, args != nullptr, return nullptr, "Invalid para"); napi_value workName = CreateWorkerName(env); CLIENT_CHECK_NAPI_CALL(env, workName != nullptr, return nullptr, "Failed to worker name"); napi_value promise; napi_status status = napi_create_promise(env, &deferred_, &promise); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_promise"); // Create an asynchronous call. status = napi_create_async_work(env, nullptr, workName, UpdateSession::ExecuteWork, UpdateSession::CompleteWork, this, &(worker_)); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_create_async_work"); // Put the thread in the task execution queue. status = napi_queue_async_work(env, worker_); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return nullptr, "Failed to napi_queue_async_work"); return promise; } void UpdatePromiseSession::CompleteWork(napi_env env, napi_status status) { CLIENT_LOGI("UpdatePromiseSession::CompleteWork status: %d", static_cast<int32_t>(status)); // Get the return result. napi_value processResult; UpdateResult result; int32_t fail = 0; client_->GetUpdateResult(type_, result, fail); if (fail == 0) { result.buildJSObject(env, processResult, result); napi_resolve_deferred(env, deferred_, processResult); } else { UpdateClient::BuildErrorResult(env, processResult, fail); napi_reject_deferred(env, deferred_, processResult); } napi_delete_async_work(env, worker_); worker_ = nullptr; } napi_value UpdateListener::StartWork(napi_env env, size_t startIndex, const napi_value *args) { CLIENT_LOGI("UpdateListener::StartWork"); CLIENT_CHECK_NAPI_CALL(env, args != nullptr && totalArgc_ > startIndex, return nullptr, "Invalid para"); int ret = UpdateClient::GetStringValue(env, args[0], eventType_); CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to get event type"); // reference count is 1 ret = CreateReference(env, args[startIndex], 1, handlerRef_); CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return nullptr, "Failed to create reference"); napi_value result; napi_create_int32(env, 0, &result); return result; } void UpdateListener::NotifyJS(napi_env env, napi_value thisVar, int32_t retcode, const UpdateResult &result) { CLIENT_LOGI("NotifyJS retcode:%d", retcode); napi_value jsEvent; napi_value handler = nullptr; napi_value callResult; int32_t ret = result.buildJSObject(env, jsEvent, result); CLIENT_CHECK_NAPI_CALL(env, ret == napi_ok, return, "Failed to build json"); { std::unique_lock<std::mutex> lock(mutex_); CLIENT_CHECK_NAPI_CALL(env, handlerRef_ != nullptr, return, "handlerRef_ has beed freed"); napi_status status = napi_get_reference_value(env, handlerRef_, &handler); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok && handler != nullptr, return, "Failed to get reference"); } CLIENT_CHECK_NAPI_CALL(env, handler != nullptr, return, "handlerRef_ has beed freed"); napi_call_function(env, thisVar, handler, 1, &jsEvent, &callResult); } bool UpdateListener::CheckEqual(napi_env env, napi_value handler, const std::string &type) { std::unique_lock<std::mutex> lock(mutex_); bool isEquals = false; napi_value handlerTemp = nullptr; napi_status status = napi_get_reference_value(env, handlerRef_, &handlerTemp); CLIENT_CHECK_NAPI_CALL(env, status == napi_ok, return false, "Failed to get reference"); napi_strict_equals(env, handler, handlerTemp, &isEquals); return isEquals && (type.compare(eventType_) == 0); } void UpdateListener::RemoveHandlerRef(napi_env env) { std::unique_lock<std::mutex> lock(mutex_); CLIENT_LOGI("RemoveHandlerRef handlerRef_:%{public}p %{public}u", handlerRef_, GetSessionId()); napi_delete_reference(env, handlerRef_); handlerRef_ = nullptr; } } // namespace updateClient