diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..08192774 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Copyright (C) 2023 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. + +target +Cargo.lock \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 9bcf156b..58510523 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,5 +14,5 @@ # any change to frameworks/native/include/download_server_ipc_interface_code.h needs to be reviewed by @leonchan5 frameworks/native/include/download_server_ipc_interface_code.h @leonchan5 -# any change to services/service/rust/src/download_server_ipc_interface_code.rs needs to be reviewed by @leonchan5 -services/service/rust/src/download_server_ipc_interface_code.rs @leonchan5 +# any change to services/service/request/src/service/interface.rs needs to be reviewed by @leonchan5 +services/service/request/src/service/interface.rs @leonchan5 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..013aa8e1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +# Copyright (C) 2023 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. + +[workspace] +resolver = "2" +members = [ + "services/service/request", + "services/service/download_server", +] diff --git a/bundle.json b/bundle.json index 61ef99d3..e61d0940 100644 --- a/bundle.json +++ b/bundle.json @@ -67,7 +67,7 @@ "service_group": [ "//base/request/request/services/etc/init:downloadservice.cfg", "//base/request/request/services/sa_profile:download_sa_profiles", - "//base/request/request/services/service/rust:download_server" + "//base/request/request/services/service/download_server:download_server" ] }, "inner_kits": [], diff --git a/frameworks/js/napi/include/js_common.h b/frameworks/js/napi/include/js_common.h index 57acb13a..12259380 100644 --- a/frameworks/js/napi/include/js_common.h +++ b/frameworks/js/napi/include/js_common.h @@ -101,6 +101,7 @@ struct Config { uint32_t index = 0; int64_t begins = 0; int64_t ends = -1; + uint32_t priority = 0; bool overwrite = false; bool metered = false; bool roaming = false; @@ -209,6 +210,7 @@ struct TaskInfo { Reason code; std::string reason; bool withSystem = false; + uint32_t priority; std::map extras; std::vector taskStates; }; diff --git a/frameworks/js/napi/include/notify_stub.h b/frameworks/js/napi/include/notify_stub.h index f7834851..12bb2a74 100644 --- a/frameworks/js/napi/include/notify_stub.h +++ b/frameworks/js/napi/include/notify_stub.h @@ -36,7 +36,6 @@ public: private: void OnCallBack(MessageParcel &data); - static void OnDone(MessageParcel &data); bool IsHeaderReceive(const std::string &type, const NotifyData ¬ifyData); static void GetDownloadNotify(const std::string &type, const NotifyData ¬ifyData, Notify ¬ify); static void GetUploadNotify(const std::string &type, const NotifyData ¬ifyData, Notify ¬ify); diff --git a/frameworks/js/napi/include/request_event.h b/frameworks/js/napi/include/request_event.h index 093eafb9..2de3459c 100644 --- a/frameworks/js/napi/include/request_event.h +++ b/frameworks/js/napi/include/request_event.h @@ -48,10 +48,6 @@ public: static napi_value Stop(napi_env env, napi_callback_info info); static std::map failMap_; - static void AddCache(const std::string &taskId, const std::shared_ptr &info); - static bool GetCache(const std::string &taskId, std::shared_ptr &info); - static void RemoveCache(const std::string &taskId); - private: struct JsParam { std::string type; @@ -93,10 +89,6 @@ private: static NotifyData BuildNotifyData(const std::shared_ptr &taskInfo); static bool IsSupportType(const std::string &type, Version version); static void ConvertType(std::string &type); - static bool NeedNotify(const std::string &type, std::shared_ptr &taskInfo); - - static std::mutex taskCacheMutex_; - static std::map> taskCache_; }; } // namespace OHOS::Request diff --git a/frameworks/js/napi/src/js_task.cpp b/frameworks/js/napi/src/js_task.cpp index 001db867..5c691524 100644 --- a/frameworks/js/napi/src/js_task.cpp +++ b/frameworks/js/napi/src/js_task.cpp @@ -76,7 +76,6 @@ JsTask::~JsTask() { REQUEST_HILOGD("~JsTask()"); ClearListener(); - RequestEvent::RemoveCache(tid_); } napi_value JsTask::JsUpload(napi_env env, napi_callback_info info) { diff --git a/frameworks/js/napi/src/napi_utils.cpp b/frameworks/js/napi/src/napi_utils.cpp index 06576fb5..65c67db2 100644 --- a/frameworks/js/napi/src/napi_utils.cpp +++ b/frameworks/js/napi/src/napi_utils.cpp @@ -370,6 +370,7 @@ napi_value Convert2JSValue(napi_env env, TaskInfo &taskInfo) napi_set_named_property(env, value, "mimeType", Convert2JSValue(env, taskInfo.mimeType)); napi_set_named_property(env, value, "progress", Convert2JSValue(env, taskInfo.progress)); napi_set_named_property(env, value, "gauge", Convert2JSValue(env, taskInfo.gauge)); + napi_set_named_property(env, value, "priority", Convert2JSValue(env, taskInfo.priority)); napi_set_named_property(env, value, "ctime", Convert2JSValue(env, taskInfo.ctime)); napi_set_named_property(env, value, "mtime", Convert2JSValue(env, taskInfo.mtime)); napi_set_named_property(env, value, "retry", Convert2JSValue(env, taskInfo.retry)); diff --git a/frameworks/js/napi/src/notify_stub.cpp b/frameworks/js/napi/src/notify_stub.cpp index 18e04da5..326692b4 100644 --- a/frameworks/js/napi/src/notify_stub.cpp +++ b/frameworks/js/napi/src/notify_stub.cpp @@ -33,7 +33,6 @@ int32_t NotifyStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageP OnCallBack(data); break; case static_cast(RequestNotifyInterfaceCode::REQUEST_DONE_NOTIFY): - OnDone(data); break; default: REQUEST_HILOGE("Default value received, check needed."); @@ -78,7 +77,8 @@ void NotifyStub::OnCallBack(MessageParcel &data) notifyData.taskStates.push_back(taskState); } RequestCallBack(type, tid, notifyData); - if (notifyData.version == Version::API10 && (type == "complete" || type == "fail")) { + + if (type == "complete" || type == "fail") { JsTask::ClearTaskContext(tid); } } @@ -187,19 +187,4 @@ void NotifyStub::GetUploadNotify(const std::string &type, const NotifyData ¬i notify.progress.extras = notifyData.progress.extras; } } - -void NotifyStub::OnDone(MessageParcel &data) -{ - auto taskInfo = std::make_shared(); - ParcelHelper::UnMarshal(data, *taskInfo); - REQUEST_HILOGI("task %{public}s done", taskInfo->tid.c_str()); - std::lock_guard lockGuard(JsTask::taskMutex_); - auto item = JsTask::taskMap_.find(taskInfo->tid); - if (item == JsTask::taskMap_.end()) { - REQUEST_HILOGW("Task ID not found"); - return; - } - RequestEvent::AddCache(taskInfo->tid, taskInfo); - JsTask::ClearTaskContext(taskInfo->tid); -} } // namespace OHOS::Request diff --git a/frameworks/js/napi/src/request_event.cpp b/frameworks/js/napi/src/request_event.cpp index 206d790c..03dfaacf 100644 --- a/frameworks/js/napi/src/request_event.cpp +++ b/frameworks/js/napi/src/request_event.cpp @@ -89,8 +89,6 @@ std::map RequestEvent::failMap_ = { {UNSUPPORT_RANGE_REQUEST, ERROR_UNKNOWN}, }; -std::mutex RequestEvent::taskCacheMutex_; -std::map> RequestEvent::taskCache_; napi_value RequestEvent::Pause(napi_env env, napi_callback_info info) { REQUEST_HILOGD("Pause in"); @@ -151,38 +149,12 @@ napi_value RequestEvent::On(napi_env env, napi_callback_info info) REQUEST_HILOGD("On event %{public}s + %{public}s", jsParam.type.c_str(), jsParam.task->GetTid().c_str()); std::string key = jsParam.type + jsParam.task->GetTid(); jsParam.task->AddListener(key, listener); - std::shared_ptr taskInfo; - if (GetCache(jsParam.task->GetTid(), taskInfo) && taskInfo != nullptr) { - if (!NeedNotify(jsParam.type, taskInfo)) { - return nullptr; - } - listener->RequestCallBack(jsParam.type, jsParam.task->GetTid(), BuildNotifyData(taskInfo)); - return nullptr; - } if (jsParam.task->GetListenerSize(key) == 1) { RequestManager::GetInstance()->On(jsParam.type, jsParam.task->GetTid(), listener); } return nullptr; } -bool RequestEvent::NeedNotify(const std::string &type, std::shared_ptr &taskInfo) -{ - if (type == EVENT_FAIL && taskInfo->progress.state != State::FAILED) { - return false; - } - if (type == EVENT_COMPLETE && taskInfo->progress.state != State::COMPLETED) { - return false; - } - if (!taskInfo->progress.sizes.empty()) { - uint64_t processed = taskInfo->progress.processed; - int64_t totalSize = taskInfo->progress.sizes[0]; - if (type == EVENT_PROGRESS && processed == 0 && totalSize == -1) { - return false; - } - } - return true; -} - napi_value RequestEvent::Off(napi_env env, napi_callback_info info) { JsParam jsParam; @@ -353,16 +325,17 @@ int32_t RequestEvent::PauseExec(const std::shared_ptr &context) int32_t RequestEvent::QueryExec(const std::shared_ptr &context) { - std::shared_ptr infoRes; + TaskInfo infoRes; int32_t ret = E_OK; - if (!GetCache(context->task->GetTid(), infoRes) || infoRes == nullptr) { - infoRes = std::make_shared(); - ret = RequestManager::GetInstance()->Show(context->task->GetTid(), *infoRes); + if (!RequestManager::GetInstance()->LoadRequestServer()) { + ret = E_SERVICE_ERROR; + return ret; } + ret = RequestManager::GetInstance()->Show(context->task->GetTid(), infoRes); if (context->version_ != Version::API10 && ret != E_PERMISSION) { ret = E_OK; } - GetDownloadInfo(*infoRes, context->infoRes); + GetDownloadInfo(infoRes, context->infoRes); return ret; } @@ -370,8 +343,8 @@ int32_t RequestEvent::QueryMimeTypeExec(const std::shared_ptr &cont { std::shared_ptr infoRes; int32_t ret = E_OK; - if (GetCache(context->task->GetTid(), infoRes) || infoRes != nullptr) { - context->strRes = infoRes->mimeType; + if (!RequestManager::GetInstance()->LoadRequestServer()) { + ret = E_SERVICE_ERROR; return ret; } ret = RequestManager::GetInstance()->QueryMimeType(context->task->GetTid(), context->strRes); @@ -441,30 +414,4 @@ int32_t RequestEvent::ResumeExec(const std::shared_ptr &context) } return ret; } - -void RequestEvent::AddCache(const std::string &taskId, const std::shared_ptr &info) -{ - REQUEST_HILOGI("AddCache in, task id is %{public}s", taskId.c_str()); - std::lock_guard lock(taskCacheMutex_); - taskCache_[taskId] = info; -} - -bool RequestEvent::GetCache(const std::string &taskId, std::shared_ptr &info) -{ - REQUEST_HILOGI("GetCache in, task id is %{public}s", taskId.c_str()); - std::lock_guard lock(taskCacheMutex_); - auto it = taskCache_.find(taskId); - if (it != taskCache_.end()) { - info = it->second; - return true; - } - return false; -} - -void RequestEvent::RemoveCache(const std::string &taskId) -{ - std::lock_guard lock(taskCacheMutex_); - REQUEST_HILOGI("RemoveCache in, task id is %{public}s", taskId.c_str()); - taskCache_.erase(taskId); -} } // namespace OHOS::Request diff --git a/frameworks/native/src/parcel_helper.cpp b/frameworks/native/src/parcel_helper.cpp index 9979c2d6..32e92683 100644 --- a/frameworks/native/src/parcel_helper.cpp +++ b/frameworks/native/src/parcel_helper.cpp @@ -59,6 +59,7 @@ void ParcelHelper::UnMarshalBase(MessageParcel &data, TaskInfo &info) info.mtime = data.ReadUint64(); info.data = data.ReadString(); info.description = data.ReadString(); + info.priority = data.ReadUint32(); } bool ParcelHelper::UnMarshalFormItem(MessageParcel &data, TaskInfo &info) diff --git a/frameworks/native/src/request_service_proxy.cpp b/frameworks/native/src/request_service_proxy.cpp index d0e08b06..0a7fa440 100644 --- a/frameworks/native/src/request_service_proxy.cpp +++ b/frameworks/native/src/request_service_proxy.cpp @@ -54,6 +54,7 @@ int32_t RequestServiceProxy::Create(const Config &config, int32_t &tid, sptr(ability: &T) { - debug!(LOG_LABEL, "on_start"); + info!(LOG_LABEL, "on_start"); let service = match RequestServiceStub::new_remote_stub(RequestService) { Some(service) => service, None => { @@ -49,11 +44,13 @@ fn on_start(ability: &T) { ability.publish(&object, REQUEST_SERVICE_ID); start(); + info!(LOG_LABEL, "on_start succeed"); } -fn on_stop(ability: &T) { - debug!(LOG_LABEL, "on_stop"); +fn on_stop(_ability: &T) { + info!(LOG_LABEL, "on_stop"); stop(); + info!(LOG_LABEL, "on_stop succeed"); } fn service_start_fault() { @@ -66,7 +63,7 @@ fn service_start_fault() { DOMAIN, SERVICE_START_FAULT, EventType::Fault, - &[build_number_param!(ERROR_INFO, DOWNLOAD_PUBLISH_FAIL)] + &[build_number_param!(ERROR_INFO, DOWNLOAD_PUBLISH_FAIL)], ); } @@ -76,7 +73,7 @@ define_system_ability!(sa: SystemAbility(on_start, on_stop),); #[link_section = ".init_array"] static A: extern "C" fn() = { extern "C" fn init() { - debug!(LOG_LABEL, "request service init"); + info!(LOG_LABEL, "request service init"); let system_ability = SystemAbility::new_system_ability(REQUEST_SERVICE_ID, false) .expect("create service failed"); system_ability.register(); diff --git a/services/service/rust/BUILD.gn b/services/service/request/BUILD.gn similarity index 54% rename from services/service/rust/BUILD.gn rename to services/service/request/BUILD.gn index 12eb3c6e..acdfa48e 100644 --- a/services/service/rust/BUILD.gn +++ b/services/service/request/BUILD.gn @@ -27,74 +27,26 @@ config("download_service_config") { ] } -ohos_rust_shared_library("download_server") { - sources = [ "src/request_service_init.rs" ] - - public_configs = [ ":download_service_config" ] - - deps = [ ":request" ] - - external_deps = [ - "hilog:hilog_rust", - "hisysevent:hisysevent_rust", - "ipc:ipc_rust", - "safwk:system_ability_fwk_rust", - ] - - clippy_lints = "none" - rustflags = [ - "-Copt-level=3", - "-Cprefer-dynamic", - ] - crate_name = "download_server" - crate_type = "dylib" - subsystem_name = "request" - part_name = "request" -} - ohos_rust_shared_library("request") { - sources = [ - "src/c_string_wrapper.rs", - "src/download_server_ipc_interface_code.rs", - "src/enumration.rs", - "src/filter.rs", - "src/form_item.rs", - "src/lib.rs", - "src/log.rs", - "src/progress.rs", - "src/request_binding.rs", - "src/request_service.rs", - "src/request_service_ability.rs", - "src/request_task.rs", - "src/task_config.rs", - "src/task_info.rs", - "src/task_manager.rs", - "src/utils.rs", - ] + sources = [ "src/lib.rs" ] public_configs = [ ":download_service_config" ] deps = [ ":request_service_c" ] + features = [ "oh" ] + external_deps = [ "hilog:hilog_rust", "hisysevent:hisysevent_rust", "hitrace:hitrace_meter_rust", "ipc:ipc_rust", "netstack:ylong_http_client", - "safwk:system_ability_fwk_rust", "samgr:rust_samgr", "ylong_runtime:ylong_runtime", ] - clippy_lints = "none" - rustflags = [ - "-Copt-level=3", - "-Cprefer-dynamic", - "-Zmacro-backtrace", - ] crate_name = "request" - crate_type = "dylib" subsystem_name = "request" part_name = "request" } @@ -102,24 +54,24 @@ ohos_rust_shared_library("request") { ohos_shared_library("request_service_c") { include_dirs = [ "${request_path}/common/include", - "${request_path}/services/service/rust/src/c_wrapper/include", + "${request_path}/services/service/request/c_wrapper/include", "${core_service_path}/innerkits/include", "${cellular_data_path}/innerkits/include", "${notification_path}/interfaces/inner_api", ] sources = [ - "${request_path}/services/service/rust/src/c_wrapper/source/application_state_observer.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/background_notification.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_check_permission.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_event_handler.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_request_database.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_string_wrapper.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_task_config.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/c_task_info.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/common_event_notify.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/get_calling_bundle.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/get_top_bundle.cpp", - "${request_path}/services/service/rust/src/c_wrapper/source/network_adapter.cpp", + "${request_path}/services/service/request/c_wrapper/source/application_state_observer.cpp", + "${request_path}/services/service/request/c_wrapper/source/background_notification.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_check_permission.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_event_handler.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_request_database.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_string_wrapper.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_task_config.cpp", + "${request_path}/services/service/request/c_wrapper/source/c_task_info.cpp", + "${request_path}/services/service/request/c_wrapper/source/common_event_notify.cpp", + "${request_path}/services/service/request/c_wrapper/source/get_calling_bundle.cpp", + "${request_path}/services/service/request/c_wrapper/source/get_top_bundle.cpp", + "${request_path}/services/service/request/c_wrapper/source/network_adapter.cpp", ] cflags_cc = [ "-O2" ] diff --git a/services/service/request/Cargo.toml b/services/service/request/Cargo.toml new file mode 100644 index 00000000..9bf94357 --- /dev/null +++ b/services/service/request/Cargo.toml @@ -0,0 +1,42 @@ +# Copyright (C) 2023 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. + +[package] +name = "request" +version = "1.0.0" +edition = "2021" +license = "Apache-2.0" + +[features] +default = [] + +oh = ["hilog_rust", "hisysevent", "hitrace_meter_rust", "rust_samgr", "ipc_rust"] + +[dependencies] +ylong_runtime = { git = "https://gitee.com/openharmony/commonlibrary_rust_ylong_runtime", features = ["full"] } +ylong_http_client = { git = "https://gitee.com/openharmony/commonlibrary_rust_ylong_http", features = [ + "async", + "c_openssl_3_0", + "http1_1", + "ylong_base", +] } + +hilog_rust = { git = "https://gitee.com/openharmony/hiviewdfx_hilog.git", optional = true } +hisysevent = { git = "https://gitee.com/openharmony/hiviewdfx_hisysevent.git", optional = true } +hitrace_meter_rust = { git = "https://gitee.com/openharmony/hiviewdfx_hitrace.git", optional = true } + +rust_samgr = { git = "https://gitee.com/openharmony/systemabilitymgr_samgr.git", optional = true } +ipc_rust = { git = "https://gitee.com/openharmony/communication_ipc.git", optional = true } + +log = { version = "0.4.20" } +env_logger = { version = "0.10.1" } diff --git a/services/service/rust/src/c_wrapper/include/application_state_observer.h b/services/service/request/c_wrapper/include/application_state_observer.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/application_state_observer.h rename to services/service/request/c_wrapper/include/application_state_observer.h diff --git a/services/service/rust/src/c_wrapper/include/background_notification.h b/services/service/request/c_wrapper/include/background_notification.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/background_notification.h rename to services/service/request/c_wrapper/include/background_notification.h diff --git a/services/service/rust/src/c_wrapper/include/c_check_permission.h b/services/service/request/c_wrapper/include/c_check_permission.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_check_permission.h rename to services/service/request/c_wrapper/include/c_check_permission.h diff --git a/services/service/rust/src/c_wrapper/include/c_enumration.h b/services/service/request/c_wrapper/include/c_enumration.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_enumration.h rename to services/service/request/c_wrapper/include/c_enumration.h diff --git a/services/service/rust/src/c_wrapper/include/c_event_handler.h b/services/service/request/c_wrapper/include/c_event_handler.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_event_handler.h rename to services/service/request/c_wrapper/include/c_event_handler.h diff --git a/services/service/rust/src/c_wrapper/include/c_filter.h b/services/service/request/c_wrapper/include/c_filter.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_filter.h rename to services/service/request/c_wrapper/include/c_filter.h diff --git a/services/service/rust/src/c_wrapper/include/c_form_item.h b/services/service/request/c_wrapper/include/c_form_item.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_form_item.h rename to services/service/request/c_wrapper/include/c_form_item.h diff --git a/services/service/rust/src/c_wrapper/include/c_progress.h b/services/service/request/c_wrapper/include/c_progress.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_progress.h rename to services/service/request/c_wrapper/include/c_progress.h diff --git a/services/service/rust/src/c_wrapper/include/c_request_database.h b/services/service/request/c_wrapper/include/c_request_database.h similarity index 96% rename from services/service/rust/src/c_wrapper/include/c_request_database.h rename to services/service/request/c_wrapper/include/c_request_database.h index 70d80b8c..d4107094 100644 --- a/services/service/rust/src/c_wrapper/include/c_request_database.h +++ b/services/service/request/c_wrapper/include/c_request_database.h @@ -66,7 +66,8 @@ constexpr const char *CREATE_REQUEST_TABLE1 = "CREATE TABLE IF NOT EXISTS reques "processed TEXT, " "extras TEXT, " "form_items_len INTEGER, " - "file_specs_len INTEGER)"; + "file_specs_len INTEGER, " + "priority INTEGER)"; constexpr const char *CREATE_REQUEST_TABLE2 = "CREATE TABLE IF NOT EXISTS task_info_attachment " "(id INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -111,7 +112,8 @@ constexpr const char *CREATE_REQUEST_TABLE3 = "CREATE TABLE IF NOT EXISTS reques "version INTEGER, " "form_items_len INTEGER, " "file_specs_len INTEGER, " - "body_file_names_len INTEGER)"; + "body_file_names_len INTEGER, " + "priority INTEGER)"; constexpr const char *CREATE_REQUEST_TABLE4 = "CREATE TABLE IF NOT EXISTS task_config_attachment " "(id INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -166,6 +168,7 @@ struct CVectorWrapper { bool HasRequestTaskRecord(uint32_t taskId); bool RecordRequestTaskInfo(CTaskInfo *taskInfo); bool UpdateRequestTaskInfo(uint32_t taskId, CUpdateInfo *updateInfo); +CTaskInfo *Show(uint32_t taskId, uint64_t uid); CTaskInfo *Touch(uint32_t taskId, uint64_t uid, CStringWrapper token); CTaskInfo *Query(uint32_t taskId, Action queryAction); CVectorWrapper Search(CFilter filter); @@ -186,6 +189,7 @@ bool RecordRequestTaskConfig(CTaskConfig *taskConfig); void GetCommonTaskConfig(std::shared_ptr resultSet, TaskConfig &taskConfig); CTaskConfig **QueryAllTaskConfig(); int QueryTaskConfigLen(); +void QuerySingleTaskConfig(std::shared_ptr resultSet, TaskConfig &taskConfig); int QueryRequestTaskConfig(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, std::vector &taskConfigs); int QueryTaskConfigAttachment(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, TaskConfig &taskConfig, int64_t formItemsLen, int64_t fileSpecsLen, int64_t bodyFileNamesLen); diff --git a/services/service/rust/src/c_wrapper/include/c_string_wrapper.h b/services/service/request/c_wrapper/include/c_string_wrapper.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/c_string_wrapper.h rename to services/service/request/c_wrapper/include/c_string_wrapper.h diff --git a/services/service/rust/src/c_wrapper/include/c_task_config.h b/services/service/request/c_wrapper/include/c_task_config.h similarity index 97% rename from services/service/rust/src/c_wrapper/include/c_task_config.h rename to services/service/request/c_wrapper/include/c_task_config.h index dca963df..0e4ef902 100644 --- a/services/service/rust/src/c_wrapper/include/c_task_config.h +++ b/services/service/request/c_wrapper/include/c_task_config.h @@ -41,6 +41,7 @@ struct CommonTaskConfig { int64_t ends; bool gauge; bool precise; + uint32_t priority; bool background; }; diff --git a/services/service/rust/src/c_wrapper/include/c_task_info.h b/services/service/request/c_wrapper/include/c_task_info.h similarity index 98% rename from services/service/rust/src/c_wrapper/include/c_task_info.h rename to services/service/request/c_wrapper/include/c_task_info.h index 6f8b2b2f..bdf8d614 100644 --- a/services/service/rust/src/c_wrapper/include/c_task_info.h +++ b/services/service/request/c_wrapper/include/c_task_info.h @@ -37,6 +37,7 @@ struct CommonTaskInfo { bool retry; uint32_t tries; uint8_t version; + uint32_t priority; }; struct CEachFileStatus { diff --git a/services/service/rust/src/c_wrapper/include/common_event_notify.h b/services/service/request/c_wrapper/include/common_event_notify.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/common_event_notify.h rename to services/service/request/c_wrapper/include/common_event_notify.h diff --git a/services/service/rust/src/c_wrapper/include/get_calling_bundle.h b/services/service/request/c_wrapper/include/get_calling_bundle.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/get_calling_bundle.h rename to services/service/request/c_wrapper/include/get_calling_bundle.h diff --git a/services/service/rust/src/c_wrapper/include/get_top_bundle.h b/services/service/request/c_wrapper/include/get_top_bundle.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/get_top_bundle.h rename to services/service/request/c_wrapper/include/get_top_bundle.h diff --git a/services/service/rust/src/c_wrapper/include/network_adapter.h b/services/service/request/c_wrapper/include/network_adapter.h similarity index 100% rename from services/service/rust/src/c_wrapper/include/network_adapter.h rename to services/service/request/c_wrapper/include/network_adapter.h diff --git a/services/service/rust/src/c_wrapper/source/application_state_observer.cpp b/services/service/request/c_wrapper/source/application_state_observer.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/application_state_observer.cpp rename to services/service/request/c_wrapper/source/application_state_observer.cpp diff --git a/services/service/rust/src/c_wrapper/source/background_notification.cpp b/services/service/request/c_wrapper/source/background_notification.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/background_notification.cpp rename to services/service/request/c_wrapper/source/background_notification.cpp diff --git a/services/service/rust/src/c_wrapper/source/c_check_permission.cpp b/services/service/request/c_wrapper/source/c_check_permission.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/c_check_permission.cpp rename to services/service/request/c_wrapper/source/c_check_permission.cpp diff --git a/services/service/rust/src/c_wrapper/source/c_event_handler.cpp b/services/service/request/c_wrapper/source/c_event_handler.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/c_event_handler.cpp rename to services/service/request/c_wrapper/source/c_event_handler.cpp diff --git a/services/service/rust/src/c_wrapper/source/c_request_database.cpp b/services/service/request/c_wrapper/source/c_request_database.cpp similarity index 83% rename from services/service/rust/src/c_wrapper/source/c_request_database.cpp rename to services/service/request/c_wrapper/source/c_request_database.cpp index 28a97e09..2ba3fba8 100644 --- a/services/service/rust/src/c_wrapper/source/c_request_database.cpp +++ b/services/service/request/c_wrapper/source/c_request_database.cpp @@ -197,6 +197,7 @@ bool WriteRequestTaskInfo(CTaskInfo *taskInfo) insertValues.PutInt("retry", taskInfo->commonData.retry); insertValues.PutLong("tries", taskInfo->commonData.tries); insertValues.PutInt("version", taskInfo->commonData.version); + insertValues.PutLong("priority", taskInfo->commonData.priority); insertValues.PutString("bundle", std::string(taskInfo->bundle.cStr, taskInfo->bundle.len)); insertValues.PutString("url", std::string(taskInfo->url.cStr, taskInfo->url.len)); insertValues.PutString("data", std::string(taskInfo->data.cStr, taskInfo->data.len)); @@ -301,6 +302,26 @@ bool UpdateRequestTaskInfo(uint32_t taskId, CUpdateInfo *updateInfo) return true; } +CTaskInfo *Show(uint32_t taskId, uint64_t uid) +{ + OHOS::NativeRdb::RdbPredicates rdbPredicates1("request_task_info"); + rdbPredicates1.EqualTo("task_id", std::to_string(taskId)) + ->And() + ->EqualTo("uid", std::to_string(uid)); + int64_t formItemsLen = 0; + int64_t fileSpecsLen = 0; + TaskInfo taskInfo; + if (TouchRequestTaskInfo(rdbPredicates1, taskInfo, formItemsLen, fileSpecsLen) == OHOS::Request::QUERY_ERR) { + return nullptr; + } + OHOS::NativeRdb::RdbPredicates rdbPredicates2("task_info_attachment"); + rdbPredicates2.EqualTo("task_id", std::to_string(taskId))->And()->EqualTo("uid", std::to_string(uid)); + if (TouchTaskInfoAttachment(rdbPredicates2, taskInfo, formItemsLen, fileSpecsLen) == OHOS::Request::QUERY_ERR) { + return nullptr; + } + return BuildCTaskInfo(taskInfo); +} + CTaskInfo *Touch(uint32_t taskId, uint64_t uid, CStringWrapper token) { OHOS::NativeRdb::RdbPredicates rdbPredicates1("request_task_info"); @@ -405,27 +426,27 @@ void GetCommonTaskInfo(std::shared_ptr resultSet, Ta int64_t tries = 0; int version = 0; - resultSet->GetLong(0, taskId); + resultSet->GetLong(0, taskId); // Line 0 here is 'task_id' taskInfo.commonData.taskId = static_cast(taskId); - resultSet->GetLong(1, uid); + resultSet->GetLong(1, uid); // Line 1 here is 'uid' taskInfo.commonData.uid = static_cast(uid); - resultSet->GetInt(2, action); + resultSet->GetInt(2, action); // Line 2 here is 'action' taskInfo.commonData.action = static_cast(action); - resultSet->GetInt(3, mode); + resultSet->GetInt(3, mode); // Line 3 here is 'mode' taskInfo.commonData.mode = static_cast(mode); - resultSet->GetLong(4, ctime); + resultSet->GetLong(4, ctime); // Line 4 here is 'ctime' taskInfo.commonData.ctime = static_cast(ctime); - resultSet->GetLong(5, mtime); + resultSet->GetLong(5, mtime); // Line 5 here is 'mtime' taskInfo.commonData.mtime = static_cast(mtime); - resultSet->GetInt(6, reason); + resultSet->GetInt(6, reason); // Line 6 here is 'reason' taskInfo.commonData.reason = static_cast(reason); - resultSet->GetInt(7, gauge); + resultSet->GetInt(7, gauge); // Line 7 here is 'gauge' taskInfo.commonData.gauge = static_cast(gauge); - resultSet->GetInt(8, retry); + resultSet->GetInt(8, retry); // Line 8 here is 'retry' taskInfo.commonData.retry = static_cast(retry); - resultSet->GetLong(9, tries); + resultSet->GetLong(9, tries); // Line 9 here is 'tries' taskInfo.commonData.tries = static_cast(tries); - resultSet->GetInt(10, version); + resultSet->GetInt(10, version); // Line 10 here is 'version' taskInfo.commonData.version = static_cast(version); } @@ -435,7 +456,7 @@ int TouchRequestTaskInfo(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, Ta auto resultSet = OHOS::Request::RequestDataBase::GetInstance().Query(rdbPredicates, { "task_id", "uid", "action", "mode", "ctime", "mtime", "reason", "gauge", "retry", "tries", "version", "url", "data", "titile", "description", "mime_type", "state", "idx", "total_processed", "sizes", "processed", - "extras", "form_items_len", "file_specs_len" }); + "extras", "form_items_len", "file_specs_len", "priority" }); if (resultSet == nullptr || resultSet->GoToFirstRow() != OHOS::NativeRdb::E_OK) { REQUEST_HILOGE("result set is nullptr or go to first row failed"); return OHOS::Request::QUERY_ERR; @@ -443,23 +464,26 @@ int TouchRequestTaskInfo(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, Ta int state = 0; int64_t idx = 0; int64_t totalProcessed = 0; + int64_t priority = 0; GetCommonTaskInfo(resultSet, taskInfo); - resultSet->GetString(11, taskInfo.url); - resultSet->GetString(12, taskInfo.data); - resultSet->GetString(13, taskInfo.title); - resultSet->GetString(14, taskInfo.description); - resultSet->GetString(15, taskInfo.mimeType); - resultSet->GetInt(16, state); + resultSet->GetString(11, taskInfo.url); // Line 11 here is 'url' + resultSet->GetString(12, taskInfo.data); // Line 12 here is 'data' + resultSet->GetString(13, taskInfo.title); // Line 13 here is 'title' + resultSet->GetString(14, taskInfo.description); // Line 14 here is 'description' + resultSet->GetString(15, taskInfo.mimeType); // Line 15 here is 'mimeType' + resultSet->GetInt(16, state); // Line 16 here is 'state' taskInfo.progress.commonData.state = static_cast(state); - resultSet->GetLong(17, idx); + resultSet->GetLong(17, idx); // Line 17 here is 'idx' taskInfo.progress.commonData.index = static_cast(idx); - resultSet->GetLong(18, totalProcessed); + resultSet->GetLong(18, totalProcessed); // Line 18 here is 'totalProcessed' taskInfo.progress.commonData.totalProcessed = static_cast(totalProcessed); - resultSet->GetString(19, taskInfo.progress.sizes); - resultSet->GetString(20, taskInfo.progress.processed); - resultSet->GetString(21, taskInfo.progress.extras); - resultSet->GetLong(22, formItemsLen); - resultSet->GetLong(23, fileSpecsLen); + resultSet->GetString(19, taskInfo.progress.sizes); // Line 19 here is 'sizes' + resultSet->GetString(20, taskInfo.progress.processed); // Line 20 here is 'processed' + resultSet->GetString(21, taskInfo.progress.extras); // Line 21 here is 'extras' + resultSet->GetLong(22, formItemsLen); // Line 22 here is 'formItemsLen' + resultSet->GetLong(23, fileSpecsLen); // Line 23 here is 'fileSpecsLen' + resultSet->GetLong(24, priority); // Line 24 here is 'priority' + taskInfo.commonData.priority = static_cast(priority); resultSet->Close(); return OHOS::Request::QUERY_OK; } @@ -478,22 +502,25 @@ int QueryRequestTaskInfo(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, Ta int state = 0; int64_t idx = 0; int64_t totalProcessed = 0; + int64_t priority = 0; GetCommonTaskInfo(resultSet, taskInfo); - resultSet->GetString(11, taskInfo.bundle); - resultSet->GetString(12, taskInfo.title); - resultSet->GetString(13, taskInfo.description); - resultSet->GetString(14, taskInfo.mimeType); - resultSet->GetInt(15, state); + resultSet->GetString(11, taskInfo.bundle); // Line 11 here is 'bundle' + resultSet->GetString(12, taskInfo.title); // Line 12 here is 'title' + resultSet->GetString(13, taskInfo.description); // Line 13 here is 'description' + resultSet->GetString(14, taskInfo.mimeType); // Line 14 here is 'mimeType' + resultSet->GetInt(15, state); // Line 15 here is 'state' taskInfo.progress.commonData.state = static_cast(state); - resultSet->GetLong(16, idx); + resultSet->GetLong(16, idx); // Line 16 here is 'idx' taskInfo.progress.commonData.index = static_cast(idx); - resultSet->GetLong(17, totalProcessed); + resultSet->GetLong(17, totalProcessed); // Line 17 here is 'totalProcessed' taskInfo.progress.commonData.totalProcessed = static_cast(totalProcessed); - resultSet->GetString(18, taskInfo.progress.sizes); - resultSet->GetString(19, taskInfo.progress.processed); - resultSet->GetString(20, taskInfo.progress.extras); - resultSet->GetLong(21, formItemsLen); - resultSet->GetLong(22, fileSpecsLen); + resultSet->GetString(18, taskInfo.progress.sizes); // Line 18 here is 'sizes' + resultSet->GetString(19, taskInfo.progress.processed); // Line 19 here is 'processed' + resultSet->GetString(20, taskInfo.progress.extras); // Line 20 here is 'extras' + resultSet->GetLong(21, formItemsLen); // Line 21 here is 'formItemsLen' + resultSet->GetLong(22, fileSpecsLen); // Line 22 here is 'fileSpecsLen' + resultSet->GetLong(23, priority); // Line 23 here is 'priority' + taskInfo.commonData.priority = static_cast(priority); resultSet->Close(); return OHOS::Request::QUERY_OK; } @@ -515,25 +542,25 @@ int TouchTaskInfoAttachment(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, } if (i < formItemsLen) { FormItem formItem; - resultSet->GetString(0, formItem.name); - resultSet->GetString(1, formItem.value); + resultSet->GetString(0, formItem.name); // Line 0 here is 'name' + resultSet->GetString(1, formItem.value); // Line 1 here is 'value' taskInfo.formItems.push_back(std::move(formItem)); } if (i < fileSpecsLen) { FileSpec fileSpec; std::string path; - resultSet->GetString(2, fileSpec.name); - resultSet->GetString(3, path); - resultSet->GetString(4, fileSpec.fileName); - resultSet->GetString(5, fileSpec.mimeType); + resultSet->GetString(2, fileSpec.name); // Line 2 here is 'name' + resultSet->GetString(3, path); // Line 3 here is 'path' + resultSet->GetString(4, fileSpec.fileName); // Line 4 here is 'fileName' + resultSet->GetString(5, fileSpec.mimeType); // Line 5 here is 'mimeType' fileSpec.path = path; taskInfo.fileSpecs.push_back(std::move(fileSpec)); EachFileStatus eachFileStatus; eachFileStatus.path = std::move(path); int reason = 0; - resultSet->GetInt(6, reason); + resultSet->GetInt(6, reason); // Line 6 here is 'reason' eachFileStatus.reason = static_cast(reason); - resultSet->GetString(7, eachFileStatus.message); + resultSet->GetString(7, eachFileStatus.message); // Line 7 here is 'message' taskInfo.eachFileStatus.push_back(std::move(eachFileStatus)); } } @@ -560,9 +587,9 @@ int QueryTaskInfoAttachment(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, resultSet->GetString(0, path); eachFileStatus.path = path; int reason = 0; - resultSet->GetInt(1, reason); + resultSet->GetInt(1, reason); // Line 1 here is 'reason' eachFileStatus.reason = static_cast(reason); - resultSet->GetString(2, eachFileStatus.message); + resultSet->GetString(2, eachFileStatus.message); // Line 2 here is 'message' taskInfo.eachFileStatus.push_back(std::move(eachFileStatus)); FileSpec fileSpec; fileSpec.path = std::move(path); @@ -663,6 +690,7 @@ bool WriteRequestTaskConfig(CTaskConfig *taskConfig) insertValues.PutLong("ends", taskConfig->commonData.ends); insertValues.PutInt("gauge", taskConfig->commonData.gauge); insertValues.PutInt("precise", taskConfig->commonData.precise); + insertValues.PutLong("priority", taskConfig->commonData.priority); insertValues.PutInt("background", taskConfig->commonData.background); insertValues.PutString("bundle", std::string(taskConfig->bundle.cStr, taskConfig->bundle.len)); insertValues.PutString("url", std::string(taskConfig->url.cStr, taskConfig->url.len)); @@ -747,37 +775,37 @@ void GetCommonTaskConfig(std::shared_ptr resultSet, int precise = 0; int background = 0; - resultSet->GetLong(0, taskId); + resultSet->GetLong(0, taskId); // Line 0 here is 'taskId' taskConfig.commonData.taskId = static_cast(taskId); - resultSet->GetLong(1, uid); + resultSet->GetLong(1, uid); // Line 1 here is 'uid' taskConfig.commonData.uid = static_cast(uid); - resultSet->GetInt(2, action); + resultSet->GetInt(2, action); // Line 2 here is 'action' taskConfig.commonData.action = static_cast(action); - resultSet->GetInt(3, mode); + resultSet->GetInt(3, mode); // Line 3 here is 'mode' taskConfig.commonData.mode = static_cast(mode); - resultSet->GetInt(4, cover); + resultSet->GetInt(4, cover); // Line 4 here is 'cover' taskConfig.commonData.cover = static_cast(cover); - resultSet->GetInt(5, network); + resultSet->GetInt(5, network); // Line 5 here is 'network' taskConfig.commonData.network = static_cast(network); - resultSet->GetInt(6, meterd); + resultSet->GetInt(6, meterd); // Line 6 here is 'meterd' taskConfig.commonData.meterd = static_cast(meterd); - resultSet->GetInt(7, roaming); + resultSet->GetInt(7, roaming); // Line 7 here is 'roaming' taskConfig.commonData.roaming = static_cast(roaming); - resultSet->GetInt(8, retry); + resultSet->GetInt(8, retry); // Line 8 here is 'retry' taskConfig.commonData.retry = static_cast(retry); - resultSet->GetInt(9, redirect); + resultSet->GetInt(9, redirect); // Line 9 here is 'redirect' taskConfig.commonData.redirect = static_cast(redirect); - resultSet->GetLong(10, index); + resultSet->GetLong(10, index); // Line 10 here is 'index' taskConfig.commonData.index = static_cast(index); - resultSet->GetLong(11, begins); + resultSet->GetLong(11, begins); // Line 11 here is 'begins' taskConfig.commonData.begins = static_cast(begins); - resultSet->GetLong(12, ends); + resultSet->GetLong(12, ends); // Line 12 here is 'ends' taskConfig.commonData.ends = static_cast(ends); - resultSet->GetInt(13, gauge); + resultSet->GetInt(13, gauge); // Line 13 here is 'gauge' taskConfig.commonData.gauge = static_cast(gauge); - resultSet->GetInt(14, precise); + resultSet->GetInt(14, precise); // Line 14 here is 'precise' taskConfig.commonData.precise = static_cast(precise); - resultSet->GetInt(15, background); + resultSet->GetInt(15, background); // Line 15 here is 'background' taskConfig.commonData.background = static_cast(background); } @@ -803,13 +831,26 @@ int QueryTaskConfigLen() return len; } +void QuerySingleTaskConfig(std::shared_ptr resultSet, TaskConfig &taskConfig) +{ + resultSet->GetString(16, taskConfig.bundle); // Line 16 here is 'background' + resultSet->GetString(17, taskConfig.url); // Line 17 here is 'background' + resultSet->GetString(18, taskConfig.title); // Line 18 here is 'background' + resultSet->GetString(19, taskConfig.description); // Line 19 here is 'background' + resultSet->GetString(20, taskConfig.method); // Line 20 here is 'background' + resultSet->GetString(21, taskConfig.headers); // Line 21 here is 'background' + resultSet->GetString(22, taskConfig.data); // Line 22 here is 'background' + resultSet->GetString(23, taskConfig.token); // Line 23 here is 'background' + resultSet->GetString(24, taskConfig.extras); // Line 24 here is 'background' +} + int QueryRequestTaskConfig(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, std::vector &taskConfigs) { auto resultSet = OHOS::Request::RequestDataBase::GetInstance().Query(rdbPredicates, { "task_id", "uid", "action", "mode", "cover", "network", "meterd", "roaming", "retry", "redirect", "idx", "begins", "ends", "gauge", "precise", "background", "bundle", "url", "titile", "description", "method", "headers", "data", "token", "extras", "version", - "form_items_len", "file_specs_len", "body_file_names_len" }); + "form_items_len", "file_specs_len", "body_file_names_len", "priority" }); int rowCount = 0; if (resultSet == nullptr || resultSet->GetRowCount(rowCount) != OHOS::NativeRdb::E_OK) { REQUEST_HILOGE("TaskConfig result set is nullptr or get row count failed"); @@ -825,21 +866,16 @@ int QueryRequestTaskConfig(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, int64_t formItemsLen = 0; int64_t fileSpecsLen = 0; int64_t bodyFileNamesLen = 0; + int64_t priority = 0; GetCommonTaskConfig(resultSet, taskConfig); - resultSet->GetString(16, taskConfig.bundle); - resultSet->GetString(17, taskConfig.url); - resultSet->GetString(18, taskConfig.title); - resultSet->GetString(19, taskConfig.description); - resultSet->GetString(20, taskConfig.method); - resultSet->GetString(21, taskConfig.headers); - resultSet->GetString(22, taskConfig.data); - resultSet->GetString(23, taskConfig.token); - resultSet->GetString(24, taskConfig.extras); - resultSet->GetInt(25, version); + QuerySingleTaskConfig(resultSet, taskConfig); + resultSet->GetInt(25, version); // Line 25 here is 'background' taskConfig.version = static_cast(version); - resultSet->GetLong(26, formItemsLen); - resultSet->GetLong(27, fileSpecsLen); - resultSet->GetLong(28, bodyFileNamesLen); + resultSet->GetLong(26, formItemsLen); // Line 26 here is 'background' + resultSet->GetLong(27, fileSpecsLen); // Line 27 here is 'background' + resultSet->GetLong(28, bodyFileNamesLen); // Line 28 here is 'background' + resultSet->GetLong(29, priority); // Line 29 here is 'background' + taskConfig.commonData.priority = static_cast(priority); OHOS::NativeRdb::RdbPredicates attachPredicates("task_config_attachment"); attachPredicates.EqualTo("task_id", std::to_string(taskConfig.commonData.taskId)) ->And()->EqualTo("uid", std::to_string(taskConfig.commonData.uid)); @@ -880,16 +916,16 @@ int QueryTaskConfigAttachment(const OHOS::NativeRdb::RdbPredicates &rdbPredicate } if (i < formItemsLen) { FormItem formItem; - resultSet->GetString(0, formItem.name); - resultSet->GetString(1, formItem.value); + resultSet->GetString(0, formItem.name); // Line 0 here is 'name' + resultSet->GetString(1, formItem.value); // Line 1 here is 'value' taskConfig.formItems.push_back(std::move(formItem)); } if (i < fileSpecsLen) { FileSpec fileSpec; - resultSet->GetString(2, fileSpec.name); - resultSet->GetString(3, fileSpec.path); - resultSet->GetString(4, fileSpec.fileName); - resultSet->GetString(5, fileSpec.mimeType); + resultSet->GetString(2, fileSpec.name); // Line 2 here is 'name' + resultSet->GetString(3, fileSpec.path); // Line 3 here is 'path' + resultSet->GetString(4, fileSpec.fileName); // Line 4 here is 'fileName' + resultSet->GetString(5, fileSpec.mimeType); // Line 5 here is 'mimeType' taskConfig.fileSpecs.push_back(std::move(fileSpec)); } if (i < bodyFileNamesLen) { diff --git a/services/service/rust/src/c_wrapper/source/c_string_wrapper.cpp b/services/service/request/c_wrapper/source/c_string_wrapper.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/c_string_wrapper.cpp rename to services/service/request/c_wrapper/source/c_string_wrapper.cpp diff --git a/services/service/rust/src/c_wrapper/source/c_task_config.cpp b/services/service/request/c_wrapper/source/c_task_config.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/c_task_config.cpp rename to services/service/request/c_wrapper/source/c_task_config.cpp diff --git a/services/service/rust/src/c_wrapper/source/c_task_info.cpp b/services/service/request/c_wrapper/source/c_task_info.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/c_task_info.cpp rename to services/service/request/c_wrapper/source/c_task_info.cpp diff --git a/services/service/rust/src/c_wrapper/source/common_event_notify.cpp b/services/service/request/c_wrapper/source/common_event_notify.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/common_event_notify.cpp rename to services/service/request/c_wrapper/source/common_event_notify.cpp diff --git a/services/service/rust/src/c_wrapper/source/get_calling_bundle.cpp b/services/service/request/c_wrapper/source/get_calling_bundle.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/get_calling_bundle.cpp rename to services/service/request/c_wrapper/source/get_calling_bundle.cpp diff --git a/services/service/rust/src/c_wrapper/source/get_top_bundle.cpp b/services/service/request/c_wrapper/source/get_top_bundle.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/get_top_bundle.cpp rename to services/service/request/c_wrapper/source/get_top_bundle.cpp diff --git a/services/service/rust/src/c_wrapper/source/network_adapter.cpp b/services/service/request/c_wrapper/source/network_adapter.cpp similarity index 100% rename from services/service/rust/src/c_wrapper/source/network_adapter.cpp rename to services/service/request/c_wrapper/source/network_adapter.cpp diff --git a/services/service/request/src/error.rs b/services/service/request/src/error.rs new file mode 100644 index 00000000..171da1fe --- /dev/null +++ b/services/service/request/src/error.rs @@ -0,0 +1,28 @@ +// Copyright (C) 2023 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. + +#[derive(Clone, Copy, PartialEq, Debug)] +pub(crate) enum ErrorCode { + ErrOk = 0, + _UnloadingSA = 1, + IpcSizeTooLarge = 2, + Permission = 201, + SystemApi = 202, + ParameterCheck = 401, + FileOperationErr = 13400001, + Other = 13499999, + TaskEnqueueErr = 21900004, + _TaskModeErr, + TaskNotFound, + TaskStateErr, +} diff --git a/services/service/request/src/hilog.rs b/services/service/request/src/hilog.rs new file mode 100644 index 00000000..cd05585a --- /dev/null +++ b/services/service/request/src/hilog.rs @@ -0,0 +1,50 @@ +// Copyright (C) 2023 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. + +/// hilog label. + +macro_rules! debug { + ($($args:tt)*) => {{ + use hilog_rust::hilog; + use std::ffi::{c_char, CString}; + use $crate::LOG_LABEL; + + let log = format!($($args)*); + + hilog_rust::info!(LOG_LABEL,"{}",@public(log)); + }} +} + +macro_rules! info { + ($($args:tt)*) => {{ + use hilog_rust::hilog; + use std::ffi::{c_char, CString}; + use $crate::LOG_LABEL; + + let log = format!($($args)*); + + hilog_rust::info!(LOG_LABEL,"{}",@public(log)); + }} +} + +macro_rules! error { + ($($args:tt)*) => {{ + use hilog_rust::hilog; + use std::ffi::{c_char, CString}; + use $crate::LOG_LABEL; + + let log = format!($($args)*); + + hilog_rust::error!(LOG_LABEL,"{}",@public(log)); + }} +} diff --git a/services/service/request/src/lib.rs b/services/service/request/src/lib.rs new file mode 100644 index 00000000..57ab3f6c --- /dev/null +++ b/services/service/request/src/lib.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2023 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. + +//! This create implement the request proxy and stub + +#![warn(missing_docs, unreachable_pub)] +#![warn(clippy::redundant_clone, clippy::redundant_static_lifetimes)] +#![cfg_attr(not(feature = "oh"), allow(unused))] + +#[macro_use] +mod macros; + +#[cfg(not(feature = "oh"))] +#[allow(unused_imports)] +#[macro_use] +extern crate log; + +#[cfg(feature = "oh")] +#[macro_use] +mod hilog; + +mod error; +mod manager; +mod task; +mod utils; + +#[cfg(test)] +mod tests; + +cfg_oh! { + mod trace; + mod sys_event; + mod service; + pub use service::{RequestService, RequestServiceStub}; + + /// hilog label + pub const LOG_LABEL: hilog_rust::HiLogLabel = hilog_rust::HiLogLabel { + log_type: hilog_rust::LogType::LogCore, + domain: 0xD001C00, + tag: "RequestService", + }; + + /// Starts DownloadAbility. + pub fn start() { + info!("Download Ability prepared to be inited"); + service::ability::RequestAbility::init(); + info!("Download Ability inited"); + } + + /// Starts DownloadAbility. + pub fn stop() { + info!("Download Ability prepared to be stopped"); + service::ability::RequestAbility::stop(); + info!("Download Ability stopped"); + } + +} diff --git a/services/service/request/src/macros.rs b/services/service/request/src/macros.rs new file mode 100644 index 00000000..9034d2c2 --- /dev/null +++ b/services/service/request/src/macros.rs @@ -0,0 +1,21 @@ +// Copyright (C) 2023 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. + +macro_rules! cfg_oh { + ($($item:item)*) => { + $( + #[cfg(feature = "oh")] + $item + )* + } +} diff --git a/services/service/request/src/manager/events/construct.rs b/services/service/request/src/manager/events/construct.rs new file mode 100644 index 00000000..511e721c --- /dev/null +++ b/services/service/request/src/manager/events/construct.rs @@ -0,0 +1,160 @@ +// Copyright (C) 2023 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. + +use std::collections::HashSet; +use std::fs::File; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; + +use crate::error::ErrorCode; +use crate::manager::TaskManager; +use crate::task::config::{TaskConfig, Version}; +use crate::task::info::State; +use crate::task::reason::Reason; +use crate::task::RequestTask; + +const MAX_TASK_COUNT: u32 = 300; +const MAX_TASK_COUNT_EACH_APP: u8 = 10; + +impl TaskManager { + pub(crate) fn construct_task( + &mut self, + config: TaskConfig, + files: Vec, + body_files: Vec, + ) -> ErrorCode { + if files.is_empty() { + return ErrorCode::FileOperationErr; + } + + let uid = config.common_data.uid; + let task_id = config.common_data.task_id; + let version = config.version; + + debug!( + "TaskManager Construct, uid:{}, task_id:{}, version:{:?}", + uid, task_id, version + ); + + let app_state = self.app_state(uid, &config.bundle); + + let task = RequestTask::constructor( + config, + files, + body_files, + self.recording_rdb_num.clone(), + AtomicBool::new(false), + app_state, + ); + + match task.conf.version { + Version::API10 => { + if !self.add_task_api10(Arc::new(task)) { + return ErrorCode::TaskEnqueueErr; + } + self.api10_background_task_count += 1; + } + Version::API9 => { + self.add_task_api9(Arc::new(task)); + } + } + + if let Some(handle) = self.unload_handle.take() { + debug!("TaskManager close sa timing abort"); + handle.cancel(); + } + + ErrorCode::ErrOk + } + + pub(crate) fn add_task_api9(&mut self, task: Arc) { + task.set_status(State::Initialized, Reason::Default); + + let task_id = task.conf.common_data.task_id; + let uid = task.conf.common_data.uid; + + self.tasks.insert(task_id, task); + + match self.app_task_map.get_mut(&uid) { + Some(set) => { + set.insert(task_id); + + debug!( + "TaskManager app {} task count:{}, all task count {}", + uid, + set.len(), + self.tasks.len() + ); + } + None => { + let mut set = HashSet::new(); + set.insert(task_id); + self.app_task_map.insert(uid, set); + debug!( + "TaskManager app {} task count:{}, all task count {}", + uid, + 1, + self.tasks.len() + ); + } + } + } + + pub(crate) fn add_task_api10(&mut self, task: Arc) -> bool { + let task_id = task.conf.common_data.task_id; + let uid = task.conf.common_data.uid; + + if self.api10_background_task_count >= MAX_TASK_COUNT { + error!("TaskManager add v10 task failed, the number of tasks has reached the limit in the system"); + return false; + } + + match self.app_task_map.get_mut(&uid) { + Some(set) => { + if (set.len() as u8) == MAX_TASK_COUNT_EACH_APP { + error!( + "TaskManager add v10 task failed, the maximum value for each application processing task has been reached"); + return false; + } + set.insert(task_id); + + task.set_status(State::Initialized, Reason::Default); + self.tasks.insert(task_id, task); + + debug!( + "TaskManager app {} task count:{}, all task count {}", + uid, + set.len(), + self.tasks.len() + ); + true + } + None => { + let mut set = HashSet::new(); + set.insert(task_id); + self.app_task_map.insert(uid, set); + + task.set_status(State::Initialized, Reason::Default); + self.tasks.insert(task_id, task); + + debug!( + "TaskManager app {} task count:{}, all task count {}", + uid, + 1, + self.tasks.len() + ); + true + } + } + } +} diff --git a/services/service/request/src/manager/events/dump.rs b/services/service/request/src/manager/events/dump.rs new file mode 100644 index 00000000..fbed9a74 --- /dev/null +++ b/services/service/request/src/manager/events/dump.rs @@ -0,0 +1,46 @@ +// Copyright (C) 2023 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. + +use crate::manager::TaskManager; +use crate::task::info::{DumpAllEachInfo, DumpAllInfo, DumpOneInfo}; + +impl TaskManager { + pub(crate) fn query_one_task(&self, task_id: u32) -> Option { + self.tasks.get(&task_id).map(|task| DumpOneInfo { + task_id: task.conf.common_data.task_id, + action: task.conf.common_data.action, + state: task.status.lock().unwrap().state, + reason: task.status.lock().unwrap().reason, + total_size: task + .file_total_size + .load(std::sync::atomic::Ordering::SeqCst), + tran_size: task.progress.lock().unwrap().common_data.total_processed, + url: task.conf.url.clone(), + }) + } + + pub(crate) fn query_all_task(&self) -> DumpAllInfo { + DumpAllInfo { + vec: self + .tasks + .values() + .map(|task| DumpAllEachInfo { + task_id: task.conf.common_data.task_id, + action: task.conf.common_data.action, + state: task.status.lock().unwrap().state, + reason: task.status.lock().unwrap().reason, + }) + .collect(), + } + } +} diff --git a/services/service/request/src/manager/events/mod.rs b/services/service/request/src/manager/events/mod.rs new file mode 100644 index 00000000..48e4c97d --- /dev/null +++ b/services/service/request/src/manager/events/mod.rs @@ -0,0 +1,300 @@ +// Copyright (C) 2023 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. + +use std::fmt::Debug; +use std::fs::File; + +use ylong_runtime::sync::oneshot::{channel, Sender}; + +mod construct; +mod dump; +mod pause; +mod query; +mod query_mime_type; +mod remove; +mod resume; +mod search; +mod show; +mod start; +mod stop; +mod touch; + +use crate::error::ErrorCode; +use crate::task::config::{Action, TaskConfig}; +use crate::task::info::{ApplicationState, DumpAllInfo, DumpOneInfo, TaskInfo}; +use crate::utils::filter::Filter; +use crate::utils::Recv; + +#[derive(Debug)] +pub(crate) enum EventMessage { + Service(ServiceMessage), + State(StateMessage), + Scheduled(ScheduledMessage), + Task(TaskMessage), +} + +impl EventMessage { + pub(crate) fn construct( + config: TaskConfig, + files: Vec, + body_files: Vec, + ) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Construct( + Box::new(ConstructMessage { + config, + files, + body_files, + }), + tx, + )), + Recv::new(rx), + ) + } + + pub(crate) fn pause(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Pause(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn query(task_id: u32, action: Action) -> (Self, Recv>) { + let (tx, rx) = channel::>(); + ( + Self::Service(ServiceMessage::Query(task_id, action, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn query_mime_type(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::QueryMimeType(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn start(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Start(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn stop(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Stop(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn show(uid: u64, task_id: u32) -> (Self, Recv>) { + let (tx, rx) = channel::>(); + ( + Self::Service(ServiceMessage::Show(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn search(filter: Filter) -> (Self, Recv>) { + let (tx, rx) = channel::>(); + ( + Self::Service(ServiceMessage::Search(filter, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn touch(uid: u64, task_id: u32, token: String) -> (Self, Recv>) { + let (tx, rx) = channel::>(); + ( + Self::Service(ServiceMessage::Touch(uid, task_id, token, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn remove(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Remove(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn resume(uid: u64, task_id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + ( + Self::Service(ServiceMessage::Resume(uid, task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn dump_all() -> (Self, Recv) { + let (tx, rx) = channel::(); + (Self::Service(ServiceMessage::DumpAll(tx)), Recv::new(rx)) + } + + pub(crate) fn dump_one(task_id: u32) -> (Self, Recv>) { + let (tx, rx) = channel::>(); + ( + Self::Service(ServiceMessage::DumpOne(task_id, tx)), + Recv::new(rx), + ) + } + + pub(crate) fn app_state_change(uid: u64, state: ApplicationState) -> Self { + Self::State(StateMessage::AppStateChange(uid, state)) + } + + pub(crate) fn network_change() -> Self { + Self::State(StateMessage::NetworkChange) + } +} + +pub(crate) enum ServiceMessage { + Construct(Box, Sender), + Pause(u64, u32, Sender), + QueryMimeType(u64, u32, Sender), + Start(u64, u32, Sender), + Stop(u64, u32, Sender), + Show(u64, u32, Sender>), + Remove(u64, u32, Sender), + Resume(u64, u32, Sender), + Touch(u64, u32, String, Sender>), + Query(u32, Action, Sender>), + DumpOne(u32, Sender>), + Search(Filter, Sender>), + DumpAll(Sender), +} + +pub(crate) enum TaskMessage { + Finished(u32), +} + +pub(crate) enum StateMessage { + NetworkChange, + AppStateChange(u64, ApplicationState), +} + +pub(crate) struct ConstructMessage { + pub(crate) config: TaskConfig, + pub(crate) files: Vec, + pub(crate) body_files: Vec, +} + +impl Debug for ConstructMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Construct") + .field("uid", &self.config.common_data.uid) + .field("task_id", &self.config.common_data.task_id) + .field("title", &self.config.title) + .field("mode", &self.config.method) + .field("version", &self.config.version) + .finish() + } +} + +impl Debug for ServiceMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Construct(message, _) => message.fmt(f), + Self::Pause(uid, task_id, _) => f + .debug_struct("Pause") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::QueryMimeType(uid, task_id, _) => f + .debug_struct("QueryMimeType") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Start(uid, task_id, _) => f + .debug_struct("Start") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Stop(uid, task_id, _) => f + .debug_struct("Stop") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Show(uid, task_id, _) => f + .debug_struct("Show") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Remove(uid, task_id, _) => f + .debug_struct("Remove") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Resume(uid, task_id, _) => f + .debug_struct("Resume") + .field("uid", uid) + .field("task_id", task_id) + .finish(), + Self::Touch(uid, task_id, token, _) => f + .debug_struct("Touch") + .field("uid", uid) + .field("task_id", task_id) + .field("token", token) + .finish(), + Self::Query(task_id, action, _) => f + .debug_struct("Query") + .field("task_id", task_id) + .field("action", action) + .finish(), + Self::DumpOne(task_id, _) => { + f.debug_struct("DumpOne").field("task_id", task_id).finish() + } + Self::Search(filter, _) => f.debug_struct("Search").field("filter", filter).finish(), + Self::DumpAll(_) => f.debug_struct("DumpAll").finish(), + } + } +} + +impl Debug for TaskMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Finished(task_id) => f + .debug_struct("Finished") + .field("task_id", task_id) + .finish(), + } + } +} + +impl Debug for StateMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NetworkChange => f.pad("NetworkChange"), + Self::AppStateChange(uid, state) => f + .debug_struct("AppStateChange") + .field("uid", uid) + .field("state", state) + .finish(), + } + } +} + +#[derive(Debug)] +pub(crate) enum ScheduledMessage { + ClearTimeoutTasks, + LogTasks, + Unload, + UpdateBackgroundApp(u64), +} diff --git a/services/service/request/src/manager/events/pause.rs b/services/service/request/src/manager/events/pause.rs new file mode 100644 index 00000000..b7fc4cec --- /dev/null +++ b/services/service/request/src/manager/events/pause.rs @@ -0,0 +1,37 @@ +// Copyright (C) 2023 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. + +use crate::error::ErrorCode; +use crate::manager::TaskManager; +use crate::task::reason::Reason; + +impl TaskManager { + pub(crate) fn pause(&mut self, uid: u64, task_id: u32) -> ErrorCode { + debug!("TaskManager pause, uid:{}, task_id:{}", uid, task_id); + + match self.get_task(uid, task_id) { + Some(task) => self.pause_task(task, Reason::UserOperation), + None => { + if self.tasks.contains_key(&task_id) { + error!("TaskManager pause a task, task_id:{} exist, but not found in app_task_map, uid:{}", task_id, uid); + } else { + error!( + "TaskManager pause a task, uid:{}, task_id:{} not exist", + uid, task_id + ); + } + ErrorCode::TaskStateErr + } + } + } +} diff --git a/services/service/request/src/manager/events/query.rs b/services/service/request/src/manager/events/query.rs new file mode 100644 index 00000000..c5a236ea --- /dev/null +++ b/services/service/request/src/manager/events/query.rs @@ -0,0 +1,53 @@ +// Copyright (C) 2023 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. + +use crate::manager::TaskManager; +use crate::task::config::Action; +use crate::task::ffi::{CTaskInfo, DeleteCTaskInfo}; +use crate::task::info::TaskInfo; + +impl TaskManager { + pub(crate) fn query(&self, task_id: u32, query_action: Action) -> Option { + debug!( + "TaskManager query, task_id:{}, query_action:{:?}", + task_id, query_action + ); + + if let Some(task) = self.tasks.get(&task_id) { + if task.conf.common_data.action == query_action || query_action == Action::Any { + debug!("query task info by memory"); + let mut task_info = task.show(); + task_info.data = "".to_string(); + task_info.url = "".to_string(); + debug!("query task info is {:?}", task_info); + return Some(task_info); + } + } + + debug!("query task info by database"); + let c_task_info = unsafe { Query(task_id, query_action) }; + if c_task_info.is_null() { + return None; + } + let c_task_info = unsafe { &*c_task_info }; + let task_info = TaskInfo::from_c_struct(c_task_info); + debug!("query task info is {:?}", task_info); + unsafe { DeleteCTaskInfo(c_task_info) }; + Some(task_info) + } +} + +extern "C" { + + pub(crate) fn Query(taskId: u32, queryAction: Action) -> *const CTaskInfo; +} diff --git a/services/service/request/src/manager/events/query_mime_type.rs b/services/service/request/src/manager/events/query_mime_type.rs new file mode 100644 index 00000000..9433e597 --- /dev/null +++ b/services/service/request/src/manager/events/query_mime_type.rs @@ -0,0 +1,48 @@ +// Copyright (C) 2023 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. + +use super::show::Show; +use crate::manager::TaskManager; +use crate::task::ffi::DeleteCTaskInfo; +use crate::task::info::TaskInfo; + +impl TaskManager { + pub(crate) fn query_mime_type(&self, uid: u64, task_id: u32) -> String { + debug!( + "TaskManager query mime type, uid:{}, task_id:{}", + uid, task_id + ); + + let task = self.get_task(uid, task_id); + match task { + Some(value) => { + debug!("TaskManager query mime type by memory"); + value.query_mime_type() + } + None => { + debug!("TaskManager query mime type: show mime type from database"); + let c_task_info = unsafe { Show(task_id, uid) }; + if c_task_info.is_null() { + info!("TaskManger query mime type: no task found in database"); + return "".into(); + } + let c_task_info = unsafe { &*c_task_info }; + let task_info = TaskInfo::from_c_struct(c_task_info); + let mime_type = task_info.mime_type; + debug!("TaskManager query mime type: mime type is {:?}", mime_type); + unsafe { DeleteCTaskInfo(c_task_info) }; + mime_type + } + } + } +} diff --git a/services/service/request/src/manager/events/remove.rs b/services/service/request/src/manager/events/remove.rs new file mode 100644 index 00000000..3ba1de45 --- /dev/null +++ b/services/service/request/src/manager/events/remove.rs @@ -0,0 +1,41 @@ +// Copyright (C) 2023 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. + +use crate::error::ErrorCode; +use crate::manager::TaskManager; +use crate::task::info::State; +use crate::task::reason::Reason; + +impl TaskManager { + pub(crate) fn remove(&mut self, uid: u64, task_id: u32) -> ErrorCode { + if let Some(task) = self.get_task(uid, task_id) { + task.set_status(State::Removed, Reason::UserOperation); + self.after_task_processed(&task); + debug!( + "TaskManager remove a task, uid:{}, task_id:{} success", + uid, task_id + ); + ErrorCode::ErrOk + } else { + if self.tasks.contains_key(&task_id) { + error!("TaskManager remove a task, task_id:{} exist, but not found in app_task_map, uid:{}", task_id, uid); + } else { + error!( + "TaskManager remove a task, uid:{}, task_id:{} not exist", + uid, task_id + ); + } + ErrorCode::TaskNotFound + } + } +} diff --git a/services/service/request/src/manager/events/resume.rs b/services/service/request/src/manager/events/resume.rs new file mode 100644 index 00000000..244b46cf --- /dev/null +++ b/services/service/request/src/manager/events/resume.rs @@ -0,0 +1,59 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::Ordering; + +use crate::error::ErrorCode; +use crate::manager::TaskManager; +use crate::task::info::State; + +cfg_oh! { + use crate::manager::Notifier; +} + +impl TaskManager { + pub(crate) fn resume(&mut self, uid: u64, task_id: u32) -> ErrorCode { + debug!("TaskManager resume, uid:{}, task_id:{}", uid, task_id); + + if let Some(task) = self.get_task(uid, task_id) { + let state = task.status.lock().unwrap().state; + if state != State::Paused { + error!("can not resume a task which state is not paused"); + return ErrorCode::TaskStateErr; + } + error!("resume the task success"); + task.resume.store(true, Ordering::SeqCst); + let notify_data = task.build_notify_data(); + + #[cfg(feature = "oh")] + Notifier::service_front_notify( + "resume".into(), + notify_data, + &self.app_state(task.conf.common_data.uid, &task.conf.bundle), + ); + self.start_inner(task); + ErrorCode::ErrOk + } else { + if self.tasks.contains_key(&task_id) { + error!("TaskManager resume a task, task_id:{} exist, but not found in app_task_map, uid:{}", task_id, uid); + } else { + error!( + "TaskManager resume a task, uid:{}, task_id:{} not exist", + uid, task_id + ); + } + + ErrorCode::TaskStateErr + } + } +} diff --git a/services/service/request/src/manager/events/search.rs b/services/service/request/src/manager/events/search.rs new file mode 100644 index 00000000..817cf22d --- /dev/null +++ b/services/service/request/src/manager/events/search.rs @@ -0,0 +1,43 @@ +// Copyright (C) 2023 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. + +use crate::manager::TaskManager; +use crate::utils::c_wrapper::{CFilter, CVectorWrapper, DeleteCVectorWrapper}; +use crate::utils::filter::Filter; + +impl TaskManager { + pub(crate) fn search(&self, filter: Filter) -> Vec { + debug!("TaskManager search a task, filter:{:?}", filter); + + let mut vec = Vec::::new(); + let c_vector_wrapper = unsafe { Search(filter.to_c_struct()) }; + if c_vector_wrapper.ptr.is_null() || c_vector_wrapper.len == 0 { + error!("c_vector_wrapper is null"); + return vec; + } + let slice = unsafe { + std::slice::from_raw_parts(c_vector_wrapper.ptr, c_vector_wrapper.len as usize) + }; + for item in slice.iter() { + vec.push(*item); + } + debug!("c_vector_wrapper is not null"); + unsafe { DeleteCVectorWrapper(c_vector_wrapper.ptr) }; + vec + } +} + +extern "C" { + + pub(crate) fn Search(filter: CFilter) -> CVectorWrapper; +} diff --git a/services/service/request/src/manager/events/show.rs b/services/service/request/src/manager/events/show.rs new file mode 100644 index 00000000..6ff05e64 --- /dev/null +++ b/services/service/request/src/manager/events/show.rs @@ -0,0 +1,45 @@ +// Copyright (C) 2023 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. + +use crate::manager::TaskManager; +use crate::task::ffi::{CTaskInfo, DeleteCTaskInfo}; +use crate::task::info::TaskInfo; + +impl TaskManager { + pub(crate) fn show(&self, uid: u64, task_id: u32) -> Option { + match self.get_task(uid, task_id) { + Some(value) => { + debug!("TaskManager show, uid:{}, task_id:{} success", uid, task_id); + let task_info = value.show(); + Some(task_info) + } + None => { + debug!("TaskManager show: show task info from database"); + let c_task_info = unsafe { Show(task_id, uid) }; + if c_task_info.is_null() { + info!("TaskManger show: no task found in database"); + return None; + } + let c_task_info = unsafe { &*c_task_info }; + let task_info = TaskInfo::from_c_struct(c_task_info); + debug!("TaskManager show: task info is {:?}", task_info); + unsafe { DeleteCTaskInfo(c_task_info) }; + Some(task_info) + } + } + } +} + +extern "C" { + pub(crate) fn Show(task_id: u32, uid: u64) -> *const CTaskInfo; +} diff --git a/services/service/request/src/manager/events/start.rs b/services/service/request/src/manager/events/start.rs new file mode 100644 index 00000000..f0b2b2b6 --- /dev/null +++ b/services/service/request/src/manager/events/start.rs @@ -0,0 +1,153 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use crate::error::ErrorCode; +use crate::manager::events::{EventMessage, TaskMessage}; +use crate::manager::TaskManager; +use crate::task::config::Version; +use crate::task::info::{ApplicationState, State}; +use crate::task::reason::Reason; +use crate::task::request_task::run; +use crate::task::RequestTask; +const MAX_RUNNING_TASK_COUNT_EACH_APP: u32 = 5; // api10 +const MAX_RUNNING_TASK_COUNT_API9: u32 = 4; + +impl TaskManager { + pub(crate) fn start(&mut self, uid: u64, task_id: u32) -> ErrorCode { + info!("start a task, which task id is {}", task_id); + + if let Some(task) = self.get_task(uid, task_id) { + let task_state = task.status.lock().unwrap().state; + if task_state != State::Initialized { + error!("can not start a task which state is {}", task_state as u32); + return ErrorCode::TaskStateErr; + } + self.start_inner(task); + ErrorCode::ErrOk + } else { + if self.tasks.contains_key(&task_id) { + error!("TaskManager start a task, task_id:{} exist, but not found in app_task_map, uid:{}", task_id, uid); + } else { + error!( + "TaskManager start a task, uid:{}, task_id:{} not exist", + uid, task_id + ); + } + ErrorCode::TaskStateErr + } + } + + pub(crate) fn start_inner(&mut self, task: Arc) { + if !task.net_work_online() || !task.check_net_work_status() { + error!("check net work failed"); + self.after_task_processed(&task); + return; + } + let state = task.status.lock().unwrap().state; + if state != State::Initialized && state != State::Waiting && state != State::Paused { + self.after_task_processed(&task); + return; + } + + if self.reach_maximum_running_limit(task.conf.common_data.uid, task.conf.version) { + info!("too many task in running state"); + task.set_status(State::Waiting, Reason::RunningTaskMeetLimits); + self.after_task_processed(&task); + return; + } + + let (state, reason) = { + let status = task.status.lock().unwrap(); + (status.state, status.reason) + }; + if state == State::Waiting + && (reason == Reason::NetWorkOffline || reason == Reason::UnSupportedNetWorkType) + { + task.retry.store(true, Ordering::SeqCst); + task.tries.fetch_add(1, Ordering::SeqCst); + task.set_status(State::Retrying, Reason::Default); + } else { + task.set_status(State::Running, Reason::Default); + } + + let task_id = task.conf.common_data.task_id; + + let tx = self.tx.clone(); + + let state = ApplicationState::from( + self.app_state(task.conf.common_data.uid, &task.conf.bundle) + .load(Ordering::Relaxed), + ); + + let qos_changes = self.qos.insert(&task, state); + + self.change_qos(qos_changes); + + ylong_runtime::spawn(async move { + run(task.clone()).await; + tx.send(EventMessage::Task(TaskMessage::Finished( + task.conf.common_data.task_id, + ))) + }); + + info!("task {} start success", task_id); + } + + fn reach_maximum_running_limit(&self, uid: u64, version: Version) -> bool { + match version { + Version::API10 => { + let mut count = 0; + let tasks = match self.app_task_map.get(&uid) { + Some(v) => v, + None => return false, + }; + for task in tasks { + let request_task = match self.tasks.get(task) { + Some(task) => task, + None => { + error!("TaskManager reach_maximum_running_limit task_id:{} not found in uid:{}", task, uid); + continue; + } + }; + if request_task.conf.version == Version::API10 { + let state = request_task.status.lock().unwrap().state; + if state == State::Retrying || state == State::Running { + count += 1; + } + if count >= MAX_RUNNING_TASK_COUNT_EACH_APP { + return true; + } + } + } + } + Version::API9 => { + let mut count = 0; + for request_task in self.tasks.values() { + if request_task.conf.version == Version::API9 { + let state = request_task.status.lock().unwrap().state; + if state == State::Retrying || state == State::Running { + count += 1; + } + if count >= MAX_RUNNING_TASK_COUNT_API9 { + return true; + } + } + } + } + } + false + } +} diff --git a/services/service/request/src/manager/events/stop.rs b/services/service/request/src/manager/events/stop.rs new file mode 100644 index 00000000..3bcefb16 --- /dev/null +++ b/services/service/request/src/manager/events/stop.rs @@ -0,0 +1,51 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::Ordering; + +use crate::error::ErrorCode; +use crate::manager::TaskManager; +use crate::task::info::State; +use crate::task::reason::Reason; + +impl TaskManager { + pub(crate) fn stop(&mut self, uid: u64, task_id: u32) -> ErrorCode { + if let Some(task) = self.get_task(uid, task_id) { + if !task.set_status(State::Stopped, Reason::UserOperation) { + let state = task.status.lock().unwrap().state; + error!( + "TaskManager can not stop task_id: {} that state is {:?}", + task_id, state + ); + return ErrorCode::TaskStateErr; + } + self.after_task_processed(&task); + debug!( + "TaskManager stop a task, uid: {}, task_id:{} success", + uid, task_id + ); + task.resume.store(false, Ordering::SeqCst); + ErrorCode::ErrOk + } else { + if self.tasks.contains_key(&task_id) { + error!("TaskManager stop a task, task_id:{} exist, but not found in app_task_map, uid:{}", task_id, uid); + } else { + error!( + "TaskManager stop a task, uid:{}, task_id:{} not exist", + uid, task_id + ); + } + ErrorCode::TaskStateErr + } + } +} diff --git a/services/service/request/src/manager/events/touch.rs b/services/service/request/src/manager/events/touch.rs new file mode 100644 index 00000000..c3c2bbf0 --- /dev/null +++ b/services/service/request/src/manager/events/touch.rs @@ -0,0 +1,52 @@ +// Copyright (C) 2023 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. + +use crate::manager::TaskManager; +use crate::task::ffi::{CTaskInfo, DeleteCTaskInfo}; +use crate::task::info::TaskInfo; +use crate::utils::c_wrapper::CStringWrapper; + +impl TaskManager { + pub(crate) fn touch(&self, uid: u64, task_id: u32, token: String) -> Option { + debug!("TaskManager touch a task, uid:{}, task_id:{}", uid, task_id); + + match self.get_task(uid, task_id) { + Some(value) => { + debug!("touch task info by memory"); + if value.conf.token.eq(token.as_str()) { + let mut task_info = value.show(); + task_info.bundle = "".to_string(); + return Some(task_info); + } + None + } + None => { + debug!("TaskManger touch: touch task_info from database"); + let c_task_info = unsafe { Touch(task_id, uid, CStringWrapper::from(&token)) }; + if c_task_info.is_null() { + info!("TaskManger touch: no task found in database"); + return None; + } + let c_task_info = unsafe { &*c_task_info }; + let task_info = TaskInfo::from_c_struct(c_task_info); + debug!("TaskManger touch: task info is {:?}", task_info); + unsafe { DeleteCTaskInfo(c_task_info) }; + Some(task_info) + } + } + } +} + +extern "C" { + pub(crate) fn Touch(taskId: u32, uid: u64, token: CStringWrapper) -> *const CTaskInfo; +} diff --git a/services/service/request/src/manager/mod.rs b/services/service/request/src/manager/mod.rs new file mode 100644 index 00000000..1f132d47 --- /dev/null +++ b/services/service/request/src/manager/mod.rs @@ -0,0 +1,28 @@ +// Copyright (C) 2023 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. + +pub(crate) mod task_manager; +pub(crate) use task_manager::TaskManager; + +pub(crate) mod monitor; +mod unload; + +pub(crate) mod events; +pub(crate) mod qos; +pub(crate) mod scheduled; + +cfg_oh! { + mod notifier; + + pub(crate) use notifier::Notifier; +} diff --git a/services/service/request/src/manager/monitor.rs b/services/service/request/src/manager/monitor.rs new file mode 100644 index 00000000..cd4744ea --- /dev/null +++ b/services/service/request/src/manager/monitor.rs @@ -0,0 +1,226 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::Arc; + +use super::TaskManager; +use crate::manager::scheduled; +use crate::task::config::Action; +use crate::task::info::{ApplicationState, Mode, State}; +use crate::task::reason::Reason; + +cfg_oh! { + use crate::manager::Notifier; +} + +impl TaskManager { + pub(crate) fn update_app_state(&mut self, uid: u64, state: ApplicationState) { + if self.app_task_map.get(&uid).is_none() { + return; + } + + match state { + ApplicationState::Foreground => { + match self.app_state_map.get(&uid) { + Some(state) => { + state.store(ApplicationState::Foreground as u8, Ordering::SeqCst) + } + None => { + self.app_state_map.insert( + uid, + Arc::new(AtomicU8::new(ApplicationState::Foreground as u8)), + ); + } + } + let qos_changes = self.qos.change_state(uid, state); + self.change_qos(qos_changes); + + self.update_foreground_app(uid); + } + + ApplicationState::Background => { + match self.app_state_map.get(&uid) { + Some(state) => { + state.store(ApplicationState::Background as u8, Ordering::SeqCst) + } + None => { + self.app_state_map.insert( + uid, + Arc::new(AtomicU8::new(ApplicationState::Background as u8)), + ); + } + } + + let tx = self.tx.clone(); + ylong_runtime::spawn(scheduled::update_background_app(uid, tx)); + + let qos_changes = self.qos.change_state(uid, state); + self.change_qos(qos_changes); + } + + ApplicationState::Terminated => { + match self.app_state_map.get(&uid) { + Some(state) => { + state.store(ApplicationState::Terminated as u8, Ordering::SeqCst) + } + None => { + self.app_state_map.insert( + uid, + Arc::new(AtomicU8::new(ApplicationState::Terminated as u8)), + ); + } + } + + let qos_changes = self.qos.change_state(uid, state); + self.change_qos(qos_changes); + + self.update_terminated_app(uid); + } + } + } + + fn update_foreground_app(&mut self, uid: u64) { + debug!("TaskManager begin update_foreground_app uid:{}", uid); + + let tasks = match self.app_task_map.get(&uid) { + Some(set) => { + let mut v = vec![]; + for task_id in set { + match self.tasks.get(task_id) { + Some(task) => { + if task.conf.common_data.mode == Mode::FrontEnd { + v.push(task.clone()) + } + } + None => { + error!("TaskManager update_foreground_app uid:{}, task_id:{} not found int tasks", uid, task_id); + return; + } + } + } + v + } + None => { + error!("TaskManager update_foreground_app uid:{} not found", uid); + return; + } + }; + + tasks.into_iter().for_each(|task| { + let state = task.status.lock().unwrap().state; + let reason = task.status.lock().unwrap().reason; + if state == State::Paused && reason == Reason::AppBackgroundOrTerminate { + info!("Begin try resume task as app switch to background"); + task.resume.store(true, Ordering::SeqCst); + + let notify_data = task.build_notify_data(); + + #[cfg(feature = "oh")] + Notifier::service_front_notify( + "resume".into(), + notify_data, + &self.app_state(uid, &task.conf.bundle), + ); + self.start_inner(task); + } + }); + } + + pub(crate) fn update_background_app(&mut self, uid: u64) { + debug!("TaskManager begin update_background_app uid:{}", uid); + + if self.app_task_map.get(&uid).is_none() { + return; + } + + let tasks = match self.app_task_map.get(&uid) { + Some(set) => { + let mut v = vec![]; + for task_id in set { + match self.tasks.get(task_id) { + Some(task) => { + if task.conf.common_data.mode == Mode::FrontEnd { + v.push(task.clone()) + } + } + None => { + error!("TaskManager update_foreground_app uid:{}, task_id:{} not found int tasks", uid, task_id); + return; + } + } + } + v + } + None => { + error!("TaskManager update_foreground_app uid:{} not found", uid); + return; + } + }; + tasks.into_iter().for_each(|task| { + if task.conf.common_data.action == Action::UpLoad { + task.set_status(State::Failed, Reason::AppBackgroundOrTerminate); + self.after_task_processed(&task); + } else if task.conf.common_data.action == Action::DownLoad { + self.pause_task(task, Reason::AppBackgroundOrTerminate); + } + }); + } + + fn update_terminated_app(&mut self, uid: u64) { + debug!("TaskManager begin update_terminated_app uid:{}", uid); + + let tasks = match self.app_task_map.get(&uid) { + Some(set) => { + let mut v = vec![]; + for task_id in set { + match self.tasks.get(task_id) { + Some(task) => { + if task.conf.common_data.mode == Mode::FrontEnd { + v.push(task.clone()) + } + } + None => { + error!("TaskManager update_foreground_app uid:{}, task_id:{} not found int tasks", uid, task_id); + return; + } + } + } + v + } + None => { + error!("TaskManager update_foreground_app uid:{} not found", uid); + return; + } + }; + + tasks.into_iter().for_each(|task| { + task.set_status(State::Failed, Reason::AppBackgroundOrTerminate); + self.after_task_processed(&task); + }); + } + + pub(crate) fn update_network(&mut self) { + let tasks = self.tasks.values().cloned().collect::>(); + + for task in tasks { + if unsafe { IsOnline() } { + self.resume_waiting_task(task.clone()); + } + } + } +} + +extern "C" { + pub(crate) fn IsOnline() -> bool; +} diff --git a/services/service/request/src/manager/notifier.rs b/services/service/request/src/manager/notifier.rs new file mode 100644 index 00000000..31007d74 --- /dev/null +++ b/services/service/request/src/manager/notifier.rs @@ -0,0 +1,64 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::Arc; + +use crate::service::ability::RequestAbility; +use crate::service::notify::{Event, NotifyEvent}; +use crate::task::config::Version; +use crate::task::info::ApplicationState; +use crate::task::notify::NotifyData; +use crate::task::RequestTask; +pub(crate) struct Notifier; + +impl Notifier { + pub(crate) fn service_front_notify( + event: String, + notify_data: NotifyData, + app_state: &Arc, + ) { + let total_processed = notify_data.progress.common_data.total_processed; + let file_total_size: i64 = notify_data.progress.sizes.iter().sum(); + if total_processed == 0 && file_total_size < 0 && event.eq("progress") { + return; + } + + if ApplicationState::from(app_state.load(Ordering::SeqCst)) != ApplicationState::Foreground + && (notify_data.version == Version::API10 || event.eq("progress")) + { + return; + } + let event = match event.try_into() { + Ok(event) => event, + Err(e) => { + error!("TaskManager notify try_into failed {:?}", e); + return; + } + }; + + let event = NotifyEvent::notify(event, notify_data); + RequestAbility::notify().send_event(event); + } + + pub(crate) fn remove_notify(task: &Arc) { + let data = task.build_notify_data(); + let event = NotifyEvent::notify(Event::Remove, data); + RequestAbility::notify().send_event(event); + } + + pub(crate) fn clear_notify(task: &Arc) { + let event = NotifyEvent::clear(task.conf.common_data.task_id); + RequestAbility::notify().send_event(event); + } +} diff --git a/services/service/request/src/manager/qos.rs b/services/service/request/src/manager/qos.rs new file mode 100644 index 00000000..e2489072 --- /dev/null +++ b/services/service/request/src/manager/qos.rs @@ -0,0 +1,562 @@ +// Copyright (C) 2023 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. + +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::sync::Arc; + +use crate::task::info::ApplicationState; +use crate::task::RequestTask; + +const HIGH_QOS_MAX: usize = 10; + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub(crate) struct QosCase { + uid: u64, + task_id: u32, + qos_index: u32, +} + +impl PartialOrd for QosCase { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for QosCase { + fn cmp(&self, other: &Self) -> Ordering { + if self.uid != other.uid { + return Ordering::Equal; + } + self.qos_index.cmp(&other.qos_index) + } +} + +impl QosCase { + fn new(uid: u64, task_id: u32, qos_index: u32) -> Self { + Self { + uid, + task_id, + qos_index, + } + } +} + +#[derive(Debug)] +pub(crate) struct QosQueue { + foreground_high_qos_cases: Vec, + foreground_low_qos_cases: HashMap>, + + background_high_qos_cases: Vec, + background_low_qos_cases: HashMap>, + + tasks: HashSet, + + app_state_map: HashMap, + app_task_count: HashMap, +} + +#[derive(Debug)] +pub(crate) enum Qos { + High, + Low, +} + +impl QosQueue { + pub(crate) fn new() -> Self { + Self { + foreground_high_qos_cases: Vec::with_capacity(HIGH_QOS_MAX), + + foreground_low_qos_cases: HashMap::new(), + + background_high_qos_cases: Vec::with_capacity(HIGH_QOS_MAX), + + background_low_qos_cases: HashMap::new(), + + tasks: HashSet::new(), + + app_state_map: HashMap::new(), + app_task_count: HashMap::new(), + } + } + + pub(crate) fn insert( + &mut self, + task: &Arc, + app_state: ApplicationState, + ) -> Vec<(u32, Qos)> { + let task_id = task.conf.common_data.task_id; + let uid = task.conf.common_data.uid; + let priority = task.conf.common_data.priority; + + if self.tasks.contains(&task_id) { + error!( + "Qos insert a task twice, uid:{} task_id:{} priority:{}", + uid, task_id, priority + ); + return vec![]; + } + + if app_state == ApplicationState::Terminated { + error!( + "Qos insert a terminated task, uid:{} task_id:{} priority:{}", + uid, task_id, priority + ); + return vec![]; + } + + debug!( + "Qos insert a task, uid:{} task_id:{} priority:{}", + uid, task_id, priority + ); + + self.tasks.insert(task_id); + + match self.app_task_count.get_mut(&uid) { + Some(count) => *count += 1, + None => { + self.app_task_count.insert(uid, 1); + } + } + + let case = QosCase::new(uid, task_id, priority); + + match self.app_state_map.get(&uid) { + Some(state) => { + if *state != app_state { + error!( + "Qos app_state_map state:{:?} not eq to inserted app_state:{:?}", + state, app_state + ); + + let mut qos_changes = self.change_state(uid, app_state); + qos_changes.extend(self.insert_inner(case, app_state)); + qos_changes + } else { + self.insert_inner(case, app_state) + } + } + None => { + self.app_state_map.insert(uid, app_state); + self.insert_inner(case, app_state) + } + } + } + + fn insert_inner(&mut self, case: QosCase, state: ApplicationState) -> Vec<(u32, Qos)> { + match state { + ApplicationState::Foreground => self.frontground_insert(case, state), + + ApplicationState::Background => self.background_insert(case, state), + + _ => unreachable!(), + } + } + + fn frontground_insert(&mut self, case: QosCase, state: ApplicationState) -> Vec<(u32, Qos)> { + if self.foreground_high_qos_cases.len() < HIGH_QOS_MAX { + let mut qos_changes = Vec::new(); + qos_changes.push((case.task_id, Qos::High)); + + self.foreground_high_qos_cases.push(case); + + if self.background_high_qos_cases.len() + self.foreground_high_qos_cases.len() + > HIGH_QOS_MAX + { + self.background_high_qos_cases.sort(); + let down_grade_case = self.background_high_qos_cases.pop().unwrap(); + + qos_changes.push((down_grade_case.task_id, Qos::Low)); + + match self.background_low_qos_cases.get_mut(&down_grade_case.uid) { + Some(low_qos_cases) => { + low_qos_cases.push(down_grade_case); + } + None => { + let mut low_qos_cases = Vec::new(); + let uid = down_grade_case.uid; + low_qos_cases.push(down_grade_case); + self.background_low_qos_cases.insert(uid, low_qos_cases); + } + } + } + qos_changes + } else { + self.contest_insert(case, state) + } + } + + fn background_insert(&mut self, case: QosCase, state: ApplicationState) -> Vec<(u32, Qos)> { + if self.background_high_qos_cases.len() + self.foreground_high_qos_cases.len() + < HIGH_QOS_MAX + { + let task_id = case.task_id; + self.background_high_qos_cases.push(case); + vec![(task_id, Qos::High)] + } else { + self.contest_insert(case, state) + } + } + + fn contest_insert(&mut self, mut case: QosCase, state: ApplicationState) -> Vec<(u32, Qos)> { + let high_qos_cases = match state { + ApplicationState::Foreground => &mut self.foreground_high_qos_cases, + ApplicationState::Background => &mut self.background_high_qos_cases, + ApplicationState::Terminated => unreachable!(), + }; + + let low_qos_cases = match state { + ApplicationState::Foreground => &mut self.foreground_low_qos_cases, + ApplicationState::Background => &mut self.background_low_qos_cases, + ApplicationState::Terminated => unreachable!(), + }; + + let mut qos_changes = Vec::new(); + + let mut down_grade_case = &case; + let mut swap_case_index_opt = None; + for (i, swap_case) in high_qos_cases + .iter() + .enumerate() + .filter(|(_, swap)| swap.uid == case.uid) + { + if down_grade_case.qos_index < swap_case.qos_index { + down_grade_case = swap_case; + swap_case_index_opt = Some(i) + } + } + + if let Some(i) = swap_case_index_opt { + qos_changes.push((case.task_id, Qos::High)); + mem::swap(&mut case, high_qos_cases.get_mut(i).unwrap()); + } + + qos_changes.push((case.task_id, Qos::Low)); + match low_qos_cases.get_mut(&case.uid) { + Some(cases) => { + cases.push(case); + } + None => { + let mut cases = Vec::new(); + let uid = case.uid; + cases.push(case); + low_qos_cases.insert(uid, cases); + } + } + + qos_changes + } + + pub(crate) fn remove(&mut self, uid: u64, task_id: u32) -> Vec<(u32, Qos)> { + let state = match self.app_state_map.get(&uid) { + None => { + error!("Qos can not find app_state, uid:{}", uid); + return vec![]; + } + Some(state) => state, + }; + + if !self.tasks.remove(&task_id) { + debug!("Qos remove task_id:{} that not exist", task_id); + return vec![]; + } + + debug!("Qos remove uid:{} task_id:{}", uid, task_id); + + match self.app_task_count.get_mut(&uid) { + Some(count) => { + *count -= 1; + if *count == 0 { + self.app_task_count.remove(&uid); + } + } + None => { + error!( + "Qos remove task_id:{}, but uid:{} count task 0", + task_id, uid + ); + } + } + + match state { + ApplicationState::Foreground => self.foreground_remove(uid, task_id), + ApplicationState::Background => self.background_remove(uid, task_id), + ApplicationState::Terminated => unreachable!(), + } + } + + fn foreground_remove(&mut self, uid: u64, task_id: u32) -> Vec<(u32, Qos)> { + let mut qos_changes = vec![]; + + for i in 0..self.foreground_high_qos_cases.len() { + if self.foreground_high_qos_cases[i].task_id == task_id { + self.foreground_high_qos_cases.remove(i); + for low_qos_cases in self.foreground_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.foreground_high_qos_cases.push(case); + return qos_changes; + } + } + + for low_qos_cases in self.background_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.background_high_qos_cases.push(case); + return qos_changes; + } + } + return qos_changes; + } + } + + if let Some(set) = self.foreground_low_qos_cases.get_mut(&uid) { + for i in 0..set.len() { + if set[i].task_id == task_id { + set.remove(i); + } + } + } + + qos_changes + } + fn background_remove(&mut self, uid: u64, task_id: u32) -> Vec<(u32, Qos)> { + let mut qos_changes = vec![]; + + for i in 0..self.background_high_qos_cases.len() { + if self.background_high_qos_cases[i].task_id == task_id { + self.background_high_qos_cases.remove(i); + + for low_qos_cases in self.background_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.background_high_qos_cases.push(case); + return qos_changes; + } + } + return qos_changes; + } + } + + if let Some(set) = self.background_low_qos_cases.get_mut(&uid) { + for i in 0..set.len() { + if set[i].task_id == task_id { + set.remove(i); + } + } + } + + qos_changes + } + + pub(crate) fn change_state( + &mut self, + uid: u64, + new_state: ApplicationState, + ) -> Vec<(u32, Qos)> { + let state = match self.app_state_map.get(&uid) { + None => return vec![], + Some(state) => { + if *state == new_state { + error!("Qos change state with the same state"); + return vec![]; + } else { + *state + } + } + }; + debug!( + "Qos change state uid:{}, state:{:?}, new_state:{:?}", + uid, state, new_state + ); + + self.app_state_map.insert(uid, new_state); + + match new_state { + ApplicationState::Foreground => self.state_turn_to_foreground(uid), + ApplicationState::Background => self.state_turn_to_background(uid), + ApplicationState::Terminated => self.state_turn_to_terminated(uid, state), + } + } + + fn state_turn_to_foreground(&mut self, uid: u64) -> Vec<(u32, Qos)> { + let mut qos_changes = vec![]; + let high_qos_cases = self + .background_high_qos_cases + .iter() + .cloned() + .filter(|case| case.uid == uid); + + self.foreground_high_qos_cases.extend(high_qos_cases); + + self.background_high_qos_cases + .retain(|case| case.uid != uid); + + if let Some(mut low_qos_cases) = self.background_low_qos_cases.remove(&uid) { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + while self.foreground_high_qos_cases.len() < HIGH_QOS_MAX { + if let Some(case) = low_qos_cases.pop() { + qos_changes.extend(self.frontground_insert(case, ApplicationState::Foreground)); + } else { + break; + } + } + if !low_qos_cases.is_empty() { + self.foreground_low_qos_cases.insert(uid, low_qos_cases); + } + } + qos_changes + } + + fn state_turn_to_background(&mut self, uid: u64) -> Vec<(u32, Qos)> { + let mut qos_changes = vec![]; + + if let Some(low_qos_cases) = self.foreground_low_qos_cases.remove(&uid) { + self.background_low_qos_cases.insert(uid, low_qos_cases); + } + + let mut high_qos_cases = self + .foreground_high_qos_cases + .iter() + .cloned() + .filter(|case| case.uid == uid) + .collect::>(); + + self.foreground_high_qos_cases + .retain(|case| case.uid != uid); + + 'a: for low_qos_cases in self.foreground_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + while self.foreground_high_qos_cases.len() < HIGH_QOS_MAX { + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.foreground_high_qos_cases.push(case); + } else { + break 'a; + } + } + } + + self.foreground_low_qos_cases + .retain(|_, cases| !cases.is_empty()); + + if self.foreground_high_qos_cases.len() < 10 { + high_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + + while self.background_high_qos_cases.len() + self.foreground_high_qos_cases.len() + < HIGH_QOS_MAX + { + if let Some(case) = high_qos_cases.pop() { + self.background_high_qos_cases.push(case); + } else { + break; + } + } + } + qos_changes.extend(high_qos_cases.iter().map(|case| (case.task_id, Qos::Low))); + + if !high_qos_cases.is_empty() { + match self.background_low_qos_cases.get_mut(&uid) { + Some(low_qos_cases) => { + low_qos_cases.extend(high_qos_cases); + } + None => { + let low_qos_cases = high_qos_cases.into_iter().collect(); + self.background_low_qos_cases.insert(uid, low_qos_cases); + } + } + } + qos_changes + } + fn state_turn_to_terminated( + &mut self, + uid: u64, + old_state: ApplicationState, + ) -> Vec<(u32, Qos)> { + let mut qos_changes = vec![]; + + self.app_state_map.remove(&uid); + self.app_task_count.remove(&uid); + + match old_state { + ApplicationState::Background => { + self.background_high_qos_cases + .iter() + .filter(|case| case.uid == uid) + .for_each(|case| { + self.tasks.remove(&case.task_id); + }); + + self.background_high_qos_cases + .retain(|case| case.uid != uid); + + 'a: for low_qos_cases in self.background_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + while self.background_high_qos_cases.len() + + self.foreground_high_qos_cases.len() + < HIGH_QOS_MAX + { + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.background_high_qos_cases.push(case); + } else { + break 'a; + } + } + } + + if let Some(remove_tasks) = self.background_low_qos_cases.remove(&uid) { + remove_tasks.into_iter().for_each(|case| { + self.tasks.remove(&case.task_id); + }) + } + } + ApplicationState::Foreground => { + self.foreground_high_qos_cases + .iter() + .filter(|case| case.uid == uid) + .for_each(|case| { + self.tasks.remove(&case.task_id); + }); + + self.foreground_high_qos_cases + .retain(|case| case.uid != uid); + + 'a: for low_qos_cases in self.foreground_low_qos_cases.values_mut() { + low_qos_cases.sort_by(|a, b| b.qos_index.cmp(&a.qos_index)); + while self.foreground_high_qos_cases.len() < HIGH_QOS_MAX { + if let Some(case) = low_qos_cases.pop() { + qos_changes.push((case.task_id, Qos::High)); + self.foreground_high_qos_cases.push(case); + } else { + break 'a; + } + } + } + + if let Some(remove_tasks) = self.foreground_low_qos_cases.remove(&uid) { + remove_tasks.into_iter().for_each(|case| { + self.tasks.remove(&case.task_id); + }) + } + } + _ => unreachable!(), + } + + qos_changes + } +} diff --git a/services/service/request/src/manager/scheduled.rs b/services/service/request/src/manager/scheduled.rs new file mode 100644 index 00000000..3a183504 --- /dev/null +++ b/services/service/request/src/manager/scheduled.rs @@ -0,0 +1,122 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::time::Duration; + +use ylong_runtime::sync::mpsc::UnboundedSender; + +use super::events::{EventMessage, ScheduledMessage}; +use super::TaskManager; +use crate::task::config::Version; +use crate::task::info::State; +use crate::task::reason::Reason; +use crate::task::RequestTask; +use crate::utils::get_current_timestamp; + +const MILLISECONDS_IN_ONE_DAY: u64 = 24 * 60 * 60 * 1000; +const MILLISECONDS_IN_ONE_MONTH: u64 = 30 * 24 * 60 * 60 * 1000; +const CLEAR_INTERVAL: u64 = 30 * 60; +const LOG_INTERVAL: u64 = 5 * 60; +const UNLOAD_WAITING: u64 = 60; +const BACKGROUND_TASK_STOP_INTERVAL: u64 = 60; +// monitor tasks, tasks in waiting state turn to stop after one day, tasks in +// other state turn to stop after one month. +pub(crate) async fn clear_timeout_tasks(tx: UnboundedSender) { + loop { + ylong_runtime::time::sleep(Duration::from_secs(CLEAR_INTERVAL)).await; + let _ = tx.send(EventMessage::Scheduled(ScheduledMessage::ClearTimeoutTasks)); + } +} + +pub(crate) async fn log_all_task_info(tx: UnboundedSender) { + loop { + ylong_runtime::time::sleep(Duration::from_secs(LOG_INTERVAL)).await; + let _ = tx.send(EventMessage::Scheduled(ScheduledMessage::LogTasks)); + } +} + +pub(crate) async fn unload_sa(tx: UnboundedSender) { + ylong_runtime::time::sleep(Duration::from_secs(UNLOAD_WAITING)).await; + let _ = tx.send(EventMessage::Scheduled(ScheduledMessage::Unload)); +} + +pub(crate) async fn update_background_app(uid: u64, tx: UnboundedSender) { + ylong_runtime::time::sleep(Duration::from_secs(BACKGROUND_TASK_STOP_INTERVAL)).await; + let _ = tx.send(EventMessage::Scheduled( + ScheduledMessage::UpdateBackgroundApp(uid), + )); +} + +impl TaskManager { + pub(crate) fn clear_timeout_tasks(&mut self) { + let mut remove_tasks = Vec::>::new(); + + for task in self.tasks.values() { + let current_time = get_current_timestamp(); + let (state, time) = { + let guard = task.status.lock().unwrap(); + (guard.state, guard.waitting_network_time) + }; + if state == State::Waiting { + if let Some(t) = time { + if current_time - t > MILLISECONDS_IN_ONE_DAY { + task.set_status(State::Stopped, Reason::WaittingNetWorkOneday); + remove_tasks.push(task.clone()); + } + } + } + if task.conf.version == Version::API9 { + continue; + } + if current_time - task.ctime > MILLISECONDS_IN_ONE_MONTH { + task.set_status(State::Stopped, Reason::TaskSurvivalOneMonth); + remove_tasks.push(task.clone()); + continue; + } + } + + for task in remove_tasks { + self.after_task_processed(&task); + } + } + + pub(crate) fn log_all_task_info(&self) { + let api10_background_task_count = self.api10_background_task_count; + let recording_rdb_num = self.recording_rdb_num.load(Ordering::SeqCst); + info!( + "dump all task info, api10_background_task_count:{}, recording_rdb_num:{}", + api10_background_task_count, recording_rdb_num + ); + for (task_id, task) in self.tasks.iter() { + let task_status = task.status.lock().unwrap(); + info!("dump task message, task_id:{}, action:{}, mode:{}, bundle name:{}, task_status:{:?}", + task_id, task.conf.common_data.action as u8, task.conf.common_data.mode as u8, task.conf.bundle, *task_status); + } + } + + pub(crate) fn schedule_unload_sa(&mut self) { + debug!("TaskManage clock 60s to close sa"); + let tx = self.tx.clone(); + match self.unload_handle.take() { + Some(handle) => { + handle.cancel(); + self.unload_handle = Some(ylong_runtime::spawn(unload_sa(tx))); + } + None => { + self.unload_handle = Some(ylong_runtime::spawn(unload_sa(tx))); + } + } + } +} diff --git a/services/service/request/src/manager/task_manager.rs b/services/service/request/src/manager/task_manager.rs new file mode 100644 index 00000000..c09f87cb --- /dev/null +++ b/services/service/request/src/manager/task_manager.rs @@ -0,0 +1,456 @@ +// Copyright (C) 2023 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. + +use std::collections::{HashMap, HashSet}; +use std::sync::atomic::{AtomicU32, AtomicU8, Ordering}; +use std::sync::Arc; + +use ylong_runtime::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use ylong_runtime::task::JoinHandle; + +use super::events::{ + ConstructMessage, EventMessage, ScheduledMessage, ServiceMessage, StateMessage, TaskMessage, +}; +use super::qos::{Qos, QosQueue}; +use super::scheduled; +use crate::error::ErrorCode; +use crate::task::config::Version; +use crate::task::info::{ApplicationState, State}; +use crate::task::reason::Reason; +use crate::task::request_task::RequestTask; +use crate::utils::c_wrapper::CStringWrapper; + +cfg_oh! { + use crate::manager::Notifier; +} + +pub(crate) struct TaskManager { + pub(crate) tasks: HashMap>, + pub(crate) qos: QosQueue, + + pub(crate) app_task_map: HashMap>, + + pub(crate) app_state_map: HashMap>, + + pub(crate) restoring: bool, + pub(crate) api10_background_task_count: u32, + + pub(crate) unload_handle: Option>, + + pub(crate) recording_rdb_num: Arc, + pub(crate) tx: UnboundedSender, + pub(crate) rx: UnboundedReceiver, +} + +#[derive(Clone)] +pub(crate) struct TaskManagerEntry { + tx: UnboundedSender, +} + +impl TaskManagerEntry { + fn new(tx: UnboundedSender) -> Self { + Self { tx } + } + + pub(crate) fn send_event(&self, event: EventMessage) -> bool { + if self.tx.send(event).is_err() { + error!("Sends TaskManager event failed, or TaskManager is unloading"); + return false; + } + true + } +} + +impl TaskManager { + pub(crate) fn init() -> TaskManagerEntry { + debug!("TaskManager init"); + + ylong_runtime::builder::RuntimeBuilder::new_multi_thread() + .worker_num(4) + .build_global() + .unwrap(); + + let (tx, rx) = unbounded_channel(); + + let mut task_manager = Self::new(tx.clone(), rx); + + // Considers update invalid task in database to FAILED state here?. + + task_manager.restore_all_tasks(task_manager.recording_rdb_num.clone()); + + ylong_runtime::spawn(scheduled::clear_timeout_tasks(task_manager.tx.clone())); + + ylong_runtime::spawn(scheduled::log_all_task_info(task_manager.tx.clone())); + + ylong_runtime::spawn(task_manager.run()); + + TaskManagerEntry::new(tx) + } + + fn new(tx: UnboundedSender, rx: UnboundedReceiver) -> Self { + TaskManager { + qos: QosQueue::new(), + tasks: HashMap::new(), + app_task_map: HashMap::new(), + app_state_map: HashMap::new(), + + unload_handle: None, + restoring: false, + api10_background_task_count: 0, + recording_rdb_num: Arc::new(AtomicU32::new(0)), + rx, + tx, + } + } + + async fn run(mut self) { + loop { + let recv = match self.rx.recv().await { + Ok(message) => message, + Err(e) => { + error!("TaskManager recv error {:?}", e); + continue; + } + }; + + match recv { + EventMessage::Service(message) => self.handle_service_command(message), + EventMessage::State(message) => self.handle_state_change(message), + EventMessage::Task(message) => self.handle_request_task(message), + EventMessage::Scheduled(message) => { + if self.handle_scheduled_task(message) { + info!("TaskManager unload succeed"); + // If unload_sa success, breaks this loop. + return; + } + } + } + + debug!("TaskManager handle message done"); + } + } + + fn handle_request_task(&mut self, message: TaskMessage) { + debug!("TaskManager handle task_message {:?}", message); + + match message { + TaskMessage::Finished(task_id) => { + let task = match self.tasks.get(&task_id) { + Some(task) => task.clone(), + None => return, + }; + self.after_task_processed(&task); + } + } + } + + fn handle_scheduled_task(&mut self, message: ScheduledMessage) -> bool { + debug!("TaskManager handle scheduled_message {:?}", message); + + match message { + ScheduledMessage::ClearTimeoutTasks => self.clear_timeout_tasks(), + ScheduledMessage::LogTasks => self.log_all_task_info(), + ScheduledMessage::Unload => return self.unload_sa(), + ScheduledMessage::UpdateBackgroundApp(uid) => self.update_background_app(uid), + } + false + } + + fn handle_state_change(&mut self, message: StateMessage) { + debug!("TaskManager handle state_message {:?}", message); + + match message { + StateMessage::NetworkChange => { + self.update_network(); + } + StateMessage::AppStateChange(uid, state) => { + self.update_app_state(uid, state); + } + } + } + + fn handle_service_command(&mut self, message: ServiceMessage) { + debug!("TaskManager handle service_message {:?}", message); + + match message { + ServiceMessage::Construct(construct_message, tx) => { + let ConstructMessage { + config, + files, + body_files, + } = *construct_message; + let error_code = self.construct_task(config, files, body_files); + let _ = tx.send(error_code); + } + ServiceMessage::Pause(uid, task_id, tx) => { + let error_code = self.pause(uid, task_id); + let _ = tx.send(error_code); + } + ServiceMessage::Resume(uid, task_id, tx) => { + let error_code = self.resume(uid, task_id); + let _ = tx.send(error_code); + } + ServiceMessage::Start(uid, task_id, tx) => { + let error_code = self.start(uid, task_id); + let _ = tx.send(error_code); + } + ServiceMessage::Stop(uid, task_id, tx) => { + let error_code = self.stop(uid, task_id); + let _ = tx.send(error_code); + } + + ServiceMessage::Show(uid, task_id, tx) => { + let task_info = self.show(uid, task_id); + let _ = tx.send(task_info); + } + + ServiceMessage::Query(task_id, query_action, tx) => { + let task_info = self.query(task_id, query_action); + let _ = tx.send(task_info); + } + ServiceMessage::Search(filter, tx) => { + let v = self.search(filter); + let _ = tx.send(v); + } + ServiceMessage::Touch(uid, task_id, token, tx) => { + let task_info = self.touch(uid, task_id, token); + let _ = tx.send(task_info); + } + ServiceMessage::Remove(uid, task_id, tx) => { + let error_code = self.remove(uid, task_id); + let _ = tx.send(error_code); + } + ServiceMessage::DumpAll(tx) => { + let dump_all_info = self.query_all_task(); + let _ = tx.send(dump_all_info); + } + ServiceMessage::DumpOne(task_id, tx) => { + let dump_one_info = self.query_one_task(task_id); + let _ = tx.send(dump_one_info); + } + ServiceMessage::QueryMimeType(uid, task_id, tx) => { + let s = self.query_mime_type(uid, task_id); + let _ = tx.send(s); + } + } + } + + pub(crate) fn app_state(&mut self, uid: u64, bundle: &str) -> Arc { + match self.app_state_map.get(&uid) { + Some(state) => state.clone(), + None => { + let top_bundle = unsafe { GetTopBundleName() }; + let top_bundle = top_bundle.to_string(); + debug!( + "TaskManager try get app_state uid:{} from top_bundle {}", + uid, top_bundle + ); + if top_bundle == bundle { + let state = Arc::new(AtomicU8::new(ApplicationState::Foreground as u8)); + self.app_state_map.insert(uid, state.clone()); + state + } else { + let state = Arc::new(AtomicU8::new(ApplicationState::Background as u8)); + self.app_state_map.insert(uid, state.clone()); + state + } + } + } + } + + pub(crate) fn get_task(&self, uid: u64, task_id: u32) -> Option> { + self.app_task_map + .get(&uid) + .and_then(|set| set.get(&task_id)) + .and_then(|task_id| self.tasks.get(task_id).cloned()) + } + + fn process_waiting_task(&mut self, uid: u64, version: Version) { + match version { + Version::API10 => { + let tasks = match self.app_task_map.get(&uid) { + Some(v) => v.iter().copied().collect::>(), + None => return, + }; + for task in tasks { + let request_task = match self.tasks.get(&task) { + Some(task) => task, + None => { + error!( + "TaskManager process waiting task, task_id:{} not found", + task + ); + continue; + } + }; + if request_task.conf.version == Version::API10 { + let state = request_task.status.lock().unwrap().state; + if state == State::Waiting { + debug!( + "TaskManager begin process v10 task_id:{} which in waitting state", + task + ); + self.start_inner(request_task.clone()); + return; + } + } + } + } + Version::API9 => { + for task in self.tasks.values() { + if task.conf.version == Version::API9 { + let state = task.status.lock().unwrap().state; + if state == State::Waiting { + debug!( + "TaskManager begin process v9 task_id:{} which in waitting state", + task.conf.common_data.task_id + ); + let task = task.clone(); + self.start_inner(task); + return; + } + } + } + } + } + } + + pub(crate) fn after_task_processed(&mut self, task: &Arc) { + let state = task.status.lock().unwrap().state; + if state != State::Completed + && state != State::Failed + && state != State::Removed + && state != State::Stopped + { + return; + } + debug!( + "TaskManager remove task_id:{} from map", + task.conf.common_data.task_id + ); + + let remove_task = self.tasks.remove(&task.conf.common_data.task_id).unwrap(); + + let uid = &task.conf.common_data.uid; + match self.app_task_map.get_mut(uid) { + Some(map) => { + map.remove(&task.conf.common_data.task_id); + } + None => { + error!("TaskManager after_task_processed get uid:{} failed", uid); + return; + } + } + + match self.app_task_map.get(&task.conf.common_data.uid) { + Some(map) => { + if map.is_empty() { + self.app_task_map.remove(&task.conf.common_data.uid); + self.app_state_map.remove(&remove_task.conf.common_data.uid); + } + } + None => { + error!("TaskManger where is my map"); + return; + } + } + + if remove_task.conf.version == Version::API10 { + self.api10_background_task_count -= 1; + } + + let app_state = ApplicationState::from(remove_task.app_state.load(Ordering::SeqCst)); + if !(app_state == ApplicationState::Background + && remove_task.conf.version == Version::API10) + { + #[cfg(feature = "oh")] + Notifier::remove_notify(&remove_task); + } + + // Notifies NotifyManager to remove RemoteObj when task has been removed. + + #[cfg(feature = "oh")] + Notifier::clear_notify(&remove_task); + + let map = self + .qos + .remove(task.conf.common_data.uid, task.conf.common_data.task_id); + + self.change_qos(map); + + if self.check_unload_sa() { + self.schedule_unload_sa(); + } else { + self.process_waiting_task(remove_task.conf.common_data.uid, remove_task.conf.version); + } + } + + pub(crate) fn pause_task(&self, task: Arc, reason: Reason) -> ErrorCode { + let uid = task.conf.common_data.uid; + let task_id = task.conf.common_data.task_id; + + if !task.set_status(State::Paused, reason) { + let state = task.status.lock().unwrap(); + error!( + "TaskManager pause a task, uid:{}, task_id:{} failed which state is {:?}", + uid, task_id, state + ); + ErrorCode::TaskStateErr + } else { + task.resume.store(false, Ordering::SeqCst); + debug!( + "TaskManager pause a task, uid:{}, task_id:{} success", + uid, task_id + ); + ErrorCode::ErrOk + } + } + + pub(crate) fn resume_waiting_task(&mut self, task: Arc) { + let state = task.status.lock().unwrap().state; + if state == State::Waiting && task.is_satisfied_configuration() { + info!("Begin try resume task as network condition resume"); + task.resume.store(true, Ordering::SeqCst); + let notify_data = task.build_notify_data(); + + #[cfg(feature = "oh")] + Notifier::service_front_notify( + "resume".into(), + notify_data, + &self.app_state(task.conf.common_data.uid, &task.conf.bundle), + ); + self.start_inner(task.clone()); + } + } + + pub(crate) fn change_qos(&mut self, new_qos: Vec<(u32, Qos)>) { + for (task_id, qos) in new_qos.iter() { + if let Some(task) = self.tasks.get(task_id) { + match qos { + Qos::High => { + info!("Qos task_id:{} set to High Qos", task_id); + task.rate_limiting.store(false, Ordering::SeqCst); + } + Qos::Low => { + info!("Qos task_id:{} set to Low Qos", task_id); + task.rate_limiting.store(true, Ordering::SeqCst); + } + } + } + } + } +} + +extern "C" { + pub(crate) fn GetTopBundleName() -> CStringWrapper; +} diff --git a/services/service/request/src/manager/unload.rs b/services/service/request/src/manager/unload.rs new file mode 100644 index 00000000..cde7efcf --- /dev/null +++ b/services/service/request/src/manager/unload.rs @@ -0,0 +1,217 @@ +// Copyright (C) 2023 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. + +use std::collections::HashSet; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use std::sync::Arc; + +use super::TaskManager; +use crate::manager::monitor::IsOnline; +use crate::task::config::{TaskConfig, Version}; +use crate::task::ffi::CTaskConfig; +use crate::task::info::State; +use crate::task::RequestTask; + +impl TaskManager { + pub(crate) fn check_unload_sa(&self) -> bool { + if !self.rx.is_empty() { + return false; + } + + if !self.tasks.is_empty() && self.network_check_unload_sa() { + return false; + } + + if self.recording_rdb_num.load(Ordering::SeqCst) != 0 { + return false; + } + + if !self.rx.is_empty() { + return false; + } + true + } + + pub(crate) fn unload_sa(&mut self) -> bool { + #[cfg(feature = "oh")] + const REQUEST_SERVICE_ID: i32 = 3706; + + if !self.check_unload_sa() { + debug!("Triggers unload sa, but cannot unload now"); + return false; + } + + self.rx.close(); + + info!("unload SA"); + + if !self.tasks.is_empty() { + self.record_all_task_config(); + } + + #[cfg(feature = "oh")] + let samgr_proxy = rust_samgr::get_systemability_manager(); + + // failed logic? + #[cfg(feature = "oh")] + let _ = samgr_proxy + .unload_systemability(REQUEST_SERVICE_ID) + .map_err(|e| error!("unload SA failed, err is {:?}", e)); + + true + } + + pub(crate) fn restore_all_tasks(&mut self, recording_rdb_num: Arc) { + if self.restoring { + return; + } + + self.restoring = true; + + if let Some(config_list) = self.query_all_task_config() { + info!( + "RSA query task config list len: {} in database", + config_list.len() + ); + for config in config_list.into_iter() { + debug!("RSA query task config is {:?}", config); + let uid = config.common_data.uid; + let task_id = config.common_data.task_id; + let token = config.token.clone(); + if let Some(task_info) = self.touch(uid, task_id, token) { + let state = State::from(task_info.progress.common_data.state); + if state != State::Waiting && state != State::Paused { + continue; + } + let app_state = self.app_state(uid, &config.bundle); + let request_task = RequestTask::restore_task( + config, + task_info, + recording_rdb_num.clone(), + AtomicBool::new(false), + app_state, + ); + let task = Arc::new(request_task); + self.restore_task(task.clone()); + if unsafe { IsOnline() } { + self.resume_waiting_task(task.clone()); + } + } + unsafe { CleanTaskConfigTable(task_id, uid) }; + } + } else { + self.schedule_unload_sa(); + } + self.restoring = false; + } + + fn record_all_task_config(&mut self) { + debug!("record all task config into database"); + self.recording_rdb_num.fetch_add(1, Ordering::SeqCst); + for task in self.tasks.values() { + if unsafe { HasTaskConfigRecord(task.conf.common_data.task_id) } { + continue; + } + let state = task.status.lock().unwrap().state; + + if state != State::Waiting && state != State::Paused { + continue; + } + + let task_config = &task.conf; + + let c_task_config = + task_config.to_c_struct(task.conf.common_data.task_id, task.conf.common_data.uid); + let ret = unsafe { RecordRequestTaskConfig(&c_task_config) }; + info!("insert taskConfig DB ret is {}", ret); + } + self.recording_rdb_num.fetch_sub(1, Ordering::SeqCst); + } + + fn restore_task(&mut self, task: Arc) { + if task.conf.version == Version::API10 { + self.api10_background_task_count += 1; + } + let uid = task.conf.common_data.uid; + let task_id = task.conf.common_data.task_id; + if self.get_task(uid, task_id).is_some() { + return; + } + + self.tasks.insert(task_id, task); + + match self.app_task_map.get_mut(&uid) { + Some(set) => { + set.insert(task_id); + } + None => { + let mut set = HashSet::new(); + set.insert(task_id); + self.app_task_map.insert(uid, set); + } + } + } + + fn network_check_unload_sa(&self) -> bool { + let mut need_unload = false; + for task in self.tasks.values() { + let state = task.status.lock().unwrap().state; + if state == State::Completed + || state == State::Failed + || state == State::Removed + || state == State::Stopped + || ((state == State::Waiting || state == State::Paused) + && (!task.is_satisfied_configuration() || unsafe { !IsOnline() })) + { + need_unload = true; + } else { + return false; + } + } + need_unload + } + + pub(crate) fn query_all_task_config(&self) -> Option> { + debug!("query all task config in database"); + let mut task_config_list: Vec = Vec::new(); + let c_config_list_len = unsafe { QueryTaskConfigLen() }; + if c_config_list_len <= 0 { + debug!("no task config in database"); + return None; + } + let c_task_config_list = unsafe { QueryAllTaskConfig() }; + if c_task_config_list.is_null() { + return None; + } + let c_task_config_ptrs = + unsafe { std::slice::from_raw_parts(c_task_config_list, c_config_list_len as usize) }; + for c_task_config in c_task_config_ptrs.iter() { + let task_config = TaskConfig::from_c_struct(unsafe { &**c_task_config }); + task_config_list.push(task_config); + unsafe { DeleteCTaskConfig(*c_task_config) }; + } + unsafe { DeleteCTaskConfigs(c_task_config_list) }; + Some(task_config_list) + } +} + +extern "C" { + pub(crate) fn DeleteCTaskConfigs(ptr: *const *const CTaskConfig); + pub(crate) fn QueryAllTaskConfig() -> *const *const CTaskConfig; + pub(crate) fn QueryTaskConfigLen() -> i32; + + pub(crate) fn DeleteCTaskConfig(ptr: *const CTaskConfig); + pub(crate) fn RecordRequestTaskConfig(taskConfig: *const CTaskConfig) -> bool; + pub(crate) fn CleanTaskConfigTable(taskId: u32, uid: u64) -> bool; + pub(crate) fn HasTaskConfigRecord(taskId: u32) -> bool; +} diff --git a/services/service/request/src/service/ability.rs b/services/service/request/src/service/ability.rs new file mode 100644 index 00000000..ba18f4bd --- /dev/null +++ b/services/service/request/src/service/ability.rs @@ -0,0 +1,120 @@ +// Copyright (C) 2023 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. + +// Copyright (C) 2023 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, +// See the License for the specific language governing permissions and +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// limitations under the License. + +//! Request ability services implementations. + +use std::hint; +use std::mem::MaybeUninit; +use std::sync::atomic::{AtomicU8, Ordering}; + +use crate::manager::task_manager::TaskManagerEntry; +use crate::manager::TaskManager; +use crate::service::listener::{AppStateListener, NetworkChangeListener}; +use crate::service::notify::{NotifyEntry, NotifyManager}; + +static mut REQUEST_ABILITY: MaybeUninit = MaybeUninit::uninit(); +static STATE: AtomicU8 = AtomicU8::new(RequestAbility::NOT_INITED); + +pub(crate) struct RequestAbility { + manager: TaskManagerEntry, + notify: NotifyEntry, + app: AppStateListener, + network: NetworkChangeListener, +} + +impl RequestAbility { + const NOT_INITED: u8 = 0; + const INITIALIZING: u8 = 1; + const RUNNING: u8 = 2; + const STOPPING: u8 = 3; + const STOPPED: u8 = 4; + + // `init` must have been called before calling `get_instance`. + pub(crate) fn get_instance() -> &'static Self { + loop { + match STATE.load(Ordering::SeqCst) { + Self::RUNNING | Self::STOPPED => return unsafe { &*REQUEST_ABILITY.as_ptr() }, + _ => hint::spin_loop(), + } + } + } + + pub(crate) fn init() { + if STATE + .compare_exchange( + Self::NOT_INITED, + Self::INITIALIZING, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { + unsafe { + REQUEST_ABILITY.write(Self { + manager: TaskManager::init(), + notify: NotifyManager::init(), + app: AppStateListener::init(), + network: NetworkChangeListener::init(), + }); + RequestInitServiceHandler(); + }; + STATE.store(Self::RUNNING, Ordering::SeqCst); + } + } + + pub(crate) fn stop() { + if STATE + .compare_exchange( + Self::RUNNING, + Self::STOPPING, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { + unsafe { + let ability = REQUEST_ABILITY.assume_init_ref(); + // After entries shutdown, the `rx`s of these channels will be dropped. + ability.notify.shutdown(); + ability.app.shutdown(); + ability.network.shutdown(); + }; + STATE.store(Self::STOPPED, Ordering::SeqCst); + } + } + + pub(crate) fn notify() -> NotifyEntry { + Self::get_instance().notify.clone() + } + + pub(crate) fn task_manager() -> TaskManagerEntry { + Self::get_instance().manager.clone() + } +} + +extern "C" { + pub(crate) fn RequestInitServiceHandler(); +} diff --git a/services/service/request/src/service/command/construct.rs b/services/service/request/src/service/command/construct.rs new file mode 100644 index 00000000..eeac431a --- /dev/null +++ b/services/service/request/src/service/command/construct.rs @@ -0,0 +1,250 @@ +// Copyright (C) 2023 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. + +use std::collections::HashMap; +use std::fs::File; + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; +use crate::service::{get_calling_bundle, open_file_readonly, open_file_readwrite}; +use crate::task::config::{Action, CommonTaskConfig, Network, TaskConfig, Version}; +use crate::task::info::Mode; +use crate::utils::form_item::{FileSpec, FormItem}; +use crate::utils::generate_task_id; + +pub(crate) struct Construct; + +impl Construct { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service construct"); + + if !PermissionChecker::check_internet() { + error!("Service construct: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + + let action: u32 = data.read()?; + let action: Action = Action::from(action as u8); + + let version: u32 = data.read()?; + let version: Version = Version::from(version as u8); + + let mode: u32 = data.read()?; + let mode: Mode = Mode::from(mode as u8); + + let cover: bool = data.read()?; + + let network: u32 = data.read()?; + let network: Network = Network::from(network as u8); + + let metered: bool = data.read()?; + + let roaming: bool = data.read()?; + + let retry: bool = data.read()?; + + let redirect: bool = data.read()?; + + let background: bool = data.read()?; + + let index: u32 = data.read()?; + + let begins: i64 = data.read()?; + + let ends: i64 = data.read()?; + + let gauge: bool = data.read()?; + + let precise: bool = data.read()?; + + let priority: u32 = data.read()?; + + let url: String = data.read()?; + + let title: String = data.read()?; + + let method: String = data.read()?; + + let token: String = data.read()?; + + let description: String = data.read()?; + + let data_base: String = data.read()?; + + let bundle = get_calling_bundle(); + // Creates task_id here, move it to task_manager later? + let task_id = generate_task_id(); + let uid = get_calling_uid(); + + let mut form_items = Vec::new(); + let form_size: u32 = data.read()?; + if form_size > data.get_readable_bytes() { + error!("Service construct: form_size too large"); + reply.write(&(ErrorCode::IpcSizeTooLarge as i32))?; + return Err(IpcStatusCode::Failed); + } + for _ in 0..form_size { + let name: String = data.read()?; + let value: String = data.read()?; + form_items.push(FormItem { name, value }); + } + + let mut files = Vec::::new(); + let mut file_specs: Vec = Vec::new(); + let file_size: u32 = data.read()?; + if file_size > data.get_readable_bytes() { + error!("Service construct: file_specs size too large"); + reply.write(&(ErrorCode::IpcSizeTooLarge as i32))?; + return Err(IpcStatusCode::Failed); + } + for _ in 0..file_size { + let name: String = data.read()?; + let path: String = data.read()?; + let file_name: String = data.read()?; + let mime_type: String = data.read()?; + if action == Action::UpLoad { + let file = open_file_readonly(uid, &bundle, &path)?; + files.push(file); + } else { + let file = open_file_readwrite(uid, &bundle, &path)?; + files.push(file); + } + let _fd_error: i32 = data.read()?; + file_specs.push(FileSpec { + name, + path, + file_name, + mime_type, + }); + } + + // Response bodies fd. + let body_file_size: u32 = data.read()?; + if body_file_size > data.get_readable_bytes() { + error!("Service construct: body_file size too large"); + reply.write(&(ErrorCode::IpcSizeTooLarge as i32))?; + return Err(IpcStatusCode::Failed); + } + let mut body_files = Vec::new(); + let mut body_file_names: Vec = Vec::new(); + for _ in 0..body_file_size { + let file_name: String = data.read()?; + let body_file = open_file_readwrite(uid, &bundle, &file_name)?; + body_file_names.push(file_name); + body_files.push(body_file); + } + + let header_size: u32 = data.read()?; + if header_size > data.get_readable_bytes() { + error!("Service construct: header size too large"); + reply.write(&(ErrorCode::IpcSizeTooLarge as i32))?; + return Err(IpcStatusCode::Failed); + } + let mut headers: HashMap = HashMap::new(); + for _ in 0..header_size { + let key: String = data.read()?; + let value: String = data.read()?; + headers.insert(key, value); + } + + let extras_size: u32 = data.read()?; + if extras_size > data.get_readable_bytes() { + error!("Service construct: extras size too large"); + reply.write(&(ErrorCode::IpcSizeTooLarge as i32))?; + return Err(IpcStatusCode::Failed); + } + let mut extras: HashMap = HashMap::new(); + for _ in 0..extras_size { + let key: String = data.read()?; + let value: String = data.read()?; + extras.insert(key, value); + } + + let task_config = TaskConfig { + bundle, + url, + title, + description, + method, + headers, + data: data_base, + token, + extras, + version, + form_items, + file_specs, + body_file_names, + common_data: CommonTaskConfig { + task_id, + uid, + action, + mode, + cover, + network, + metered, + roaming, + retry, + redirect, + index, + begins: begins as u64, + ends, + gauge, + precise, + priority, + background, + }, + }; + + debug!("Service construct: task_config constructed"); + debug!("Service construct: target files {:?}", files); + + let (event, rx) = EventMessage::construct(task_config, files, body_files); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service construct: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + + debug!("Service construct: construct event sent to manager"); + + if version != Version::API10 { + let (event, _) = EventMessage::start(uid, task_id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + debug!("Service construct: start event sent to manager"); + } + + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service construct: construct task failed"); + return Err(IpcStatusCode::Failed); + } + debug!("Service construct: task id {}", task_id); + reply.write(&(task_id as i32))?; + Ok(()) + } +} diff --git a/services/service/request/src/service/command/dump.rs b/services/service/request/src/service/command/dump.rs new file mode 100644 index 00000000..2ed81201 --- /dev/null +++ b/services/service/request/src/service/command/dump.rs @@ -0,0 +1,143 @@ +// Copyright (C) 2023 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. + +use std::io::Write; + +use ipc_rust::{FileDesc, IpcStatusCode, String16}; + +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; + +const HELP_MSG: &str = "usage:\n\ + -h help text for the tool\n\ + -t [taskid] without taskid: display all task summary info; \ + taskid: display one task detail info\n"; + +pub(crate) struct Dump; + +impl Dump { + // Ignores all the file error. + pub(crate) fn execute(file: &FileDesc, args: &mut Vec) -> i32 { + info!("Service dump"); + + let len = args.len(); + if len == 0 || args[0].get_string().eq("-h") { + let _ = file.as_ref().write(HELP_MSG.as_bytes()); + return IpcStatusCode::Ok as i32; + } + + if !args[0].get_string().eq("-t") { + let _ = file.as_ref().write("invalid args".as_bytes()); + return IpcStatusCode::Ok as i32; + } + + match len { + 1 => dump_all_task_info(file), + 2 => { + let task_id = args[1].get_string().parse::(); + match task_id { + Ok(id) => dump_one_task_info(file, id), + Err(_) => { + let _ = file.as_ref().write("-t accept a number".as_bytes()); + } + } + } + _ => { + let _ = file + .as_ref() + .write("too many args, -t accept no arg or one arg".as_bytes()); + } + } + IpcStatusCode::Ok as i32 + } +} + +fn dump_all_task_info(file: &FileDesc) { + info!("Service dump: dump all task info"); + + let (event, rx) = EventMessage::dump_all(); + if !RequestAbility::task_manager().send_event(event) { + return; + } + + let infos = match rx.get() { + Some(infos) => infos, + None => { + error!("Service dump: receives infos failed"); + return; + } + }; + let len = infos.vec.len(); + let mut file = file.as_ref(); + let _ = file.write(format!("task num: {}\n", len).as_bytes()); + if len > 0 { + let _ = file.write( + format!( + "{:<20}{:<12}{:<12}{:<12}\n", + "id", "action", "state", "reason" + ) + .as_bytes(), + ); + for info in infos.vec.iter() { + let _ = file.write( + format!( + "{:<20}{:<12}{:<12}{:<12}\n", + info.task_id, info.action as u8, info.state as u8, info.reason as u8 + ) + .as_bytes(), + ); + } + } +} + +fn dump_one_task_info(file: &FileDesc, task_id: u32) { + info!("Service dump: dump one task info"); + + let (event, rx) = EventMessage::dump_one(task_id); + if !RequestAbility::task_manager().send_event(event) { + return; + } + let task = match rx.get() { + Some(task) => task, + None => { + error!("Service dump: receives task failed"); + return; + } + }; + let mut file = file.as_ref(); + + if let Some(task) = task { + let _ = file.write( + format!( + "{:<20}{:<12}{:<12}{:<12}{:<12}{:<12}{}\n", + "id", "action", "state", "reason", "total_size", "tran_size", "url" + ) + .as_bytes(), + ); + let _ = file.write( + format!( + "{:<20}{:<12}{:<12}{:<12}{:<12}{:<12}{}\n", + task.task_id, + task.action as u8, + task.state as u8, + task.reason as u8, + task.total_size, + task.tran_size, + task.url + ) + .as_bytes(), + ); + } else { + let _ = file.write(format!("invalid task id {}", task_id).as_bytes()); + } +} diff --git a/services/service/request/src/service/command/mod.rs b/services/service/request/src/service/command/mod.rs new file mode 100644 index 00000000..67bd4b02 --- /dev/null +++ b/services/service/request/src/service/command/mod.rs @@ -0,0 +1,42 @@ +// Copyright (C) 2023 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. + +mod construct; +mod dump; +mod off; +mod on; +mod pause; +mod query; +mod query_mime_type; +mod remove; +mod resume; +mod search; +mod show; +mod start; +mod stop; +mod touch; + +pub(crate) use construct::Construct; +pub(crate) use dump::Dump; +pub(crate) use off::Off; +pub(crate) use on::On; +pub(crate) use pause::Pause; +pub(crate) use query::Query; +pub(crate) use query_mime_type::QueryMimeType; +pub(crate) use remove::Remove; +pub(crate) use resume::Resume; +pub(crate) use search::Search; +pub(crate) use show::Show; +pub(crate) use start::Start; +pub(crate) use stop::Stop; +pub(crate) use touch::Touch; diff --git a/services/service/request/src/service/command/off.rs b/services/service/request/src/service/command/off.rs new file mode 100644 index 00000000..84d568e0 --- /dev/null +++ b/services/service/request/src/service/command/off.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::service::ability::RequestAbility; +use crate::service::notify::{Event, NotifyEvent}; + +pub(crate) struct Off; + +impl Off { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service off"); + let off_type: String = data.read()?; + debug!("Service off: off_type {:?}", off_type); + + let event = match Event::try_from(off_type) { + Ok(event) => event, + Err(_) => { + error!("Service off: off_type not valid"); + reply.write(&(ErrorCode::ParameterCheck as i32))?; + return Err(IpcStatusCode::Failed); + } + }; + let id: String = data.read()?; + debug!("Service off: task_id {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service off: u32 task_id is {:?}", id); + let (event, rx) = NotifyEvent::off(event, id); + RequestAbility::notify().send_event(event); + + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service off: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service off: off failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service off: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/on.rs b/services/service/request/src/service/command/on.rs new file mode 100644 index 00000000..62a88453 --- /dev/null +++ b/services/service/request/src/service/command/on.rs @@ -0,0 +1,69 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{BorrowedMsgParcel, IpcResult, IpcStatusCode, RemoteObj}; + +use crate::error::ErrorCode; +use crate::service::ability::RequestAbility; +use crate::service::notify::{Event, NotifyEvent}; + +pub(crate) struct On; + +impl On { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service on"); + let on_type: String = data.read()?; + debug!("Service on: on_type is {:?}", on_type); + + let event = match Event::try_from(on_type) { + Ok(event) => event, + Err(_) => { + error!("Service on: on_type not valid"); + reply.write(&(ErrorCode::ParameterCheck as i32))?; + return Err(IpcStatusCode::Failed); + } + }; + let id: String = data.read()?; + debug!("Service on: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service on: u32 task_id is {}", id); + let obj: RemoteObj = data.read::()?; + let (event, rx) = NotifyEvent::on(event, id, obj); + RequestAbility::notify().send_event(event); + + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service on: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service on: on failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service on: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/pause.rs b/services/service/request/src/service/command/pause.rs new file mode 100644 index 00000000..f9549607 --- /dev/null +++ b/services/service/request/src/service/command/pause.rs @@ -0,0 +1,72 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; +use crate::task::config::Version; + +pub(crate) struct Pause; + +impl Pause { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service pause"); + let version: u32 = data.read()?; + debug!("Service pause: version {}", version); + if Version::from(version as u8) == Version::API9 && !PermissionChecker::check_internet() { + error!("Service pause: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + + let id: String = data.read()?; + debug!("Service pause: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service pause: u32 task_id is {}", id); + + let uid = get_calling_uid(); + debug!("Service pause: uid is {}", uid); + + let (event, rx) = EventMessage::pause(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service pause: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service pause: pause fail for ret is {}", ret as u32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service pause: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/query.rs b/services/service/request/src/service/command/query.rs new file mode 100644 index 00000000..3d721fdd --- /dev/null +++ b/services/service/request/src/service/command/query.rs @@ -0,0 +1,82 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::{PermissionChecker, QueryPermission}; +use crate::service::{is_system_api, serialize_task_info}; +use crate::task::config::Action; + +pub(crate) struct Query; + +impl Query { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service query"); + if !is_system_api() { + error!("Service query: not system api"); + reply.write(&(ErrorCode::SystemApi as i32))?; + return Err(IpcStatusCode::Failed); + } + let permission = PermissionChecker::check_query(); + let action = match permission { + QueryPermission::NoPermission => { + error!("Service query: no QUERY permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + QueryPermission::QueryDownLoad => Action::DownLoad, + QueryPermission::QueryUpload => Action::UpLoad, + QueryPermission::QueryAll => Action::Any, + }; + + let id: String = data.read()?; + debug!("Service query: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service query: u32 task_id is {}", id); + let (event, rx) = EventMessage::query(id, action); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + match rx.get() { + Some(Some(info)) => { + reply.write(&(ErrorCode::ErrOk as i32))?; + debug!("Service query: task_info - {:?}", info); + serialize_task_info(info, reply)?; + Ok(()) + } + Some(None) => { + error!("Service query: task_id not found"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + None => { + error!("Service query: receives task_info failed"); + Err(IpcStatusCode::Failed) + } + } + } + _ => { + error!("Service query: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/query_mime_type.rs b/services/service/request/src/service/command/query_mime_type.rs new file mode 100644 index 00000000..aee47e46 --- /dev/null +++ b/services/service/request/src/service/command/query_mime_type.rs @@ -0,0 +1,66 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; + +pub(crate) struct QueryMimeType; + +impl QueryMimeType { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service query mime type"); + if !PermissionChecker::check_internet() { + error!("Service query mime type: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + let id: String = data.read()?; + debug!("Service query mime type: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service query mime type: u32 task_id is {}", id); + + let uid = get_calling_uid(); + debug!("Service query mime type: uid is {}", uid); + + let (event, rx) = EventMessage::query_mime_type(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let mime = match rx.get() { + Some(mime) => mime, + None => { + error!("Service query mime type: receives mime failed"); + return Err(IpcStatusCode::Failed); + } + }; + debug!("Service query mime type: {}", mime); + reply.write(&(ErrorCode::ErrOk as i32))?; + reply.write(&mime)?; + Ok(()) + } + _ => { + error!("Service query mime type: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/remove.rs b/services/service/request/src/service/command/remove.rs new file mode 100644 index 00000000..5ba5326a --- /dev/null +++ b/services/service/request/src/service/command/remove.rs @@ -0,0 +1,69 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; +use crate::task::config::Version; + +pub(crate) struct Remove; + +impl Remove { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service remove"); + let version: u32 = data.read()?; + if Version::from(version as u8) == Version::API9 && !PermissionChecker::check_internet() { + error!("Service remove: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + + let id: String = data.read()?; + debug!("Service remove: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service remove: u32 task_id is {}", id); + let uid = get_calling_uid(); + debug!("Service remove: uid is {}", uid); + let (event, rx) = EventMessage::remove(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service remove: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service remove: remove failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service remove: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/resume.rs b/services/service/request/src/service/command/resume.rs new file mode 100644 index 00000000..2d5b8036 --- /dev/null +++ b/services/service/request/src/service/command/resume.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; + +pub(crate) struct Resume; + +impl Resume { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service resume"); + if !PermissionChecker::check_internet() { + error!("Service resume: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + + let id: String = data.read()?; + debug!("Service resume: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service resume: u32 task_id is {}", id); + let uid = get_calling_uid(); + debug!("Service resume: uid is {}", uid); + let (event, rx) = EventMessage::resume(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service resume: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service resume: resume failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service resume: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/search.rs b/services/service/request/src/service/command/search.rs new file mode 100644 index 00000000..6261ed33 --- /dev/null +++ b/services/service/request/src/service/command/search.rs @@ -0,0 +1,75 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::{get_calling_bundle, is_system_api}; +use crate::utils::filter::{CommonFilter, Filter}; + +pub(crate) struct Search; + +impl Search { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service search"); + let mut bundle: String = data.read()?; + if !is_system_api() { + debug!("Service search: not system api"); + bundle = get_calling_bundle(); + debug!("Service search: bundle change: {}", bundle); + } + debug!("Service search: bundle is {}", bundle); + let before: i64 = data.read()?; + debug!("Service search: before is {}", before); + let after: i64 = data.read()?; + debug!("Service search: after is {}", after); + let state: u32 = data.read()?; + debug!("Service search: state is {}", state); + let action: u32 = data.read()?; + debug!("Service search: action is {}", action); + let mode: u32 = data.read()?; + debug!("Service search: mode is {}", mode); + let common_data = CommonFilter { + before, + after, + state: state as u8, + action: action as u8, + mode: mode as u8, + }; + let filter = Filter { + bundle, + common_data, + }; + let (event, rx) = EventMessage::search(filter); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ids = match rx.get() { + Some(ids) => ids, + None => { + error!("Service search: receives ids failed"); + return Err(IpcStatusCode::Failed); + } + }; + debug!("Service search: search task ids is {:?}", ids); + reply.write(&(ids.len() as u32))?; + for it in ids.iter() { + reply.write(&(it.to_string()))?; + } + Ok(()) + } +} diff --git a/services/service/request/src/service/command/show.rs b/services/service/request/src/service/command/show.rs new file mode 100644 index 00000000..d4c995a1 --- /dev/null +++ b/services/service/request/src/service/command/show.rs @@ -0,0 +1,72 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; +use crate::service::serialize_task_info; + +pub(crate) struct Show; + +impl Show { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service show"); + if !PermissionChecker::check_internet() { + error!("Service show: no INTERNET permission"); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + let id: String = data.read()?; + debug!("Service show: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service show: u32 task_id is {}", id); + let uid = get_calling_uid(); + debug!("Service show: uid is {}", uid); + + let (event, rx) = EventMessage::show(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + match rx.get() { + Some(Some(info)) => { + reply.write(&(ErrorCode::ErrOk as i32))?; + debug!("Service show: task_info {:?}", info); + serialize_task_info(info, reply)?; + Ok(()) + } + Some(None) => { + error!("Service show: task_id not found"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + None => { + error!("Service show: receives task_info failed"); + Err(IpcStatusCode::Failed) + } + } + } + _ => { + error!("Service show: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/start.rs b/services/service/request/src/service/command/start.rs new file mode 100644 index 00000000..a2fc8d0c --- /dev/null +++ b/services/service/request/src/service/command/start.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::permission::PermissionChecker; + +pub(crate) struct Start; + +impl Start { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + info!("Service start"); + if !PermissionChecker::check_internet() { + error!("Service start: no INTERNET permission."); + reply.write(&(ErrorCode::Permission as i32))?; + return Err(IpcStatusCode::Failed); + } + + let id: String = data.read()?; + debug!("Service start: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service start: u32 task_id is {}", id); + let uid = get_calling_uid(); + debug!("Service start: uid is {}", uid); + + let (event, rx) = EventMessage::start(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service start: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service start: start failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service start: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/stop.rs b/services/service/request/src/service/command/stop.rs new file mode 100644 index 00000000..ee3b557f --- /dev/null +++ b/services/service/request/src/service/command/stop.rs @@ -0,0 +1,60 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; + +pub(crate) struct Stop; + +impl Stop { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + debug!("Service stop"); + let id: String = data.read()?; + debug!("Service stop: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service stop: u32 task_id is {}", id); + let uid = get_calling_uid(); + debug!("Service stop: uid is {}", uid); + let (event, rx) = EventMessage::stop(uid, id); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + let ret = match rx.get() { + Some(ret) => ret, + None => { + error!("Service stop: receives ret failed"); + return Err(IpcStatusCode::Failed); + } + }; + reply.write(&(ret as i32))?; + if ret != ErrorCode::ErrOk { + error!("Service stop: stop failed for ret is {}", ret as i32); + return Err(IpcStatusCode::Failed); + } + Ok(()) + } + _ => { + error!("Service stop: task_id not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/request/src/service/command/touch.rs b/services/service/request/src/service/command/touch.rs new file mode 100644 index 00000000..a20b10f9 --- /dev/null +++ b/services/service/request/src/service/command/touch.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2023 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. + +use ipc_rust::{get_calling_uid, BorrowedMsgParcel, IpcResult, IpcStatusCode}; + +use crate::error::ErrorCode; +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::service::serialize_task_info; + +pub(crate) struct Touch; + +impl Touch { + pub(crate) fn execute( + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + debug!("Service touch"); + let id: String = data.read()?; + debug!("Service touch: task_id is {}", id); + match id.parse::() { + Ok(id) => { + debug!("Service touch: u32 task_id is {}", id); + let token: String = data.read()?; + debug!("Service touch: token is {}", token); + let uid = get_calling_uid(); + debug!("Service touch: uid is {}", uid); + let (event, rx) = EventMessage::touch(uid, id, token); + if !RequestAbility::task_manager().send_event(event) { + return Err(IpcStatusCode::Failed); + } + match rx.get() { + Some(Some(info)) => { + reply.write(&(ErrorCode::ErrOk as i32))?; + debug!("Service touch: task_info get"); + serialize_task_info(info, reply)?; + Ok(()) + } + Some(None) => { + error!("Service touch: task_id or token not found"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + None => { + error!("Service touch: receives task_info failed"); + Err(IpcStatusCode::Failed) + } + } + } + _ => { + error!("Service touch: task_id or token not valid"); + reply.write(&(ErrorCode::TaskNotFound as i32))?; + Err(IpcStatusCode::Failed) + } + } + } +} diff --git a/services/service/rust/src/download_server_ipc_interface_code.rs b/services/service/request/src/service/interface.rs similarity index 55% rename from services/service/rust/src/download_server_ipc_interface_code.rs rename to services/service/request/src/service/interface.rs index d5ffcd15..131776ac 100644 --- a/services/service/rust/src/download_server_ipc_interface_code.rs +++ b/services/service/request/src/service/interface.rs @@ -1,55 +1,53 @@ -/* - * Copyright (C) 2023 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. - */ - -/* SAID: 3706*/ -/// Function code of RequestInterfaceCode -pub enum RequestInterfaceCode { - /// request construct & api10 create task - Construct = 0, - /// pause task - Pause, - /// query task || system api Queries specified task details - Query, - /// query mime type - QueryMimeType, - /// remove task || removes specifed task belongs to the caller - Remove, - /// resume task - Resume, - /// on task - On, - /// off task - Off, - /// ap10 start task - Start, - /// stop task - Stop, - /// Shows specified task details belongs to the caller - Show, - /// Touches specified task with token - Touch, - /// Searches tasks, for system - Search, - /// system api deletes specifed tasks - Clear, -} - -/// Function code of RequestNotifyInterfaceCode -pub enum RequestNotifyInterfaceCode { - /// callback notification - Notify = 0, - /// Cache callback notification - DoneNotify, -} \ No newline at end of file +// Copyright (C) 2023 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. + +// SAID: 3706 +/// Function code of RequestInterfaceCode +pub(crate) enum RequestInterfaceCode { + /// request construct & api10 create task + Construct = 0, + /// pause task + Pause, + /// query task || system api Queries specified task details + Query, + /// query mime type + QueryMimeType, + /// remove task || removes specifed task belongs to the caller + Remove, + /// resume task + Resume, + /// on task + On, + /// off task + Off, + /// ap10 start task + Start, + /// stop task + Stop, + /// Shows specified task details belongs to the caller + Show, + /// Touches specified task with token + Touch, + /// Searches tasks, for system + Search, + /// system api deletes specifed tasks + Clear, +} + +/// Function code of RequestNotifyInterfaceCode +pub(crate) enum RequestNotifyInterfaceCode { + /// callback notification + Notify = 0, + /// Cache callback notification + DoneNotify, +} diff --git a/services/service/request/src/service/listener/app.rs b/services/service/request/src/service/listener/app.rs new file mode 100644 index 00000000..6573ef52 --- /dev/null +++ b/services/service/request/src/service/listener/app.rs @@ -0,0 +1,51 @@ +// Copyright (C) 2023 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. + +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; +use crate::task::info::ApplicationState; + +pub(crate) struct AppStateListener; + +impl AppStateListener { + pub(crate) fn init() -> Self { + info!("AppStateListener prepares to be inited"); + unsafe { + RegisterAPPStateCallback(app_state_change_callback); + } + info!("AppStateListener is inited"); + Self + } + + pub(crate) fn shutdown(&self) { + // Considers remove the callback. + info!("AppStateListener is stopped"); + } +} + +extern "C" fn app_state_change_callback(uid: i32, state: i32) { + info!("Receives app state change callback"); + + let state = match state { + 2 => ApplicationState::Foreground, + 4 => ApplicationState::Background, + 5 => ApplicationState::Terminated, + _ => return, + }; + + RequestAbility::task_manager().send_event(EventMessage::app_state_change(uid as u64, state)); +} + +extern "C" { + fn RegisterAPPStateCallback(f: extern "C" fn(i32, i32)); +} diff --git a/services/service/request/src/service/listener/mod.rs b/services/service/request/src/service/listener/mod.rs new file mode 100644 index 00000000..794739f7 --- /dev/null +++ b/services/service/request/src/service/listener/mod.rs @@ -0,0 +1,18 @@ +// Copyright (C) 2023 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. + +mod app; +mod network; + +pub(crate) use app::AppStateListener; +pub(crate) use network::NetworkChangeListener; diff --git a/services/service/request/src/service/listener/network.rs b/services/service/request/src/service/listener/network.rs new file mode 100644 index 00000000..86dd2509 --- /dev/null +++ b/services/service/request/src/service/listener/network.rs @@ -0,0 +1,42 @@ +// Copyright (C) 2023 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. + +use crate::manager::events::EventMessage; +use crate::service::ability::RequestAbility; + +pub(crate) struct NetworkChangeListener; + +impl NetworkChangeListener { + pub(crate) fn init() -> Self { + info!("NetworkChangeListener prepares to be inited"); + unsafe { + RegisterNetworkCallback(network_change_callback); + } + info!("NetworkChangeListener is inited"); + Self + } + + pub(crate) fn shutdown(&self) { + // Considers remove the callback. + info!("NetworkChangeListener is stopped"); + } +} + +extern "C" fn network_change_callback() { + info!("Receives network change callback"); + RequestAbility::task_manager().send_event(EventMessage::network_change()); +} + +extern "C" { + fn RegisterNetworkCallback(f: extern "C" fn()); +} diff --git a/services/service/request/src/service/mod.rs b/services/service/request/src/service/mod.rs new file mode 100644 index 00000000..bfc6e35d --- /dev/null +++ b/services/service/request/src/service/mod.rs @@ -0,0 +1,362 @@ +// Copyright (C) 2023 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. + +//! This crate implement the request server service. + +pub(crate) mod ability; +pub(crate) mod command; +#[allow(unused)] +pub(crate) mod interface; +pub(crate) mod listener; +pub(crate) mod notify; +pub(crate) mod permission; + +use std::fs::{File, OpenOptions}; + +pub(crate) use interface::{RequestInterfaceCode, RequestNotifyInterfaceCode}; +use ipc_rust::{ + define_remote_object, get_calling_token_id, BorrowedMsgParcel, FileDesc, IRemoteBroker, + InterfaceToken, IpcResult, IpcStatusCode, RemoteObj, RemoteStub, String16, +}; + +use crate::task::info::TaskInfo; +use crate::utils::c_wrapper::CStringWrapper; + +define_remote_object!( + RequestServiceInterface["ohos.request.service"] { + stub: RequestServiceStub(on_remote_request), + proxy: RequestServiceProxy, + } +); + +fn on_remote_request( + stub: &dyn RequestServiceInterface, + code: u32, + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, +) -> IpcResult<()> { + const SERVICE_TOKEN: &str = "OHOS.Download.RequestServiceInterface"; + + info!("Processes on_remote_request, code: {}", code); + match data.read::().map(|token| token.get_token()) { + Ok(token) if token == SERVICE_TOKEN => {} + _ => { + error!("Gets invalid token"); + return Err(IpcStatusCode::Failed); + } + }; + match code.try_into()? { + RequestInterfaceCode::Construct => stub.construct(data, reply), + RequestInterfaceCode::Pause => stub.pause(data, reply), + RequestInterfaceCode::Query => stub.query(data, reply), + RequestInterfaceCode::QueryMimeType => stub.query_mime_type(data, reply), + RequestInterfaceCode::Remove => stub.remove(data, reply), + RequestInterfaceCode::Resume => stub.resume(data, reply), + RequestInterfaceCode::On => stub.on(data, reply), + RequestInterfaceCode::Off => stub.off(data, reply), + RequestInterfaceCode::Start => stub.start(data, reply), + RequestInterfaceCode::Stop => stub.stop(data, reply), + RequestInterfaceCode::Show => stub.show(data, reply), + RequestInterfaceCode::Touch => stub.touch(data, reply), + RequestInterfaceCode::Search => stub.search(data, reply), + RequestInterfaceCode::Clear => Ok(()), + } +} + +impl TryFrom for RequestInterfaceCode { + type Error = IpcStatusCode; + + fn try_from(code: u32) -> IpcResult { + match code { + _ if code == Self::Construct as u32 => Ok(Self::Construct), + _ if code == Self::Pause as u32 => Ok(Self::Pause), + _ if code == Self::Query as u32 => Ok(Self::Query), + _ if code == Self::QueryMimeType as u32 => Ok(Self::QueryMimeType), + _ if code == Self::Remove as u32 => Ok(Self::Remove), + _ if code == Self::Resume as u32 => Ok(Self::Resume), + _ if code == Self::On as u32 => Ok(Self::On), + _ if code == Self::Off as u32 => Ok(Self::Off), + _ if code == Self::Start as u32 => Ok(Self::Start), + _ if code == Self::Stop as u32 => Ok(Self::Stop), + _ if code == Self::Show as u32 => Ok(Self::Show), + _ if code == Self::Touch as u32 => Ok(Self::Touch), + _ if code == Self::Search as u32 => Ok(Self::Search), + _ if code == Self::Clear as u32 => Ok(Self::Clear), + _ => Err(IpcStatusCode::Failed), + } + } +} + +/// Functions between proxy and stub. +pub trait RequestServiceInterface: IRemoteBroker { + /// Constructs or creates a task. + fn construct( + &self, + _data: &BorrowedMsgParcel, + _reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + Ok(()) + } + + /// Pauses a task. + fn pause(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Queries tasks. + fn query(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Queries the mime type of a task. + fn query_mime_type( + &self, + _data: &BorrowedMsgParcel, + _reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + Ok(()) + } + + /// Removes a task. + fn remove(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Resumes a task. + fn resume(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Sets the `on` callback of a task. + fn on(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Sets the `off` callback of a task. + fn off(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Starts a task. + fn start(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Stops a task. + fn stop(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Shows a specified task details which belongs to the caller. + fn show(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Touches a specified task with token. + fn touch(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } + + /// Searches tasks of this system. + fn search(&self, _data: &BorrowedMsgParcel, _reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + Ok(()) + } +} + +impl RequestServiceInterface for RequestServiceProxy {} + +/// RequestService type +pub struct RequestService; + +impl IRemoteBroker for RequestService { + fn dump(&self, file: &FileDesc, args: &mut Vec) -> i32 { + command::Dump::execute(file, args) + } +} + +impl RequestServiceInterface for RequestService { + fn construct(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Construct::execute(data, reply) + } + + fn pause(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Pause::execute(data, reply) + } + + fn query(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Query::execute(data, reply) + } + + fn query_mime_type( + &self, + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> IpcResult<()> { + command::QueryMimeType::execute(data, reply) + } + + fn remove(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Remove::execute(data, reply) + } + + fn resume(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Resume::execute(data, reply) + } + + fn on(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::On::execute(data, reply) + } + + fn off(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Off::execute(data, reply) + } + + fn start(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Start::execute(data, reply) + } + + fn stop(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Stop::execute(data, reply) + } + + fn show(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Show::execute(data, reply) + } + + fn touch(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Touch::execute(data, reply) + } + + fn search(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + command::Search::execute(data, reply) + } +} + +pub(crate) fn serialize_task_info(tf: TaskInfo, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { + reply.write(&(tf.common_data.gauge))?; + reply.write(&(tf.common_data.retry))?; + reply.write(&(tf.common_data.action as u32))?; + reply.write(&(tf.common_data.mode as u32))?; + reply.write(&(tf.common_data.reason as u32))?; + reply.write(&(tf.common_data.tries))?; + reply.write(&(tf.common_data.uid.to_string()))?; + reply.write(&(tf.bundle))?; + reply.write(&(tf.url))?; + reply.write(&(tf.common_data.task_id.to_string()))?; + reply.write(&tf.title)?; + reply.write(&tf.mime_type)?; + reply.write(&(tf.common_data.ctime))?; + reply.write(&(tf.common_data.mtime))?; + reply.write(&(tf.data))?; + reply.write(&(tf.description))?; + reply.write(&(tf.common_data.priority))?; + + reply.write(&(tf.form_items.len() as u32))?; + for i in 0..tf.form_items.len() { + reply.write(&(tf.form_items[i].name))?; + reply.write(&(tf.form_items[i].value))?; + } + + reply.write(&(tf.file_specs.len() as u32))?; + for i in 0..tf.file_specs.len() { + reply.write(&(tf.file_specs[i].name))?; + reply.write(&(tf.file_specs[i].path))?; + reply.write(&(tf.file_specs[i].file_name))?; + reply.write(&(tf.file_specs[i].mime_type))?; + } + + reply.write(&(tf.progress.common_data.state as u32))?; + let index = tf.progress.common_data.index; + reply.write(&(index as u32))?; + reply.write(&(tf.progress.processed[index] as u64))?; + reply.write(&(tf.progress.common_data.total_processed as u64))?; + reply.write(&(tf.progress.sizes))?; + + reply.write(&(tf.progress.extras.len() as u32))?; + for (k, v) in tf.progress.extras.iter() { + reply.write(&(k))?; + reply.write(&(v))?; + } + + reply.write(&(tf.extras.len() as u32))?; + for (k, v) in tf.extras.iter() { + reply.write(&(k))?; + reply.write(&(v))?; + } + reply.write(&(tf.common_data.version as u32))?; + reply.write(&(tf.each_file_status.len() as u32))?; + for item in tf.each_file_status.iter() { + reply.write(&(item.path))?; + reply.write(&(item.reason as u32))?; + reply.write(&(item.message))?; + } + Ok(()) +} + +pub(crate) fn get_calling_bundle() -> String { + debug!("Gets calling bundle"); + let token_id = get_calling_token_id(); + debug!("Gets token id {}", &token_id); + unsafe { GetCallingBundle(token_id).to_string() } +} + +pub(crate) fn is_system_api() -> bool { + debug!("Checks if the api is a system_api"); + let token_id = get_calling_token_id(); + debug!("Gets token id {}", &token_id); + unsafe { RequestIsSystemAPI(token_id) } +} + +pub(crate) fn open_file_readwrite(uid: u64, bundle: &str, path: &str) -> IpcResult { + match OpenOptions::new() + .read(true) + .write(true) + .append(true) + .open(convert_path(uid, bundle, path)) + { + Ok(file) => Ok(file), + Err(e) => { + error!("open_file_readwrite failed, err is {:?}", e); + Err(IpcStatusCode::Failed) + } + } +} + +pub(crate) fn open_file_readonly(uid: u64, bundle: &str, path: &str) -> IpcResult { + match OpenOptions::new() + .read(true) + .open(convert_path(uid, bundle, path)) + { + Ok(file) => Ok(file), + Err(e) => { + error!("open_file_readonly failed, err is {:?}", e); + Err(IpcStatusCode::Failed) + } + } +} + +fn convert_path(uid: u64, bundle: &str, path: &str) -> String { + let uuid = uid / 200000; + let base = "/data/storage/el2/base/"; + format!( + "/data/app/el2/{}/base/{}/{}", + uuid, + bundle, + path.replace(base, "") + ) +} + +extern "C" { + pub(crate) fn GetCallingBundle(token_id: u64) -> CStringWrapper; + pub(crate) fn RequestIsSystemAPI(token_id: u64) -> bool; +} diff --git a/services/service/request/src/service/notify/manager.rs b/services/service/request/src/service/notify/manager.rs new file mode 100644 index 00000000..825ee994 --- /dev/null +++ b/services/service/request/src/service/notify/manager.rs @@ -0,0 +1,242 @@ +// Copyright (C) 2023 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. + +use std::collections::HashMap; + +use ipc_rust::{IRemoteObj, InterfaceToken, IpcResult, MsgParcel, RemoteObj}; +use ylong_runtime::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use ylong_runtime::sync::oneshot::Sender; + +use crate::error::ErrorCode; +use crate::service::notify::{Event, NotifyData, NotifyEvent}; +use crate::service::RequestNotifyInterfaceCode; +use crate::task::info::State; + +pub(crate) struct NotifyManager { + rx: UnboundedReceiver, + remotes: HashMap, + unregistered: HashMap>, +} + +impl NotifyManager { + fn new(rx: UnboundedReceiver) -> Self { + Self { + rx, + remotes: HashMap::new(), + unregistered: HashMap::new(), + } + } + + pub(crate) fn init() -> NotifyEntry { + info!("NotifyManager prepare to be inited"); + let (tx, rx) = unbounded_channel::(); + ylong_runtime::spawn(Self::new(rx).run()); + let entry = NotifyEntry::new(tx); + info!("NotifyManager is inited"); + entry + } + + pub(crate) async fn run(mut self) { + loop { + let event = match self.rx.recv().await { + Ok(message) => message, + Err(e) => { + error!("Notifier recv error {:?}", e); + continue; + } + }; + match event { + NotifyEvent::Notify(event, data) => self.notify(event, data), + NotifyEvent::On(event, id, obj, tx) => self.on(event, id, obj, tx), + NotifyEvent::Off(event, id, sender) => self.off(event, id, sender), + NotifyEvent::Clear(id) => self.clear(id), + NotifyEvent::Shutdown => { + info!("NotifyManager shuts down"); + return; + } + } + } + } +} + +impl NotifyManager { + fn notify(&mut self, event: Event, data: Box) { + debug!("NotifyManager gets event: {}", event.as_str()); + let key = NotifyKey::new(event, data.task_id); + if let Some(notifier) = self.remotes.get(&key) { + debug!("NotifyManager finds key succeed: {:?}", key); + // Ignores notify failed. + notifier.notify(event, data); + } else { + debug!("NotifyManager finds key failed: {:?}", key); + self.unregistered.insert(key, data); + } + } + + fn on(&mut self, event: Event, id: u32, obj: RemoteObj, sender: Sender) { + let key = NotifyKey::new(event, id); + let notifier = Notifier::new(obj); + + if let Some(data) = self.unregistered.remove(&key) { + debug!("NotifyManager notifies unregistered key: {:?}", key); + notifier.notify(event, data); + self.unregistered.remove(&key); + } + self.remotes.insert(key, notifier); + debug!("NotifyManager has inserted key: {:?}", key); + let _ = sender.send(ErrorCode::ErrOk); + } + + fn off(&mut self, event: Event, id: u32, sender: Sender) { + let key = NotifyKey::new(event, id); + if self.remotes.remove(&key).is_some() { + debug!("NotifyManager removes key: {:?}", key); + // Sends error code immediately, ignore the result. + let _ = sender.send(ErrorCode::ErrOk); + } else { + error!("NotifyManager removes key failed: {:?}", key); + // Sends error code immediately, ignore the result. + let _ = sender.send(ErrorCode::Other); + } + } + + fn clear(&mut self, id: u32) { + let events = [ + Event::Complete, + Event::Fail, + Event::HeaderReceive, + Event::Pause, + Event::Progress, + Event::Remove, + Event::Resume, + ]; + // Clears objects and unregistered notify data of the target task. + for event in events { + let key = NotifyKey::new(event, id); + self.remotes.remove(&key); + self.unregistered.remove(&key); + } + debug!("NotifyManager has cleared all the key of Task: {:?}", id); + } +} + +#[derive(Clone)] +pub(crate) struct NotifyEntry { + tx: UnboundedSender, +} + +impl NotifyEntry { + fn new(tx: UnboundedSender) -> Self { + Self { tx } + } + + pub(crate) fn shutdown(&self) { + // Ignore the result. + self.send_event(NotifyEvent::shutdown()); + } + + pub(crate) fn send_event(&self, event: NotifyEvent) { + if self.tx.send(event).is_err() { + error!("Sends NotifyEvent failed"); + } + } +} + +#[derive(Clone)] +struct Notifier { + obj: RemoteObj, +} + +impl Notifier { + fn new(obj: RemoteObj) -> Self { + Self { obj } + } + + fn notify(&self, event: Event, data: Box) { + debug!("Notifier gets notify data: {:?}", data); + if data.progress.common_data.index >= data.progress.sizes.len() { + error!("During notify: index out of range"); + return; + } + let common_data = &data.progress.common_data; + if (common_data.state == State::Running as u8 || common_data.state == State::Retrying as u8) + && common_data.total_processed == 0 + { + return; + } + + let mut parcel = match MsgParcel::new() { + Some(parcel) => parcel, + None => { + error!("During notify: create MsgParcel failed"); + return; + } + }; + + if write_parcel(&mut parcel, event, data.as_ref()).is_err() { + error!("During notify: ipc write failed"); + return; + } + + debug!("During notify: send request"); + if self + .obj + .send_request(RequestNotifyInterfaceCode::Notify as u32, &parcel, false) + .is_err() + { + error!("During notify: send request failed"); + } + } +} + +fn write_parcel(parcel: &mut MsgParcel, event: Event, data: &NotifyData) -> IpcResult<()> { + parcel.write(&InterfaceToken::new("OHOS.Download.NotifyInterface"))?; + parcel.write(&(event.as_str()))?; + parcel.write(&(data.task_id.to_string()))?; + parcel.write(&(data.progress.common_data.state as u32))?; + + let index = data.progress.common_data.index; + parcel.write(&(index as u32))?; + parcel.write(&(data.progress.processed[index] as u64))?; + + parcel.write(&(data.progress.common_data.total_processed as u64))?; + parcel.write(&(data.progress.sizes))?; + parcel.write(&(data.progress.extras.len() as u32))?; + + for (k, v) in data.progress.extras.iter() { + parcel.write(&k)?; + parcel.write(&v)?; + } + parcel.write(&(data.action as u32))?; + parcel.write(&(data.version as u32))?; + + parcel.write(&(data.each_file_status.len() as u32))?; + for status in data.each_file_status.iter() { + parcel.write(&(status.path))?; + parcel.write(&(status.reason as u32))?; + parcel.write(&(status.message))?; + } + Ok(()) +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +struct NotifyKey { + event: Event, + task_id: u32, +} + +impl NotifyKey { + fn new(event: Event, task_id: u32) -> Self { + Self { event, task_id } + } +} diff --git a/services/service/request/src/service/notify/mod.rs b/services/service/request/src/service/notify/mod.rs new file mode 100644 index 00000000..cc95b3da --- /dev/null +++ b/services/service/request/src/service/notify/mod.rs @@ -0,0 +1,107 @@ +// Copyright (C) 2023 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. + +mod manager; + +use ipc_rust::RemoteObj; +pub(crate) use manager::{NotifyEntry, NotifyManager}; +use ylong_runtime::sync::oneshot::{channel, Sender}; + +use crate::error::ErrorCode; +use crate::task::notify::NotifyData; +use crate::utils::Recv; + +pub(crate) enum NotifyEvent { + Notify(Event, Box), + On(Event, u32, RemoteObj, Sender), + Off(Event, u32, Sender), + Clear(u32), + Shutdown, +} + +impl NotifyEvent { + pub(crate) fn notify(event: Event, data: NotifyData) -> Self { + Self::Notify(event, Box::new(data)) + } + + pub(crate) fn on(event: Event, id: u32, obj: RemoteObj) -> (Self, Recv) { + let (tx, rx) = channel::(); + (Self::On(event, id, obj, tx), Recv::new(rx)) + } + + pub(crate) fn off(event: Event, id: u32) -> (Self, Recv) { + let (tx, rx) = channel::(); + (Self::Off(event, id, tx), Recv::new(rx)) + } + + pub(crate) fn clear(id: u32) -> Self { + Self::Clear(id) + } + + pub(crate) fn shutdown() -> Self { + Self::Shutdown + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub(crate) enum Event { + Complete, + Fail, + HeaderReceive, + Pause, + Progress, + Remove, + Resume, +} + +impl Event { + fn as_str(&self) -> &'static str { + match self { + Self::Complete => "complete", + Self::Fail => "fail", + Self::HeaderReceive => "headerReceive", + Self::Pause => "pause", + Self::Progress => "progress", + Self::Remove => "remove", + Self::Resume => "resume", + } + } +} + +impl TryFrom<&str> for Event { + type Error = EventConvertError; + + fn try_from(value: &str) -> Result { + match value { + "complete" => Ok(Self::Complete), + "fail" => Ok(Self::Fail), + "headerReceive" => Ok(Self::HeaderReceive), + "pause" => Ok(Self::Pause), + "progress" => Ok(Self::Progress), + "remove" => Ok(Self::Remove), + "resume" => Ok(Self::Resume), + _ => Err(EventConvertError), + } + } +} + +impl TryFrom for Event { + type Error = EventConvertError; + + fn try_from(value: String) -> Result { + Event::try_from(value.as_str()) + } +} + +#[derive(Debug)] +pub(crate) struct EventConvertError; diff --git a/services/service/request/src/service/permission.rs b/services/service/request/src/service/permission.rs new file mode 100644 index 00000000..f953e542 --- /dev/null +++ b/services/service/request/src/service/permission.rs @@ -0,0 +1,65 @@ +// Copyright (C) 2023 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. + +use ipc_rust::get_calling_token_id; + +use crate::utils::c_wrapper::CStringWrapper; + +static INTERNET_PERMISSION: &str = "ohos.permission.INTERNET"; +static QUERY_DOWNLOAD: &str = "ohos.permission.DOWNLOAD_SESSION_MANAGER"; +static QUERY_UPLOAD: &str = "ohos.permission.UPLOAD_SESSION_MANAGER"; + +pub(crate) struct PermissionChecker; + +impl PermissionChecker { + pub(crate) fn check_internet() -> bool { + debug!("Checks INTERNET permission"); + unsafe { + RequestCheckPermission( + get_calling_token_id(), + CStringWrapper::from(INTERNET_PERMISSION), + ) + } + } + + pub(crate) fn check_query() -> QueryPermission { + debug!("Checks QUERY permission"); + let id = get_calling_token_id(); + let query_download = + unsafe { RequestCheckPermission(id, CStringWrapper::from(QUERY_DOWNLOAD)) }; + let query_upload = + unsafe { RequestCheckPermission(id, CStringWrapper::from(QUERY_UPLOAD)) }; + info!( + "Checks query_download permission is {}, query_upload permission is {}", + query_download, query_upload + ); + + match (query_download, query_upload) { + (true, true) => QueryPermission::QueryAll, + (true, false) => QueryPermission::QueryDownLoad, + (false, true) => QueryPermission::QueryUpload, + (false, false) => QueryPermission::NoPermission, + } + } +} + +pub(crate) enum QueryPermission { + NoPermission = 0, + QueryDownLoad, + QueryUpload, + QueryAll, +} + +extern "C" { + pub(crate) fn RequestCheckPermission(token_id: u64, permission: CStringWrapper) -> bool; +} diff --git a/services/service/request/src/sys_event.rs b/services/service/request/src/sys_event.rs new file mode 100644 index 00000000..46490dbb --- /dev/null +++ b/services/service/request/src/sys_event.rs @@ -0,0 +1,65 @@ +// Copyright (C) 2023 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. + +use hisysevent::{write, EventType, HiSysEventParam}; + +const DOMAIN: &str = "REQUEST"; + +pub(crate) const ERROR_INFO: &str = "ERROR_INFO"; +pub(crate) const TASKS_TYPE: &str = "TASKS_TYPE"; +pub(crate) const TOTAL_FILE_NUM: &str = "TOTAL_FILE_NUM"; +pub(crate) const FAIL_FILE_NUM: &str = "FAIL_FILE_NUM"; +pub(crate) const SUCCESS_FILE_NUM: &str = "SUCCESS_FILE_NUM"; + +/// System events structure which base on `Hisysevent`. +pub(crate) struct SysEvent<'a> { + event_kind: EventKind, + inner_type: EventType, + params: Vec>, +} + +impl<'a> SysEvent<'a> { + pub(crate) fn task_fault() -> Self { + Self { + event_kind: EventKind::TaskFault, + inner_type: EventType::Fault, + params: Vec::new(), + } + } + + pub(crate) fn param(mut self, param: HiSysEventParam<'a>) -> Self { + self.params.push(param); + self + } + + pub(crate) fn write(self) { + write( + DOMAIN, + self.event_kind.as_str(), + self.inner_type, + self.params.as_slice(), + ); + } +} + +enum EventKind { + TaskFault, +} + +impl EventKind { + fn as_str(&self) -> &str { + match self { + EventKind::TaskFault => "TASK_FAULT", + } + } +} diff --git a/services/service/request/src/task/config.rs b/services/service/request/src/task/config.rs new file mode 100644 index 00000000..35b75794 --- /dev/null +++ b/services/service/request/src/task/config.rs @@ -0,0 +1,110 @@ +// Copyright (C) 2023 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. + +use std::collections::HashMap; + +use super::info::Mode; +use crate::utils::form_item::{FileSpec, FormItem}; + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub(crate) enum Action { + DownLoad = 0, + UpLoad, + Any, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub(crate) enum Version { + API9 = 1, + API10, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub(crate) enum Network { + Any = 0, + Wifi, + Cellular, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct CommonTaskConfig { + pub(crate) task_id: u32, + pub(crate) uid: u64, + pub(crate) action: Action, + pub(crate) mode: Mode, + pub(crate) cover: bool, + pub(crate) network: Network, + pub(crate) metered: bool, + pub(crate) roaming: bool, + pub(crate) retry: bool, + pub(crate) redirect: bool, + pub(crate) index: u32, + pub(crate) begins: u64, + pub(crate) ends: i64, + pub(crate) gauge: bool, + pub(crate) precise: bool, + pub(crate) priority: u32, + pub(crate) background: bool, +} + +#[derive(Debug)] +pub(crate) struct TaskConfig { + pub(crate) bundle: String, + pub(crate) url: String, + pub(crate) title: String, + pub(crate) description: String, + pub(crate) method: String, + pub(crate) headers: HashMap, + pub(crate) data: String, + pub(crate) token: String, + #[allow(unused)] + pub(crate) extras: HashMap, + pub(crate) version: Version, + pub(crate) form_items: Vec, + pub(crate) file_specs: Vec, + pub(crate) body_file_names: Vec, + pub(crate) common_data: CommonTaskConfig, +} + +impl From for Action { + fn from(value: u8) -> Self { + match value { + 0 => Action::DownLoad, + 1 => Action::UpLoad, + _ => Action::Any, + } + } +} + +impl From for Version { + fn from(value: u8) -> Self { + match value { + 2 => Version::API10, + _ => Version::API9, + } + } +} + +impl From for Network { + fn from(value: u8) -> Self { + match value { + 0 => Network::Any, + 2 => Network::Cellular, + _ => Network::Wifi, + } + } +} diff --git a/services/service/request/src/task/download.rs b/services/service/request/src/task/download.rs new file mode 100644 index 00000000..caf138d9 --- /dev/null +++ b/services/service/request/src/task/download.rs @@ -0,0 +1,173 @@ +// Copyright (C) 2023 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. + +use std::pin::Pin; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use ylong_http_client::async_impl::{DownloadOperator, Downloader}; +use ylong_http_client::{HttpClientError, Response, SpeedLimit, Timeout}; + +use super::operator::TaskOperator; +use super::reason::Reason; +use super::tick::Clock; +use crate::task::info::State; +use crate::task::RequestTask; + +cfg_oh! { + use crate::trace::Trace; +} + +const SECONDS_IN_ONE_WEEK: u64 = 7 * 24 * 60 * 60; + +const LOW_SPEED_TIME: u64 = 60; +const LOW_SPEED_LIMIT: u64 = 1; + +impl DownloadOperator for TaskOperator { + fn poll_download( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + data: &[u8], + ) -> Poll> { + let me = self.get_mut(); + if me.task.rate_limiting.load(Ordering::SeqCst) { + if me.check_point.take().is_none() { + Clock::get_instance().register(cx); + me.check_point = Some(()); + return Poll::Pending; + } + } else { + Clock::get_instance().tick(); + } + + if me.task.range_request.load(Ordering::SeqCst) { + if me.task.range_response.load(Ordering::SeqCst) { + return me.poll_write_file(cx, data, 0); + } + // write partial response data + let begins = me.task.conf.common_data.begins; + let ends = me.task.conf.common_data.ends; + return me.poll_write_partial_file(cx, data, begins, ends); + } + me.poll_write_file(cx, data, 0) + } + + fn poll_progress( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + _downloaded: u64, + _total: Option, + ) -> Poll> { + self.poll_progress_common(cx) + } +} + +pub(crate) fn build_downloader( + task: Arc, + response: Response, +) -> Downloader { + let task_operator = TaskOperator::new(task); + + Downloader::builder() + .body(response) + .operator(task_operator) + .timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK)) + .speed_limit(SpeedLimit::new().min_speed(LOW_SPEED_LIMIT, LOW_SPEED_TIME)) + .build() +} + +pub(crate) async fn download(task: Arc) { + download_inner(task.clone()).await; + + #[cfg(feature = "oh")] + use hisysevent::{build_number_param, build_str_param}; + + #[cfg(feature = "oh")] + use crate::sys_event::SysEvent; + #[cfg(feature = "oh")] + let reason = task.code.lock().unwrap()[0]; + // If `Reason` is not `Default`a records this sys event. + #[cfg(feature = "oh")] + if reason != Reason::Default { + SysEvent::task_fault() + .param(build_str_param!(crate::sys_event::TASKS_TYPE, "DOWNLOAD")) + .param(build_number_param!(crate::sys_event::TOTAL_FILE_NUM, 1)) + .param(build_number_param!(crate::sys_event::FAIL_FILE_NUM, 1)) + .param(build_number_param!(crate::sys_event::SUCCESS_FILE_NUM, 0)) + .param(build_number_param!( + crate::sys_event::ERROR_INFO, + reason as i32 + )) + .write(); + } +} + +async fn download_inner(task: Arc) { + info!("begin download"); + + // Ensures `_trace` can only be freed when this function exits. + #[cfg(feature = "oh")] + Trace::start("download file"); + + let response = match task.client.as_ref() { + Some(client) => { + let request = match task.build_download_request().await { + Some(request) => request, + None => return, + }; + + let name = task.conf.file_specs[0].path.as_str(); + let download = task.progress.lock().unwrap().processed[0]; + // Ensures `_trace` can only be freed when this function exits. + #[cfg(feature = "oh")] + Trace::start(&format!( + "download file name: {name} downloaded size: {download}" + )); + #[cfg(feature = "oh")] + Trace::finish(); + client.request(request).await + } + None => { + return; + } + }; + + task.record_response_header(&response); + if !task.handle_response_error(&response).await { + error!("response error"); + return; + } + let response = response.unwrap(); + + if !task.get_file_info(&response) { + return; + } + + let mut downloader = build_downloader(task.clone(), response); + + let result = downloader.download().await; + + if !task.handle_download_error(&result) { + error!("handle_download_error"); + return; + } + + // Makes sure all the data has been written to the target file. + if let Some(file) = task.files.get(0) { + let _ = file.sync_all().await; + } + task.set_status(State::Completed, Reason::Default); + #[cfg(feature = "oh")] + Trace::finish(); +} diff --git a/services/service/request/src/task/ffi.rs b/services/service/request/src/task/ffi.rs new file mode 100644 index 00000000..4d43b2ce --- /dev/null +++ b/services/service/request/src/task/ffi.rs @@ -0,0 +1,382 @@ +// Copyright (C) 2023 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. + +use std::ffi::c_char; + +use super::config::{Action, CommonTaskConfig, Network, TaskConfig, Version}; +use super::info::{CommonTaskInfo, InfoSet, Mode, TaskInfo, UpdateInfo}; +use super::notify::{CommonProgress, EachFileStatus, Progress}; +use super::reason::Reason; +use crate::utils::c_wrapper::{ + CFileSpec, CFormItem, CStringWrapper, DeleteCFileSpec, DeleteCFormItem, DeleteCStringPtr, +}; +use crate::utils::form_item::{FileSpec, FormItem}; +use crate::utils::{build_vec, hashmap_to_string, split_string, string_to_hashmap}; + +#[repr(C)] +pub(crate) struct CTaskConfig { + pub(crate) bundle: CStringWrapper, + pub(crate) url: CStringWrapper, + pub(crate) title: CStringWrapper, + pub(crate) description: CStringWrapper, + pub(crate) method: CStringWrapper, + pub(crate) headers: CStringWrapper, + pub(crate) data: CStringWrapper, + pub(crate) token: CStringWrapper, + pub(crate) extras: CStringWrapper, + pub(crate) version: u8, + pub(crate) form_items_ptr: *const CFormItem, + pub(crate) form_items_len: u32, + pub(crate) file_specs_ptr: *const CFileSpec, + pub(crate) file_specs_len: u32, + pub(crate) body_file_names_ptr: *const CStringWrapper, + pub(crate) body_file_names_len: u32, + pub(crate) common_data: CommonCTaskConfig, +} + +#[repr(C)] +pub(crate) struct CommonCTaskConfig { + pub(crate) task_id: u32, + pub(crate) uid: u64, + pub(crate) action: u8, + pub(crate) mode: u8, + pub(crate) cover: bool, + pub(crate) network: u8, + pub(crate) metered: bool, + pub(crate) roaming: bool, + pub(crate) retry: bool, + pub(crate) redirect: bool, + pub(crate) index: u32, + pub(crate) begins: u64, + pub(crate) ends: i64, + pub(crate) gauge: bool, + pub(crate) precise: bool, + pub(crate) priority: u32, + pub(crate) background: bool, +} + +#[repr(C)] +pub(crate) struct CEachFileStatus { + pub(crate) path: CStringWrapper, + pub(crate) reason: u8, + pub(crate) message: CStringWrapper, +} + +impl EachFileStatus { + pub(crate) fn to_c_struct(&self) -> CEachFileStatus { + CEachFileStatus { + path: CStringWrapper::from(&self.path), + reason: self.reason as u8, + message: CStringWrapper::from(&self.message), + } + } + + pub(crate) fn from_c_struct(c_struct: &CEachFileStatus) -> EachFileStatus { + EachFileStatus { + path: c_struct.path.to_string(), + reason: Reason::from(c_struct.reason), + message: c_struct.message.to_string(), + } + } +} + +#[repr(C)] +pub(crate) struct CProgress { + pub(crate) common_data: CommonProgress, + pub(crate) sizes: CStringWrapper, + pub(crate) processed: CStringWrapper, + pub(crate) extras: CStringWrapper, +} + +impl Progress { + pub(crate) fn to_c_struct(&self, sizes: &str, processed: &str, extras: &str) -> CProgress { + CProgress { + common_data: self.common_data.clone(), + sizes: CStringWrapper::from(sizes), + processed: CStringWrapper::from(processed), + extras: CStringWrapper::from(extras), + } + } + + pub(crate) fn from_c_struct(c_struct: &CProgress) -> Self { + Progress { + common_data: c_struct.common_data.clone(), + sizes: split_string(&mut c_struct.sizes.to_string()) + .map(|s| s.parse::().unwrap()) + .collect(), + processed: split_string(&mut c_struct.processed.to_string()) + .map(|s| s.parse::().unwrap()) + .collect(), + extras: string_to_hashmap(&mut c_struct.extras.to_string()), + } + } +} + +#[repr(C)] +pub(crate) struct CTaskInfo { + pub(crate) bundle: CStringWrapper, + pub(crate) url: CStringWrapper, + pub(crate) data: CStringWrapper, + pub(crate) token: CStringWrapper, + pub(crate) form_items_ptr: *const CFormItem, + pub(crate) form_items_len: u32, + pub(crate) file_specs_ptr: *const CFileSpec, + pub(crate) file_specs_len: u32, + pub(crate) title: CStringWrapper, + pub(crate) description: CStringWrapper, + pub(crate) mime_type: CStringWrapper, + pub(crate) progress: CProgress, + pub(crate) each_file_status_ptr: *const CEachFileStatus, + pub(crate) each_file_status_len: u32, + pub(crate) common_data: CommonTaskInfo, +} + +impl TaskInfo { + pub(crate) fn to_c_struct(&self, info: &InfoSet) -> CTaskInfo { + CTaskInfo { + bundle: CStringWrapper::from(&self.bundle), + url: CStringWrapper::from(&self.url), + data: CStringWrapper::from(&self.data), + token: CStringWrapper::from(&self.token), + form_items_ptr: info.form_items.as_ptr(), + form_items_len: info.form_items.len() as u32, + file_specs_ptr: info.file_specs.as_ptr(), + file_specs_len: info.file_specs.len() as u32, + title: CStringWrapper::from(&self.title), + description: CStringWrapper::from(&self.description), + mime_type: CStringWrapper::from(&self.mime_type), + progress: self + .progress + .to_c_struct(&info.sizes, &info.processed, &info.extras), + each_file_status_ptr: info.each_file_status.as_ptr(), + each_file_status_len: info.each_file_status.len() as u32, + common_data: self.common_data, + } + } + + pub(crate) fn from_c_struct(c_struct: &CTaskInfo) -> Self { + let progress = Progress::from_c_struct(&c_struct.progress); + let extras = progress.extras.clone(); + + // Removes this logic if api9 and api10 matched. + let mime_type = if c_struct.common_data.version == Version::API9 as u8 { + c_struct.mime_type.to_string() + } else { + String::new() + }; + + let task_info = TaskInfo { + bundle: c_struct.bundle.to_string(), + url: c_struct.url.to_string(), + data: c_struct.data.to_string(), + token: c_struct.token.to_string(), + form_items: build_vec( + c_struct.form_items_ptr, + c_struct.form_items_len as usize, + FormItem::from_c_struct, + ), + file_specs: build_vec( + c_struct.file_specs_ptr, + c_struct.file_specs_len as usize, + FileSpec::from_c_struct, + ), + title: c_struct.title.to_string(), + description: c_struct.description.to_string(), + mime_type, + progress, + extras, + each_file_status: build_vec( + c_struct.each_file_status_ptr, + c_struct.each_file_status_len as usize, + EachFileStatus::from_c_struct, + ), + common_data: c_struct.common_data, + }; + + unsafe { DeleteCFormItem(c_struct.form_items_ptr) }; + unsafe { DeleteCFileSpec(c_struct.file_specs_ptr) }; + unsafe { DeleteCEachFileStatus(c_struct.each_file_status_ptr) }; + task_info + } +} + +#[repr(C)] +pub(crate) struct CUpdateInfo { + pub(crate) mtime: u64, + pub(crate) reason: u8, + pub(crate) tries: u32, + pub(crate) mime_type: CStringWrapper, + pub(crate) progress: CProgress, + pub(crate) each_file_status_ptr: *const CEachFileStatus, + pub(crate) each_file_status_len: u32, +} + +impl UpdateInfo { + pub(crate) fn to_c_struct( + &self, + sizes: &str, + processed: &str, + extras: &str, + each_file_status: &Vec, + ) -> CUpdateInfo { + CUpdateInfo { + mtime: self.mtime, + reason: self.reason, + tries: self.tries, + mime_type: CStringWrapper::from(self.mime_type.as_str()), + progress: self.progress.to_c_struct(sizes, processed, extras), + each_file_status_ptr: each_file_status.as_ptr(), + each_file_status_len: each_file_status.len() as u32, + } + } +} + +#[repr(C)] +pub(crate) struct RequestTaskMsg { + pub(crate) task_id: u32, + pub(crate) uid: i32, + pub(crate) action: u8, +} + +#[derive(Debug)] +#[repr(C)] +pub(crate) struct NetworkInfo { + pub(crate) network_type: Network, + pub(crate) is_metered: bool, + pub(crate) is_roaming: bool, +} + +impl TaskConfig { + pub(crate) fn to_c_struct(&self, task_id: u32, uid: u64) -> CTaskConfig { + let form_items: Vec = self.form_items.iter().map(|x| x.to_c_struct()).collect(); + let file_specs: Vec = self.file_specs.iter().map(|x| x.to_c_struct()).collect(); + let body_file_names: Vec = self + .body_file_names + .iter() + .map(CStringWrapper::from) + .collect(); + CTaskConfig { + bundle: CStringWrapper::from(&self.bundle), + url: CStringWrapper::from(&self.url), + title: CStringWrapper::from(&self.title), + description: CStringWrapper::from(&self.description), + method: CStringWrapper::from(&self.method), + headers: CStringWrapper::from(&hashmap_to_string(&self.headers)), + data: CStringWrapper::from(&self.data), + token: CStringWrapper::from(&self.token), + extras: CStringWrapper::from(&hashmap_to_string(&self.extras)), + version: self.version as u8, + form_items_ptr: form_items.as_ptr(), + form_items_len: form_items.len() as u32, + file_specs_ptr: file_specs.as_ptr(), + file_specs_len: file_specs.len() as u32, + body_file_names_ptr: body_file_names.as_ptr() as *const CStringWrapper, + body_file_names_len: body_file_names.len() as u32, + common_data: CommonCTaskConfig { + task_id, + uid, + action: self.common_data.action as u8, + mode: self.common_data.mode as u8, + cover: self.common_data.cover, + network: self.common_data.network as u8, + metered: self.common_data.metered, + roaming: self.common_data.roaming, + retry: self.common_data.retry, + redirect: self.common_data.redirect, + index: self.common_data.index, + begins: self.common_data.begins, + ends: self.common_data.ends, + gauge: self.common_data.gauge, + precise: self.common_data.precise, + priority: self.common_data.priority, + background: self.common_data.background, + }, + } + } + + pub(crate) fn from_c_struct(c_struct: &CTaskConfig) -> Self { + let task_config = TaskConfig { + bundle: c_struct.bundle.to_string(), + url: c_struct.url.to_string(), + title: c_struct.title.to_string(), + description: c_struct.description.to_string(), + method: c_struct.method.to_string(), + headers: string_to_hashmap(&mut c_struct.headers.to_string()), + data: c_struct.data.to_string(), + token: c_struct.token.to_string(), + extras: string_to_hashmap(&mut c_struct.extras.to_string()), + version: Version::from(c_struct.version), + form_items: build_vec( + c_struct.form_items_ptr, + c_struct.form_items_len as usize, + FormItem::from_c_struct, + ), + file_specs: build_vec( + c_struct.file_specs_ptr, + c_struct.file_specs_len as usize, + FileSpec::from_c_struct, + ), + body_file_names: build_vec( + c_struct.body_file_names_ptr, + c_struct.body_file_names_len as usize, + CStringWrapper::to_string, + ), + common_data: CommonTaskConfig { + task_id: c_struct.common_data.task_id, + uid: c_struct.common_data.uid, + action: Action::from(c_struct.common_data.action), + mode: Mode::from(c_struct.common_data.mode), + cover: c_struct.common_data.cover, + network: Network::from(c_struct.common_data.network), + metered: c_struct.common_data.metered, + roaming: c_struct.common_data.roaming, + retry: c_struct.common_data.retry, + redirect: c_struct.common_data.redirect, + index: c_struct.common_data.index, + begins: c_struct.common_data.begins, + ends: c_struct.common_data.ends, + gauge: c_struct.common_data.gauge, + precise: c_struct.common_data.precise, + priority: c_struct.common_data.priority, + background: c_struct.common_data.background, + }, + }; + unsafe { DeleteCFormItem(c_struct.form_items_ptr) }; + unsafe { DeleteCFileSpec(c_struct.file_specs_ptr) }; + unsafe { DeleteCStringPtr(c_struct.body_file_names_ptr) }; + task_config + } +} + +extern "C" { + pub(crate) fn GetNetworkInfo() -> *const NetworkInfo; + pub(crate) fn DeleteCTaskInfo(ptr: *const CTaskInfo); + pub(crate) fn RecordRequestTaskInfo(taskInfo: *const CTaskInfo) -> bool; + pub(crate) fn DeleteCEachFileStatus(ptr: *const CEachFileStatus); + pub(crate) fn UpdateRequestTaskInfo(taskId: u32, updateInfo: *const CUpdateInfo) -> bool; + pub(crate) fn HasRequestTaskRecord(taskId: u32) -> bool; + pub(crate) fn PublishStateChangeEvents( + bundleName: *const c_char, + bundleNameLen: u32, + taskId: u32, + state: i32, + ); + + pub(crate) fn RequestBackgroundNotify( + msg: RequestTaskMsg, + path: *const c_char, + pathLen: i32, + percent: u32, + ); + +} diff --git a/services/service/request/src/task/info.rs b/services/service/request/src/task/info.rs new file mode 100644 index 00000000..38ff2d9d --- /dev/null +++ b/services/service/request/src/task/info.rs @@ -0,0 +1,185 @@ +// Copyright (C) 2023 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. + +use std::collections::HashMap; + +use super::ffi::CEachFileStatus; +use super::notify::{EachFileStatus, Progress}; +use crate::task::config::Action; +use crate::task::reason::Reason; +use crate::utils::c_wrapper::{CFileSpec, CFormItem}; +use crate::utils::form_item::{FileSpec, FormItem}; +use crate::utils::hashmap_to_string; + +#[derive(Debug)] +pub(crate) struct TaskInfo { + pub(crate) bundle: String, + pub(crate) url: String, + pub(crate) data: String, + pub(crate) token: String, + pub(crate) form_items: Vec, + pub(crate) file_specs: Vec, + pub(crate) title: String, + pub(crate) description: String, + pub(crate) mime_type: String, + pub(crate) progress: Progress, + pub(crate) extras: HashMap, + pub(crate) each_file_status: Vec, + pub(crate) common_data: CommonTaskInfo, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct CommonTaskInfo { + pub(crate) task_id: u32, + pub(crate) uid: u64, + pub(crate) action: u8, + pub(crate) mode: u8, + pub(crate) ctime: u64, + pub(crate) mtime: u64, + pub(crate) reason: u8, + pub(crate) gauge: bool, + pub(crate) retry: bool, + pub(crate) tries: u32, + pub(crate) version: u8, + pub(crate) priority: u32, +} + +pub(crate) struct InfoSet { + pub(crate) form_items: Vec, + pub(crate) file_specs: Vec, + pub(crate) sizes: String, + pub(crate) processed: String, + pub(crate) extras: String, + pub(crate) each_file_status: Vec, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub(crate) enum Mode { + BackGround = 0, + FrontEnd, + Any, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub(crate) enum State { + Initialized = 0x00, + Waiting = 0x10, + Running = 0x20, + Retrying = 0x21, + Paused = 0x30, + Stopped = 0x31, + Completed = 0x40, + Failed = 0x41, + Removed = 0x50, + Created = 0x60, + Any, +} + +pub(crate) struct UpdateInfo { + pub(crate) mtime: u64, + pub(crate) reason: u8, + pub(crate) tries: u32, + pub(crate) mime_type: String, + pub(crate) progress: Progress, + pub(crate) each_file_status: Vec, +} + +impl From for Mode { + fn from(value: u8) -> Self { + match value { + 0 => Mode::BackGround, + 1 => Mode::FrontEnd, + _ => Mode::Any, + } + } +} + +impl From for State { + fn from(value: u8) -> Self { + match value { + 0 => State::Initialized, + 16 => State::Waiting, + 32 => State::Running, + 33 => State::Retrying, + 48 => State::Paused, + 49 => State::Stopped, + 64 => State::Completed, + 65 => State::Failed, + 80 => State::Removed, + 96 => State::Created, + _ => State::Any, + } + } +} + +impl TaskInfo { + pub(crate) fn build_info_set(&self) -> InfoSet { + InfoSet { + form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(), + file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(), + sizes: format!("{:?}", self.progress.sizes), + processed: format!("{:?}", self.progress.processed), + extras: hashmap_to_string(&self.extras), + each_file_status: self + .each_file_status + .iter() + .map(|x| x.to_c_struct()) + .collect(), + } + } +} + +#[derive(Debug)] +pub(crate) struct DumpAllInfo { + pub(crate) vec: Vec, +} + +#[derive(Debug)] +pub(crate) struct DumpAllEachInfo { + pub(crate) task_id: u32, + pub(crate) action: Action, + pub(crate) state: State, + pub(crate) reason: Reason, +} + +#[derive(Debug)] +pub(crate) struct DumpOneInfo { + pub(crate) task_id: u32, + pub(crate) action: Action, + pub(crate) state: State, + pub(crate) reason: Reason, + pub(crate) total_size: i64, + pub(crate) tran_size: usize, + pub(crate) url: String, +} + +#[derive(Clone, Copy, PartialEq, Debug, Eq, PartialOrd, Ord)] +pub(crate) enum ApplicationState { + Foreground = 2, + Background = 4, + Terminated = 5, +} + +impl From for ApplicationState { + fn from(value: u8) -> Self { + match value { + 2 => ApplicationState::Foreground, + 4 => ApplicationState::Background, + 5 => ApplicationState::Terminated, + _ => panic!("wrong application value"), + } + } +} diff --git a/services/service/request/src/task/mod.rs b/services/service/request/src/task/mod.rs new file mode 100644 index 00000000..f388ad85 --- /dev/null +++ b/services/service/request/src/task/mod.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2023 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. + +pub(crate) mod config; +pub(crate) mod ffi; +pub(crate) mod info; +pub(crate) mod notify; +pub(crate) mod reason; +pub(crate) mod request_task; +pub(crate) use request_task::RequestTask; +mod download; +mod operator; +mod tick; +mod upload; diff --git a/services/service/request/src/task/notify.rs b/services/service/request/src/task/notify.rs new file mode 100644 index 00000000..b49d46be --- /dev/null +++ b/services/service/request/src/task/notify.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2023 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. + +use std::collections::HashMap; + +use super::config::{Action, Version}; +use super::info::State; +use super::reason::Reason; + +#[derive(Debug, Clone)] +pub(crate) struct NotifyData { + pub(crate) progress: Progress, + pub(crate) action: Action, + pub(crate) version: Version, + pub(crate) each_file_status: Vec, + pub(crate) task_id: u32, + pub(crate) _uid: u64, + pub(crate) _bundle: String, +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub(crate) struct CommonProgress { + pub(crate) state: u8, + pub(crate) index: usize, + pub(crate) total_processed: usize, +} + +#[derive(Debug, Clone)] +pub(crate) struct Progress { + pub(crate) common_data: CommonProgress, + pub(crate) sizes: Vec, + pub(crate) processed: Vec, + pub(crate) extras: HashMap, +} + +#[derive(Debug, Clone)] +pub(crate) struct EachFileStatus { + pub(crate) path: String, + pub(crate) reason: Reason, + pub(crate) message: String, +} + +impl Progress { + pub(crate) fn new(sizes: Vec) -> Self { + let len = sizes.len(); + Progress { + common_data: CommonProgress { + state: State::Created as u8, + index: 0, + total_processed: 0, + }, + sizes, + processed: vec![0; len], + extras: HashMap::::new(), + } + } +} diff --git a/services/service/request/src/task/operator.rs b/services/service/request/src/task/operator.rs new file mode 100644 index 00000000..022712bb --- /dev/null +++ b/services/service/request/src/task/operator.rs @@ -0,0 +1,129 @@ +// Copyright (C) 2023 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. + +use std::pin::Pin; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use ylong_http_client::HttpClientError; +use ylong_runtime::io::AsyncWrite; + +#[cfg(feature = "oh")] +use crate::manager::Notifier; +use crate::task::config::Version; +use crate::task::info::State; +use crate::task::RequestTask; +use crate::utils::get_current_timestamp; + +const FRONT_NOTIFY_INTERVAL: u64 = 1000; +const BACKGROUND_NOTIFY_INTERVAL: u64 = 3000; + +pub(crate) struct TaskOperator { + pub(crate) task: Arc, + pub(crate) check_point: Option<()>, +} + +impl TaskOperator { + pub(crate) fn new(task: Arc) -> Self { + Self { + task, + check_point: None, + } + } + + pub(crate) fn poll_progress_common( + &self, + _cx: &mut Context<'_>, + ) -> Poll> { + let current = get_current_timestamp(); + + let state = self.task.status.lock().unwrap().state; + if (state != State::Running && state != State::Retrying) + || (self.task.conf.version == Version::API10 && !self.task.check_net_work_status()) + { + debug!("pause the task"); + return Poll::Ready(Err(HttpClientError::user_aborted())); + } + + let version = self.task.conf.version; + if current > self.task.last_notify.load(Ordering::SeqCst) + FRONT_NOTIFY_INTERVAL { + let notify_data = self.task.build_notify_data(); + self.task.last_notify.store(current, Ordering::SeqCst); + + #[cfg(feature = "oh")] + Notifier::service_front_notify("progress".into(), notify_data, &self.task.app_state); + } + + let gauge = self.task.conf.common_data.gauge; + if version == Version::API9 || gauge { + let last_background_notify_time = + self.task.background_notify_time.load(Ordering::SeqCst); + if get_current_timestamp() - last_background_notify_time >= BACKGROUND_NOTIFY_INTERVAL { + self.task.background_notify(); + } + } + Poll::Ready(Ok(())) + } + + pub(crate) fn poll_write_partial_file( + &self, + cx: &mut Context<'_>, + data: &[u8], + begins: u64, + ends: i64, + ) -> Poll> { + let data_size = data.len(); + let skip_size = self.task.skip_bytes.load(Ordering::SeqCst); + if skip_size + data_size as u64 <= begins { + self.task + .skip_bytes + .fetch_add(data_size as u64, Ordering::SeqCst); + return Poll::Ready(Ok(data_size)); + } + let remain_skip_bytes = (begins - skip_size) as usize; + let mut data = &data[remain_skip_bytes..]; + self.task.skip_bytes.store(begins, Ordering::SeqCst); + if ends >= 0 { + let total_bytes = ends as u64 - begins + 1; + let written_bytes = self.task.progress.lock().unwrap().processed[0] as u64; + if written_bytes == total_bytes { + return Poll::Ready(Err(HttpClientError::user_aborted())); + } + if data.len() as u64 + written_bytes >= total_bytes { + let remain_bytes = (total_bytes - written_bytes) as usize; + data = &data[..remain_bytes]; + } + } + self.poll_write_file(cx, data, remain_skip_bytes) + } + + pub(crate) fn poll_write_file( + &self, + cx: &mut Context<'_>, + data: &[u8], + skip_size: usize, + ) -> Poll> { + let file = unsafe { &mut *self.task.files.0.get() }.get_mut(0).unwrap(); + let mut progress_guard = self.task.progress.lock().unwrap(); + match Pin::new(file).poll_write(cx, data) { + Poll::Ready(Ok(size)) => { + progress_guard.processed[0] += size; + progress_guard.common_data.total_processed += size; + Poll::Ready(Ok(size + skip_size)) + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(HttpClientError::other(Some(e)))), + } + } +} diff --git a/services/service/request/src/task/reason.rs b/services/service/request/src/task/reason.rs new file mode 100644 index 00000000..97e17401 --- /dev/null +++ b/services/service/request/src/task/reason.rs @@ -0,0 +1,93 @@ +// Copyright (C) 2023 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. + +#[derive(Clone, Copy, PartialEq, Debug)] +pub(crate) enum Reason { + Default = 0, + TaskSurvivalOneMonth, + WaittingNetWorkOneday, + StoppedByNewFrontTask, + RunningTaskMeetLimits, + UserOperation, + AppBackgroundOrTerminate, + NetWorkOffline, + UnSupportedNetWorkType, + BuildClientFailed, + BuildRequestFailed, + GetFileSizeFailed, + ContinuousTaskTimeOut, + ConnectError, + RequestError, + UploadFileError, + RedirectError, + ProtocolError, + IoError, + UnSupportRangeRequest, + OthersError, +} + +impl From for Reason { + fn from(value: u8) -> Self { + match value { + 0 => Reason::Default, + 1 => Reason::TaskSurvivalOneMonth, + 2 => Reason::WaittingNetWorkOneday, + 3 => Reason::StoppedByNewFrontTask, + 4 => Reason::RunningTaskMeetLimits, + 5 => Reason::UserOperation, + 6 => Reason::AppBackgroundOrTerminate, + 7 => Reason::NetWorkOffline, + 8 => Reason::UnSupportedNetWorkType, + 9 => Reason::BuildClientFailed, + 10 => Reason::BuildRequestFailed, + 11 => Reason::GetFileSizeFailed, + 12 => Reason::ContinuousTaskTimeOut, + 13 => Reason::ConnectError, + 14 => Reason::RequestError, + 15 => Reason::UploadFileError, + 16 => Reason::RedirectError, + 17 => Reason::ProtocolError, + 18 => Reason::IoError, + 19 => Reason::UnSupportRangeRequest, + _ => Reason::OthersError, + } + } +} + +impl Reason { + pub(crate) fn to_str(self) -> &'static str { + match self { + Reason::Default => "", + Reason::TaskSurvivalOneMonth => "The task has not been completed for a month yet", + Reason::WaittingNetWorkOneday => "The task waiting for network recovery has not been completed for a day yet", + Reason::StoppedByNewFrontTask => "Stopped by a new front task", + Reason::RunningTaskMeetLimits => "Too many task in running state", + Reason::UserOperation => "User operation", + Reason::AppBackgroundOrTerminate => "The app is background or terminate", + Reason::NetWorkOffline => "NetWork is offline", + Reason::UnSupportedNetWorkType => "NetWork type not meet the task config", + Reason::BuildClientFailed => "Build client error", + Reason::BuildRequestFailed => "Build request error", + Reason::GetFileSizeFailed => "Failed because cannot get the file size from the server and the precise is setted true by user", + Reason::ContinuousTaskTimeOut => "Continuous processing task time out", + Reason::ConnectError => "Connect error", + Reason::RequestError => "Request error", + Reason::UploadFileError => "There are some files upload failed", + Reason::RedirectError => "Redirect error", + Reason::ProtocolError => "Http protocol error", + Reason::IoError => "Io Error", + Reason::UnSupportRangeRequest => "The server is not support range request", + Reason::OthersError => "Some other error occured", + } + } +} diff --git a/services/service/request/src/task/request_task.rs b/services/service/request/src/task/request_task.rs new file mode 100644 index 00000000..7652d819 --- /dev/null +++ b/services/service/request/src/task/request_task.rs @@ -0,0 +1,1107 @@ +// Copyright (C) 2023 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. + +use std::cell::UnsafeCell; +use std::ffi::{c_char, CString}; +use std::fs::File; +use std::io::{Read, SeekFrom}; +use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, AtomicU8, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread::sleep; +use std::time::Duration; + +use ylong_http_client::async_impl::Client; +use ylong_http_client::{ + Body, Certificate, ErrorKind, HttpClientError, Method, Redirect, Request, RequestBuilder, + Response, Timeout, TlsVersion, +}; +use ylong_runtime::fs::File as YlongFile; +use ylong_runtime::io::{AsyncSeekExt, AsyncWriteExt}; + +use super::config::{Network, Version}; +use super::download::download; +use super::ffi::{HasRequestTaskRecord, PublishStateChangeEvents}; +use super::info::{CommonTaskInfo, Mode, State, TaskInfo, UpdateInfo}; +use super::notify::{EachFileStatus, NotifyData, Progress}; +use super::reason::Reason; +use super::upload::upload; +use crate::manager::monitor::IsOnline; +use crate::task::config::{Action, TaskConfig}; +use crate::task::ffi::{ + GetNetworkInfo, RecordRequestTaskInfo, RequestBackgroundNotify, RequestTaskMsg, + UpdateRequestTaskInfo, +}; +use crate::utils::{get_current_timestamp, hashmap_to_string}; + +cfg_oh! { + use crate::service::{open_file_readonly, open_file_readwrite}; + use crate::manager::Notifier; +} + +const SECONDS_IN_ONE_WEEK: u64 = 7 * 24 * 60 * 60; + +const CONNECT_TIMEOUT: u64 = 60; +const RETRY_INTERVAL: u64 = 20; + +#[derive(Clone, Debug)] +pub(crate) struct TaskStatus { + pub(crate) waitting_network_time: Option, + pub(crate) mtime: u64, + pub(crate) state: State, + pub(crate) reason: Reason, +} + +impl Default for TaskStatus { + fn default() -> Self { + TaskStatus { + waitting_network_time: None, + mtime: get_current_timestamp(), + state: State::Created, + reason: Reason::Default, + } + } +} + +pub(crate) struct Files(pub(crate) UnsafeCell>); + +impl Files { + pub(crate) fn get(&self, index: usize) -> Option<&YlongFile> { + unsafe { &*self.0.get() }.get(index) + } +} + +unsafe impl Sync for Files {} +unsafe impl Send for Files {} + +// Need to release file timely. +pub(crate) struct BodyFiles(UnsafeCell>>); +unsafe impl Sync for BodyFiles {} +unsafe impl Send for BodyFiles {} + +pub(crate) struct RequestTask { + pub(crate) conf: TaskConfig, + pub(crate) ctime: u64, + pub(crate) mime_type: Mutex, + pub(crate) progress: Mutex, + pub(crate) tries: AtomicU32, + pub(crate) status: Mutex, + pub(crate) retry: AtomicBool, + pub(crate) get_file_info: AtomicBool, + pub(crate) retry_for_request: AtomicBool, + #[allow(unused)] + pub(crate) retry_for_speed: AtomicBool, + pub(crate) code: Mutex>, + pub(crate) background_notify_time: AtomicU64, + pub(crate) file_total_size: AtomicI64, + pub(crate) resume: AtomicBool, + pub(crate) files: Files, + pub(crate) body_files: BodyFiles, + pub(crate) seek_flag: AtomicBool, + pub(crate) range_request: AtomicBool, + pub(crate) range_response: AtomicBool, + pub(crate) restored: AtomicBool, + pub(crate) skip_bytes: AtomicU64, + pub(crate) upload_counts: AtomicU32, + pub(crate) client: Option, + pub(crate) recording_rdb_num: Arc, + pub(crate) rate_limiting: AtomicBool, + pub(crate) app_state: Arc, + pub(crate) last_notify: AtomicU64, +} + +impl RequestTask { + pub(crate) fn constructor( + conf: TaskConfig, + files: Vec, + body_files: Vec, + recording_rdb_num: Arc, + rate_limiting: AtomicBool, + app_state: Arc, + ) -> Self { + let mut sizes = Vec::new(); + match conf.common_data.action { + Action::DownLoad => sizes.push(-1), + Action::UpLoad => { + for f in files.iter() { + let file_size = f.metadata().unwrap().len() as i64; + debug!("file size size is {}", file_size); + sizes.push(file_size); + } + } + _ => {} + } + let file_count = files.len(); + let action = conf.common_data.action; + + let mut task = RequestTask { + conf, + ctime: get_current_timestamp(), + files: Files(UnsafeCell::new( + files.into_iter().map(YlongFile::new).collect(), + )), + body_files: BodyFiles(UnsafeCell::new( + body_files + .into_iter() + .map(|f| Some(YlongFile::new(f))) + .collect(), + )), + mime_type: Mutex::new(String::new()), + progress: Mutex::new(Progress::new(sizes)), + tries: AtomicU32::new(0), + status: Mutex::new(TaskStatus::default()), + retry: AtomicBool::new(false), + get_file_info: AtomicBool::new(false), + retry_for_request: AtomicBool::new(false), + retry_for_speed: AtomicBool::new(false), + code: Mutex::new(vec![Reason::Default; file_count]), + background_notify_time: AtomicU64::new(get_current_timestamp()), + file_total_size: AtomicI64::new(-1), + resume: AtomicBool::new(false), + seek_flag: AtomicBool::new(false), + range_request: AtomicBool::new(false), + range_response: AtomicBool::new(false), + restored: AtomicBool::new(false), + skip_bytes: AtomicU64::new(0), + upload_counts: AtomicU32::new(0), + client: None, + recording_rdb_num, + rate_limiting, + app_state, + last_notify: AtomicU64::new(get_current_timestamp()), + }; + task.client = task.build_client(); + + if action == Action::UpLoad { + task.file_total_size + .store(task.get_upload_file_total_size() as i64, Ordering::SeqCst); + } + task + } + + pub(crate) fn restore_task( + conf: TaskConfig, + info: TaskInfo, + recording_rdb_num: Arc, + rate_limiting: AtomicBool, + app_state: Arc, + ) -> Self { + let progress_index = info.progress.common_data.index; + let uid = info.common_data.uid; + let action = conf.common_data.action; + let files = get_restore_files(&conf, uid); + let body_files = get_restore_body_files(&conf, uid); + let file_count = files.len(); + + let mut task = RequestTask { + conf, + ctime: info.common_data.ctime, + files: Files(UnsafeCell::new( + files.into_iter().map(YlongFile::new).collect(), + )), + body_files: BodyFiles(UnsafeCell::new( + body_files + .into_iter() + .map(|f| Some(YlongFile::new(f))) + .collect(), + )), + mime_type: Mutex::new(info.mime_type), + progress: Mutex::new(info.progress.clone()), + tries: AtomicU32::new(info.common_data.tries), + status: Mutex::new(TaskStatus { + waitting_network_time: None, + mtime: get_current_timestamp(), + state: State::from(info.progress.common_data.state), + reason: Reason::from(info.common_data.reason), + }), + retry: AtomicBool::new(info.common_data.retry), + get_file_info: AtomicBool::new(false), + retry_for_request: AtomicBool::new(false), + retry_for_speed: AtomicBool::new(false), + code: Mutex::new(vec![Reason::Default; file_count]), + background_notify_time: AtomicU64::new(get_current_timestamp()), + file_total_size: AtomicI64::new(-1), + resume: AtomicBool::new(false), + seek_flag: AtomicBool::new(false), + range_request: AtomicBool::new(false), + range_response: AtomicBool::new(false), + restored: AtomicBool::new(true), + skip_bytes: AtomicU64::new(0), + upload_counts: AtomicU32::new(progress_index as u32), + client: None, + recording_rdb_num, + rate_limiting, + app_state, + last_notify: AtomicU64::new(get_current_timestamp()), + }; + task.client = task.build_client(); + match action { + Action::UpLoad => task + .file_total_size + .store(task.get_upload_file_total_size() as i64, Ordering::SeqCst), + Action::DownLoad => task + .file_total_size + .store(info.progress.sizes[progress_index], Ordering::SeqCst), + _ => {} + } + task + } + + pub(crate) fn build_notify_data(&self) -> NotifyData { + let mut vec = Vec::new(); + let size = self.conf.file_specs.len(); + let guard = self.code.lock().unwrap(); + for i in 0..size { + vec.push(EachFileStatus { + path: self.conf.file_specs[i].path.clone(), + reason: guard[i], + message: guard[i].to_str().into(), + }); + } + NotifyData { + progress: self.progress.lock().unwrap().clone(), + action: self.conf.common_data.action, + version: self.conf.version, + each_file_status: vec, + task_id: self.conf.common_data.task_id, + _uid: self.conf.common_data.uid, + _bundle: self.conf.bundle.clone(), + } + } + + pub(crate) fn record_waitting_network_time(&self) { + let mut staus = self.status.lock().unwrap(); + staus.waitting_network_time = Some(get_current_timestamp()); + } + + pub(crate) fn check_net_work_status(&self) -> bool { + if !self.is_satisfied_configuration() { + if self.conf.version == Version::API10 + && self.conf.common_data.mode == Mode::BackGround + && self.conf.common_data.retry + { + self.set_status(State::Waiting, Reason::UnSupportedNetWorkType); + } else { + self.set_status(State::Failed, Reason::UnSupportedNetWorkType); + } + return false; + } + true + } + + pub(crate) fn net_work_online(&self) -> bool { + if unsafe { !IsOnline() } { + if self.conf.version == Version::API10 + && self.conf.common_data.mode == Mode::BackGround + && self.conf.common_data.retry + { + self.set_status(State::Waiting, Reason::NetWorkOffline); + } else { + let retry_times = 20; + for _ in 0..retry_times { + if unsafe { IsOnline() } { + return true; + } + sleep(Duration::from_millis(RETRY_INTERVAL)); + } + self.set_status(State::Failed, Reason::NetWorkOffline); + } + return false; + } + true + } + + fn build_client(&self) -> Option { + let mut client = Client::builder() + .connect_timeout(Timeout::from_secs(CONNECT_TIMEOUT)) + .request_timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK)) + .min_tls_version(TlsVersion::TLS_1_2); + + if self.conf.common_data.redirect { + client = client.redirect(Redirect::limited(usize::MAX)); + } else { + client = client.redirect(Redirect::none()); + } + + if self.conf.url.contains("https") { + let mut buf = Vec::new(); + let file = File::open("/etc/ssl/certs/cacert.pem"); + match file { + Ok(mut f) => { + f.read_to_end(&mut buf).unwrap(); + let cert = Certificate::from_pem(&buf).unwrap(); + client = client.add_root_certificate(cert); + } + Err(e) => { + error!("open cacert.pem failed, error is {:?}", e); + self.set_status(State::Failed, Reason::IoError); + return None; + } + } + } + let result = client.build(); + match result { + Ok(value) => Some(value), + Err(e) => { + error!("build client error is {:?}", e); + self.set_status(State::Failed, Reason::BuildClientFailed); + None + } + } + } + + pub(crate) fn build_request_builder(&self) -> RequestBuilder { + let url = self.conf.url.clone(); + let method = match self.conf.method.to_uppercase().as_str() { + "PUT" => "PUT", + "POST" => "POST", + "GET" => "GET", + _ => match self.conf.common_data.action { + Action::UpLoad => { + if self.conf.version == Version::API10 { + "PUT" + } else { + "POST" + } + } + Action::DownLoad => "GET", + _ => "", + }, + }; + let method = Method::try_from(method).unwrap(); + let mut request = RequestBuilder::new().method(method).url(url.as_str()); + for (key, value) in self.conf.headers.iter() { + request = request.header(key.as_str(), value.as_str()); + } + request + } + + async fn clear_downloaded_file(&self) -> bool { + let file = unsafe { &mut *self.files.0.get() }.get_mut(0).unwrap(); + let res = file.set_len(0).await; + match res { + Err(e) => { + error!("clear download file error: {:?}", e); + self.set_status(State::Failed, Reason::IoError); + false + } + _ => { + debug!("set len success"); + match file.seek(SeekFrom::Start(0)).await { + Err(e) => { + error!("seek err is {:?}", e); + self.set_status(State::Failed, Reason::IoError); + false + } + Ok(_) => { + debug!("seek success"); + let mut progress_guard = self.progress.lock().unwrap(); + progress_guard.common_data.total_processed = 0; + progress_guard.processed[0] = 0; + true + } + } + } + } + } + + pub(crate) async fn build_download_request(&self) -> Option> { + let mut request_builder = self.build_request_builder(); + let mut begins = self.conf.common_data.begins; + let ends = self.conf.common_data.ends; + self.range_response.store(false, Ordering::SeqCst); + if self.resume.load(Ordering::SeqCst) || begins > 0 || ends >= 0 { + self.range_request.store(true, Ordering::SeqCst); + self.skip_bytes.store(0, Ordering::SeqCst); + if self.resume.load(Ordering::SeqCst) { + let if_range = { + let progress_guard = self.progress.lock().unwrap(); + let etag = progress_guard.extras.get("etag"); + let last_modified = progress_guard.extras.get("last-modified"); + if let Some(etag) = etag { + request_builder = request_builder.header("If-Range", etag.as_str()); + true + } else if let Some(last_modified) = last_modified { + request_builder = + request_builder.header("If-Range", last_modified.as_str()); + true + } else { + false + } + }; + if !if_range { + // unable to verify file consistency, need download again + if begins == 0 && ends < 0 { + self.range_request.store(false, Ordering::SeqCst); + } + if !self.clear_downloaded_file().await { + return None; + } + } + } + begins += self.progress.lock().unwrap().processed[0] as u64; + if self.range_request.load(Ordering::SeqCst) { + let range = if ends < 0 { + format!("bytes={begins}-") + } else { + format!("bytes={begins}-{ends}") + }; + request_builder = request_builder.header("Range", range.as_str()); + } + } else { + self.range_request.store(false, Ordering::SeqCst); + } + let result = request_builder.body(self.conf.data.clone()); + match result { + Ok(value) => Some(value), + Err(e) => { + error!("build download request error is {:?}", e); + self.set_status(State::Failed, Reason::BuildRequestFailed); + None + } + } + } + + pub(crate) fn get_file_info(&self, response: &Response) -> bool { + if self.get_file_info.load(Ordering::SeqCst) { + return true; + } + self.get_file_info.store(true, Ordering::SeqCst); + let content_type = response.headers().get("content-type"); + if let Some(mime_type) = content_type { + if let Ok(value) = mime_type.to_str() { + *self.mime_type.lock().unwrap() = value; + } + } + + let content_length = response.headers().get("content-length"); + if let Some(len) = content_length { + let length = len.to_str(); + match length { + Ok(value) => { + let len = value.parse::(); + match len { + Ok(v) => { + let mut guard = self.progress.lock().unwrap(); + if !self.restored.load(Ordering::SeqCst) { + guard.sizes[0] = v; + } + self.file_total_size.store(v, Ordering::SeqCst); + debug!("the download task content-length is {}", v); + } + Err(e) => { + error!("convert string to i64 error: {:?}", e); + } + } + } + Err(e) => { + error!("convert header value to string error: {:?}", e); + } + } + } else { + error!("cannot get content-length of the task"); + if self.conf.common_data.precise { + self.set_status(State::Failed, Reason::GetFileSizeFailed); + return false; + } + } + true + } + + fn handle_body_transfer_error(&self) { + if unsafe { !IsOnline() } { + match self.conf.version { + Version::API9 => { + if self.conf.common_data.action == Action::DownLoad { + self.set_status(State::Waiting, Reason::NetWorkOffline); + } else { + self.set_status(State::Failed, Reason::NetWorkOffline); + } + } + Version::API10 => { + if self.conf.common_data.mode == Mode::FrontEnd || !self.conf.common_data.retry + { + self.set_status(State::Failed, Reason::NetWorkOffline); + } else { + self.set_status(State::Waiting, Reason::NetWorkOffline); + } + } + } + } else { + let index = self.progress.lock().unwrap().common_data.index; + self.set_code(index, Reason::OthersError); + } + } + + pub(crate) fn handle_download_error(&self, result: &Result<(), HttpClientError>) -> bool { + match result { + Ok(_) => true, + Err(err) => { + error!("download err is {:?}", err); + match err.error_kind() { + ErrorKind::Timeout => { + self.set_status(State::Failed, Reason::ContinuousTaskTimeOut); + } + // user triggered + ErrorKind::UserAborted => return true, + ErrorKind::BodyTransfer => self.handle_body_transfer_error(), + _ => { + self.set_status(State::Failed, Reason::OthersError); + } + } + false + } + } + } + + pub(crate) async fn handle_response_error( + &self, + response: &Result, + ) -> bool { + let index = self.progress.lock().unwrap().common_data.index; + match response { + Ok(r) => { + let http_response_code = r.status(); + info!("the http response code is {}", http_response_code); + if http_response_code.is_server_error() + || (http_response_code.as_u16() != 408 && http_response_code.is_client_error()) + || http_response_code.is_redirection() + { + self.set_code(index, Reason::ProtocolError); + return false; + } + if http_response_code.as_u16() == 408 { + if !self.retry_for_request.load(Ordering::SeqCst) { + self.retry_for_request.store(true, Ordering::SeqCst); + } else { + self.set_code(index, Reason::ProtocolError); + } + return false; + } + if self.range_request.load(Ordering::SeqCst) { + match http_response_code.as_u16() { + 206 => { + self.range_response.store(true, Ordering::SeqCst); + } + 200 => { + self.range_response.store(false, Ordering::SeqCst); + if self.resume.load(Ordering::SeqCst) { + if !self.clear_downloaded_file().await { + return false; + } + } else { + self.set_code(index, Reason::UnSupportRangeRequest); + return false; + } + } + _ => {} + } + } + true + } + Err(e) => { + error!("http client err is {:?}", e); + match e.error_kind() { + ErrorKind::UserAborted => self.set_code(index, Reason::UserOperation), + ErrorKind::Timeout => self.set_code(index, Reason::ContinuousTaskTimeOut), + ErrorKind::Request => self.set_code(index, Reason::RequestError), + ErrorKind::Redirect => self.set_code(index, Reason::RedirectError), + ErrorKind::Connect | ErrorKind::ConnectionUpgrade => { + self.set_code(index, Reason::ConnectError) + } + ErrorKind::BodyTransfer => self.handle_body_transfer_error(), + _ => self.set_code(index, Reason::OthersError), + } + false + } + } + } + + pub(crate) fn record_response_header(&self, response: &Result) { + if let Ok(r) = response { + let mut guard = self.progress.lock().unwrap(); + guard.extras.clear(); + for (k, v) in r.headers() { + if let Ok(value) = v.to_str() { + guard.extras.insert(k.to_string().to_lowercase(), value); + } + } + } + } + + pub(crate) async fn record_upload_response( + &self, + index: usize, + response: Result, + ) { + self.record_response_header(&response); + if let Ok(mut r) = response { + let mut yfile = match unsafe { &mut *self.body_files.0.get() }.get_mut(index) { + Some(yfile) => match yfile.take() { + Some(yf) => yf, + None => return, + }, + None => return, + }; + + loop { + let mut buf = [0u8; 1024]; + let size = r.body_mut().data(&mut buf).await; + let size = match size { + Ok(size) => size, + Err(_e) => break, + }; + + if size == 0 { + break; + } + let _ = yfile.write_all(&buf[..size]).await; + } + // Makes sure all the data has been written to the target file. + let _ = yfile.sync_all().await; + } + if self.conf.version == Version::API9 && self.conf.common_data.action == Action::UpLoad { + let notify_data = self.build_notify_data(); + #[cfg(feature = "oh")] + Notifier::service_front_notify("headerReceive".into(), notify_data, &self.app_state); + } + } + + fn set_code(&self, index: usize, code: Reason) { + if code == Reason::UploadFileError { + return; + } + let mut code_guard = self.code.lock().unwrap(); + if index < code_guard.len() && code_guard[index] == Reason::Default { + debug!("set code"); + code_guard[index] = code; + } + } + + pub(crate) fn reset_code(&self, index: usize) { + let file_counts = self.conf.file_specs.len(); + let mut code_guard = self.code.lock().unwrap(); + if index < file_counts { + debug!("reset code"); + code_guard[index] = Reason::Default; + } + } + + pub(crate) fn set_status(&self, state: State, reason: Reason) -> bool { + debug!("set status"); + { + let mut current_status = self.status.lock().unwrap(); + if state == current_status.state && reason == current_status.reason { + return true; + } + let mut progress_guard = self.progress.lock().unwrap(); + let index = progress_guard.common_data.index; + let current_state = current_status.state; + debug!( + "set state {:?}, reason {:?} current_state {:?}", + state, reason, current_state + ); + match state { + State::Paused | State::Stopped => { + if current_state != State::Running + && current_state != State::Retrying + && current_state != State::Waiting + { + return false; + } + self.set_code(index, reason); + } + State::Completed => { + if current_state != State::Running && current_state != State::Retrying { + return false; + } + } + State::Failed | State::Waiting => { + if current_state == State::Completed + || current_state == State::Removed + || current_state == State::Stopped + || current_state == State::Failed + { + return false; + } + self.set_code(index, reason); + if state == State::Failed { + let file_counts = self.conf.file_specs.len(); + for i in index..file_counts { + self.set_code(i, reason); + } + } + } + State::Removed => self.set_code(index, reason), + _ => {} + } + current_status.mtime = get_current_timestamp(); + progress_guard.common_data.state = state as u8; + current_status.state = state; + current_status.reason = reason; + info!("current state is {:?}, reason is {:?}", state, reason); + } + if state == State::Waiting { + self.record_waitting_network_time(); + } + self.record_task_info(); + self.state_change_notify(state); + true + } + + fn state_change_notify(&self, state: State) { + if state == State::Initialized + || (self.progress.lock().unwrap().common_data.total_processed == 0 + && (state == State::Running || state == State::Retrying)) + { + return; + } + + debug!("state change notification"); + let notify_data = self.build_notify_data(); + #[cfg(feature = "oh")] + Notifier::service_front_notify("progress".into(), notify_data.clone(), &self.app_state); + let bundle = CString::new(self.conf.bundle.as_str()).unwrap(); + match state { + State::Completed => { + unsafe { + PublishStateChangeEvents( + bundle.as_ptr(), + self.conf.bundle.len() as u32, + self.conf.common_data.task_id, + State::Completed as i32, + ); + } + #[cfg(feature = "oh")] + Notifier::service_front_notify("complete".into(), notify_data, &self.app_state) + } + State::Failed => { + unsafe { + PublishStateChangeEvents( + bundle.as_ptr(), + self.conf.bundle.len() as u32, + self.conf.common_data.task_id, + State::Failed as i32, + ); + } + #[cfg(feature = "oh")] + Notifier::service_front_notify("fail".into(), notify_data, &self.app_state) + } + State::Paused | State::Waiting => + { + #[cfg(feature = "oh")] + Notifier::service_front_notify("pause".into(), notify_data, &self.app_state) + } + _ => {} + } + self.background_notify(); + } + + fn record_task_info(&self) { + debug!( + "RequestTask record task info, task_id:{}", + self.conf.common_data.task_id + ); + + self.recording_rdb_num.fetch_add(1, Ordering::SeqCst); + + let has_record = unsafe { HasRequestTaskRecord(self.conf.common_data.task_id) }; + if !has_record { + let task_info = self.show(); + let info_set = task_info.build_info_set(); + let c_task_info = task_info.to_c_struct(&info_set); + let ret = unsafe { RecordRequestTaskInfo(&c_task_info) }; + info!("insert database ret is {}", ret); + } else { + let update_info = self.get_update_info(); + let sizes: String = format!("{:?}", update_info.progress.sizes); + let processed: String = format!("{:?}", update_info.progress.processed); + let extras = hashmap_to_string(&update_info.progress.extras); + let each_file_status = update_info + .each_file_status + .iter() + .map(|x| x.to_c_struct()) + .collect(); + let c_update_info = + update_info.to_c_struct(&sizes, &processed, &extras, &each_file_status); + let ret = + unsafe { UpdateRequestTaskInfo(self.conf.common_data.task_id, &c_update_info) }; + info!("update database ret is {}", ret); + } + + self.recording_rdb_num.fetch_sub(1, Ordering::SeqCst); + } + + fn get_each_file_status(&self) -> Vec { + let mut vec = Vec::new(); + let size = self.conf.file_specs.len(); + let guard = self.code.lock().unwrap(); + for i in 0..size { + vec.push(EachFileStatus { + path: self.conf.file_specs[i].path.clone(), + reason: guard[i], + message: guard[i].to_str().into(), + }); + } + vec + } + + fn get_update_info(&self) -> UpdateInfo { + let status = self.status.lock().unwrap(); + let progress = self.progress.lock().unwrap(); + UpdateInfo { + mtime: status.mtime, + reason: status.reason as u8, + tries: self.tries.load(Ordering::SeqCst), + mime_type: self.mime_type.lock().unwrap().clone(), + progress: progress.clone(), + each_file_status: self.get_each_file_status(), + } + } + + pub(crate) fn show(&self) -> TaskInfo { + let status = self.status.lock().unwrap(); + let progress = self.progress.lock().unwrap(); + TaskInfo { + bundle: self.conf.bundle.clone(), + url: self.conf.url.clone(), + data: self.conf.data.clone(), + token: self.conf.token.clone(), + form_items: self.conf.form_items.clone(), + file_specs: self.conf.file_specs.clone(), + title: self.conf.title.clone(), + description: self.conf.description.clone(), + mime_type: { + match self.conf.version { + Version::API10 => match self.conf.common_data.action { + Action::DownLoad => match self.conf.headers.get("Content-Type") { + None => "".into(), + Some(v) => v.clone(), + }, + Action::UpLoad => "multipart/form-data".into(), + _ => "".into(), + }, + Version::API9 => self.mime_type.lock().unwrap().clone(), + } + }, + progress: progress.clone(), + extras: progress.extras.clone(), + each_file_status: self.get_each_file_status(), + common_data: CommonTaskInfo { + task_id: self.conf.common_data.task_id, + uid: self.conf.common_data.uid, + action: self.conf.common_data.action as u8, + mode: self.conf.common_data.mode as u8, + ctime: self.ctime, + mtime: status.mtime, + reason: status.reason as u8, + gauge: self.conf.common_data.gauge, + retry: match self.conf.common_data.mode { + Mode::FrontEnd => false, + _ => self.conf.common_data.retry, + }, + tries: self.tries.load(Ordering::SeqCst), + version: self.conf.version as u8, + priority: self.conf.common_data.priority, + }, + } + } + + // only use for download task + pub(crate) fn query_mime_type(&self) -> String { + self.mime_type.lock().unwrap().clone() + } + + pub(crate) fn is_satisfied_configuration(&self) -> bool { + if self.conf.common_data.network == Network::Any { + return true; + } + unsafe { + let network_info = GetNetworkInfo(); + if !self.conf.common_data.roaming && (*network_info).is_roaming { + error!("not allow roaming"); + return false; + } + if !self.conf.common_data.metered && (*network_info).is_metered { + error!("not allow metered"); + return false; + } + if (*network_info).network_type != self.conf.common_data.network { + error!("dismatch network type"); + return false; + } + }; + true + } + + pub(crate) fn background_notify(&self) { + if self.conf.version == Version::API9 && !self.conf.common_data.background { + return; + } + if self.conf.version == Version::API10 && self.conf.common_data.mode == Mode::FrontEnd { + return; + } + let mut file_total_size = self.file_total_size.load(Ordering::SeqCst); + let total_processed = self.progress.lock().unwrap().common_data.total_processed as u64; + if file_total_size <= 0 || total_processed == 0 { + return; + } + if self.conf.common_data.action == Action::DownLoad { + if self.conf.common_data.ends < 0 { + file_total_size -= self.conf.common_data.begins as i64; + } else { + file_total_size = + self.conf.common_data.ends - self.conf.common_data.begins as i64 + 1; + } + } + self.background_notify_time + .store(get_current_timestamp(), Ordering::SeqCst); + let index = self.progress.lock().unwrap().common_data.index; + if index >= self.conf.file_specs.len() { + return; + } + let file_path = self.conf.file_specs[index].path.as_ptr() as *const c_char; + let file_path_len = self.conf.file_specs[index].path.as_bytes().len() as i32; + let percent = total_processed * 100 / (file_total_size as u64); + debug!("background notify"); + let task_msg = RequestTaskMsg { + task_id: self.conf.common_data.task_id, + uid: self.conf.common_data.uid as i32, + action: self.conf.common_data.action as u8, + }; + unsafe { + RequestBackgroundNotify(task_msg, file_path, file_path_len, percent as u32); + }; + } + + pub(crate) fn get_upload_info(&self, index: usize) -> (bool, u64) { + let guard = self.progress.lock().unwrap(); + let file_size = guard.sizes[index]; + let mut is_partial_upload = false; + let mut upload_file_length: u64 = file_size as u64 - guard.processed[index] as u64; + if file_size == 0 { + return (is_partial_upload, upload_file_length); + } + if index as u32 != self.conf.common_data.index { + return (is_partial_upload, upload_file_length); + } + let begins = self.conf.common_data.begins; + let mut ends = self.conf.common_data.ends; + if ends < 0 || ends >= file_size { + ends = file_size - 1; + } + if begins >= file_size as u64 || begins > ends as u64 { + return (is_partial_upload, upload_file_length); + } + is_partial_upload = true; + upload_file_length = ends as u64 - begins + 1 - guard.processed[index] as u64; + (is_partial_upload, upload_file_length) + } + + fn get_upload_file_total_size(&self) -> u64 { + let mut file_total_size = 0; + for i in 0..self.conf.file_specs.len() { + let (_, upload_size) = self.get_upload_info(i); + file_total_size += upload_size; + } + file_total_size + } +} + +pub(crate) async fn run(task: Arc) { + info!("run the task which id is {}", task.conf.common_data.task_id); + if !task.net_work_online() || !task.check_net_work_status() { + return; + } + let action = task.conf.common_data.action; + match action { + Action::DownLoad => loop { + task.reset_code(0); + + download(task.clone()).await; + + let state = task.status.lock().unwrap().state; + if state != State::Running && state != State::Retrying { + break; + } + let code = task.code.lock().unwrap()[0]; + if code != Reason::Default { + task.set_status(State::Failed, code); + break; + } + }, + Action::UpLoad => { + let state = task.status.lock().unwrap().state; + if state == State::Retrying { + let index = { + let mut progress_guard = task.progress.lock().unwrap(); + let index = progress_guard.common_data.index; + progress_guard.common_data.total_processed -= progress_guard.processed[index]; + progress_guard.processed[index] = 0; + index + }; + let file = unsafe { &mut *task.files.0.get() }.get_mut(index).unwrap(); + let mut begins = task.conf.common_data.begins; + let (is_partial_upload, _) = task.get_upload_info(index); + if !is_partial_upload { + begins = 0; + } + if let Err(e) = file.seek(SeekFrom::Start(begins)).await { + task.set_code(index, Reason::IoError); + error!("seek err is {:?}", e); + } + } + upload(task.clone()).await; + } + _ => {} + } + info!("run end"); +} + +fn get_restore_files(conf: &TaskConfig, uid: u64) -> Vec { + let mut files: Vec = Vec::new(); + + #[cfg(feature = "oh")] + for fs in &conf.file_specs { + if conf.common_data.action == Action::UpLoad { + match open_file_readonly(uid, &conf.bundle, &fs.path) { + Ok(file) => files.push(file), + Err(e) => { + error!("open file RO failed, err is {:?}", e); + } + } + } else { + match open_file_readwrite(uid, &conf.bundle, &fs.path) { + Ok(file) => files.push(file), + Err(e) => { + error!("open file RW failed, err is {:?}", e); + } + } + } + } + files +} + +fn get_restore_body_files(conf: &TaskConfig, uid: u64) -> Vec { + let mut body_files: Vec = Vec::new(); + + #[cfg(feature = "oh")] + for name in &conf.body_file_names { + match open_file_readwrite(uid, &conf.bundle, name) { + Ok(body_file) => body_files.push(body_file), + Err(e) => { + error!("open body_file failed, err is {:?}", e); + } + } + } + body_files +} diff --git a/services/service/request/src/task/tick.rs b/services/service/request/src/task/tick.rs new file mode 100644 index 00000000..73e688e6 --- /dev/null +++ b/services/service/request/src/task/tick.rs @@ -0,0 +1,84 @@ +// Copyright (C) 2023 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. + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +use std::task::{Context, Waker}; + +const WAITING_TICK: usize = 10; + +pub(crate) struct Clock { + registers: Mutex>, + tick: AtomicUsize, +} + +impl Clock { + pub(crate) fn tick(&mut self) { + let tick = self.tick.fetch_add(1, Ordering::Relaxed); + + if tick >= WAITING_TICK { + self.tick.store(0, Ordering::Relaxed); + let mut registers = Clock::get_instance().registers.lock().unwrap(); + while let Some(waker) = registers.pop() { + waker.wake() + } + } + } + + pub(crate) fn get_instance() -> &'static mut Self { + static mut CLOCK: Clock = Clock { + registers: Mutex::new(Vec::new()), + tick: AtomicUsize::new(0), + }; + unsafe { &mut CLOCK } + } + pub(crate) fn register(&mut self, cx: &mut Context<'_>) { + self.registers.lock().unwrap().push(cx.waker().clone()); + } +} + +#[cfg(test)] +mod test { + + use std::future::Future; + use std::task::Poll; + + use super::*; + struct TestFuture(Option<()>); + impl Future for TestFuture { + type Output = (); + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let me = self.get_mut(); + if me.0.take().is_none() { + Clock::get_instance().register(cx); + me.0 = Some(()); + println!("hello"); + Poll::Pending + } else { + println!("world"); + Poll::Ready(()) + } + } + } + + #[test] + fn tick_tesk() { + let join_handle = ylong_runtime::spawn(TestFuture(None)); + assert!(!join_handle.is_finished()); + let _ = ylong_runtime::spawn(async { + loop { + Clock::get_instance().tick(); + } + }); + } +} diff --git a/services/service/request/src/task/upload.rs b/services/service/request/src/task/upload.rs new file mode 100644 index 00000000..186a0b5c --- /dev/null +++ b/services/service/request/src/task/upload.rs @@ -0,0 +1,349 @@ +// Copyright (C) 2023 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. + +use std::io::SeekFrom; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use ylong_http_client::async_impl::{MultiPart, Part, UploadOperator, Uploader}; +use ylong_http_client::{Body, HttpClientError, Request}; +use ylong_runtime::io::{AsyncRead, AsyncSeek, ReadBuf}; + +use super::operator::TaskOperator; +use super::reason::Reason; +use super::tick::Clock; +use crate::task::info::State; +use crate::task::RequestTask; + +cfg_oh! { + use crate::trace::Trace; +} + +struct TaskReader { + task: Arc, + check_point: Option<()>, +} + +use std::sync::atomic::Ordering; + +impl TaskReader { + pub(crate) fn new(task: Arc) -> Self { + Self { + task, + check_point: None, + } + } +} + +impl AsyncRead for TaskReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + if self.task.rate_limiting.load(Ordering::SeqCst) { + if self.check_point.take().is_none() { + Clock::get_instance().register(cx); + self.as_mut().check_point = Some(()); + return Poll::Pending; + } + } else { + Clock::get_instance().tick(); + } + + let index = self.task.progress.lock().unwrap().common_data.index; + let file = unsafe { &mut *self.task.files.0.get() } + .get_mut(index) + .unwrap(); + let (is_partial_upload, total_upload_bytes) = self.task.get_upload_info(index); + let mut progress_guard = self.task.progress.lock().unwrap(); + if !is_partial_upload { + let filled_len = buf.filled().len(); + match Pin::new(file).poll_read(cx, buf) { + Poll::Ready(Ok(_)) => { + let current_filled_len = buf.filled().len(); + let upload_size = current_filled_len - filled_len; + progress_guard.processed[index] += upload_size; + progress_guard.common_data.total_processed += upload_size; + Poll::Ready(Ok(())) + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + } + } else { + let begins = self.task.conf.common_data.begins; + if !self.task.seek_flag.load(Ordering::SeqCst) { + match Pin::new(file).poll_seek(cx, SeekFrom::Start(begins)) { + Poll::Ready(Err(e)) => { + error!("seek err is {:?}", e); + return Poll::Ready(Err(e)); + } + _ => self.task.seek_flag.store(true, Ordering::SeqCst), + } + } + let buf_filled_len = buf.filled().len(); + let mut read_buf = buf.take(total_upload_bytes as usize); + let filled_len = read_buf.filled().len(); + let file = unsafe { &mut *self.task.files.0.get() } + .get_mut(index) + .unwrap(); + match Pin::new(file).poll_read(cx, &mut read_buf) { + Poll::Ready(Ok(_)) => { + let current_filled_len = read_buf.filled().len(); + let upload_size = current_filled_len - filled_len; + // need update buf.filled and buf.initialized + buf.assume_init(upload_size); + buf.set_filled(buf_filled_len + upload_size); + progress_guard.processed[index] += upload_size; + progress_guard.common_data.total_processed += upload_size; + Poll::Ready(Ok(())) + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + } + } + } +} + +impl UploadOperator for TaskOperator { + fn poll_progress( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + _uploaded: u64, + _total: Option, + ) -> Poll> { + self.poll_progress_common(cx) + } +} +fn build_stream_request( + task: Arc, + index: usize, +) -> Option>> { + info!("build stream request"); + let task_reader = TaskReader::new(task.clone()); + let task_operator = TaskOperator::new(task.clone()); + let mut request_builder = task.build_request_builder(); + if task.conf.headers.get("Content-Type").is_none() { + request_builder = request_builder.header("Content-Type", "application/octet-stream"); + } + let (_, upload_length) = task.get_upload_info(index); + info!("upload length is {}", upload_length); + request_builder = request_builder.header("Content-Length", upload_length.to_string().as_str()); + let uploader = Uploader::builder() + .reader(task_reader) + .operator(task_operator) + .total_bytes(Some(upload_length)) + .build(); + let request = request_builder.body(uploader); + build_request_common(&task, index, request) +} + +fn build_multipart_request( + task: Arc, + index: usize, +) -> Option>> { + info!("build multipart request"); + let task_reader = TaskReader::new(task.clone()); + let task_operator = TaskOperator::new(task.clone()); + let mut multi_part = MultiPart::new(); + for item in task.conf.form_items.iter() { + let part = Part::new() + .name(item.name.as_str()) + .body(item.value.as_str()); + multi_part = multi_part.part(part); + } + let (_, upload_length) = task.get_upload_info(index); + info!("upload length is {}", upload_length); + let part = Part::new() + .name(task.conf.file_specs[index].name.as_str()) + .file_name(task.conf.file_specs[index].file_name.as_str()) + .mime(task.conf.file_specs[index].mime_type.as_str()) + .length(Some(upload_length)) + .stream(task_reader); + + multi_part = multi_part.part(part); + let uploader = Uploader::builder() + .multipart(multi_part) + .operator(task_operator) + .build(); + + let request_builder = task.build_request_builder(); + let request: Result>, HttpClientError> = + request_builder.multipart(uploader); + build_request_common(&task, index, request) +} + +fn build_request_common( + task: &Arc, + index: usize, + request: Result, HttpClientError>, +) -> Option> { + match request { + Ok(value) => { + info!("build upload request success"); + Some(value) + } + Err(e) => { + error!("build upload request error is {:?}", e); + { + let mut guard = task.code.lock().unwrap(); + for i in index..guard.len() { + guard[i] = Reason::BuildRequestFailed; + } + } + task.set_status(State::Failed, Reason::BuildRequestFailed); + None + } + } +} + +pub(crate) async fn upload(task: Arc) { + info!("begin upload"); + + let url = task.conf.url.as_str(); + let num = task.conf.file_specs.len(); + // Ensures `_trace` can only be freed when this function exits. + #[cfg(feature = "oh")] + Trace::start(&format!("exec upload task url: {url} file num: {num}")); + + let size = task.conf.file_specs.len(); + if task.client.is_none() { + return; + } + let index = task.progress.lock().unwrap().common_data.index; + info!("index is {}", index); + for i in index..size { + task.progress.lock().unwrap().common_data.index = i; + let result: bool; + match task.conf.headers.get("Content-Type") { + None => { + if task.conf.method.to_uppercase().eq("POST") { + result = upload_one_file(task.clone(), i, build_multipart_request).await; + } else { + result = upload_one_file(task.clone(), i, build_stream_request).await; + } + } + Some(v) => { + if v == "multipart/form-data" { + result = upload_one_file(task.clone(), i, build_multipart_request).await; + } else { + result = upload_one_file(task.clone(), i, build_stream_request).await; + } + } + } + if result { + info!("upload one file success, which index is {}", i); + task.upload_counts.fetch_add(1, Ordering::SeqCst); + } + let state = task.status.lock().unwrap().state; + if state != State::Running && state != State::Retrying { + return; + } + } + + let uploaded = task.upload_counts.load(Ordering::SeqCst); + if uploaded == size as u32 { + task.set_status(State::Completed, Reason::Default); + } else { + task.set_status(State::Failed, Reason::UploadFileError); + + #[cfg(feature = "oh")] + use hisysevent::{build_number_param, build_str_param}; + + #[cfg(feature = "oh")] + use crate::sys_event::SysEvent; + // Records sys event. + #[cfg(feature = "oh")] + SysEvent::task_fault() + .param(build_str_param!(crate::sys_event::TASKS_TYPE, "UPLOAD")) + .param(build_number_param!(crate::sys_event::TOTAL_FILE_NUM, size)) + .param(build_number_param!( + crate::sys_event::FAIL_FILE_NUM, + size as u32 - uploaded + )) + .param(build_number_param!( + crate::sys_event::SUCCESS_FILE_NUM, + uploaded + )) + .param(build_number_param!( + crate::sys_event::ERROR_INFO, + Reason::UploadFileError as i32 + )) + .write(); + } + + info!("upload end"); + #[cfg(feature = "oh")] + Trace::finish() +} + +async fn upload_one_file( + task: Arc, + index: usize, + build_upload_request: F, +) -> bool +where + F: Fn(Arc, usize) -> Option>, + T: Body, +{ + info!("begin upload one file"); + + let (_, size) = task.get_upload_info(index); + let name = task.conf.file_specs[index].file_name.as_str(); + // Ensures `_trace` can only be freed when this function exits. + #[cfg(feature = "oh")] + Trace::start(&format!( + "upload file name:{name} index:{index} size:{size}" + )); + + loop { + task.reset_code(index); + let request = build_upload_request(task.clone(), index); + if request.is_none() { + #[cfg(feature = "oh")] + Trace::finish(); + return false; + } + let response = task + .client + .as_ref() + .unwrap() + .request(request.unwrap()) + .await; + if task.handle_response_error(&response).await { + task.code.lock().unwrap()[index] = Reason::Default; + task.record_upload_response(index, response).await; + #[cfg(feature = "oh")] + Trace::finish(); + return true; + } + task.record_upload_response(index, response).await; + let code = task.code.lock().unwrap()[index]; + if code != Reason::Default { + error!( + "upload {} file fail, which reason is {}", + index, code as u32 + ); + #[cfg(feature = "oh")] + Trace::finish(); + return false; + } + let state = task.status.lock().unwrap().state; + if state != State::Running && state != State::Retrying { + #[cfg(feature = "oh")] + Trace::finish(); + return false; + } + } +} diff --git a/services/service/request/src/tests/download.rs b/services/service/request/src/tests/download.rs new file mode 100644 index 00000000..cfca7b02 --- /dev/null +++ b/services/service/request/src/tests/download.rs @@ -0,0 +1,24 @@ +// Copyright (C) 2023 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. + +use super::set_up; + +#[test] +fn ut_download() { + set_up(); +} + +#[test] +fn ut_download_and_pause() { + set_up(); +} diff --git a/services/service/request/src/tests/mod.rs b/services/service/request/src/tests/mod.rs new file mode 100644 index 00000000..3e03295e --- /dev/null +++ b/services/service/request/src/tests/mod.rs @@ -0,0 +1,25 @@ +// Copyright (C) 2023 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. + +mod download; + +fn set_up() { + #[cfg(not(feature = "oh"))] + use std::sync::Once; + #[cfg(not(feature = "oh"))] + static INIT: Once = Once::new(); + #[cfg(not(feature = "oh"))] + INIT.call_once(|| { + env_logger::init(); + }) +} diff --git a/services/service/request/src/trace.rs b/services/service/request/src/trace.rs new file mode 100644 index 00000000..467f8906 --- /dev/null +++ b/services/service/request/src/trace.rs @@ -0,0 +1,33 @@ +// Copyright (C) 2023 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. + +use hitrace_meter_rust::{finish_trace, start_trace}; + +/// Hitrace adapter which provides timing capability. +/// +/// The timing will end automatically when the structure drops. Users should +/// take care that the lifetime of this structure. +pub(crate) struct Trace; + +impl Trace { + // Copies from `Hitrace`. + const HITRACE_TAG_MISC: u64 = 1u64 << 41; + + /// Starts tracing. + pub(crate) fn start(value: &str) { + start_trace(Self::HITRACE_TAG_MISC, value); + } + pub(crate) fn finish() { + finish_trace(Self::HITRACE_TAG_MISC); + } +} diff --git a/services/service/request/src/utils/c_wrapper.rs b/services/service/request/src/utils/c_wrapper.rs new file mode 100644 index 00000000..ef1b08ae --- /dev/null +++ b/services/service/request/src/utils/c_wrapper.rs @@ -0,0 +1,130 @@ +// Copyright (C) 2023 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. + +use std::ffi::c_char; +use std::slice; + +use crate::utils::filter::{CommonFilter, Filter}; +use crate::utils::form_item::{FileSpec, FormItem}; + +#[repr(C)] +pub(crate) struct CStringWrapper { + c_str: *const c_char, + len: u32, +} + +impl From<&str> for CStringWrapper { + fn from(value: &str) -> Self { + let c_str = value.as_ptr() as *const c_char; + let len = value.len() as u32; + CStringWrapper { c_str, len } + } +} + +impl From<&String> for CStringWrapper { + fn from(value: &String) -> Self { + Self::from(value.as_str()) + } +} + +impl ToString for CStringWrapper { + fn to_string(&self) -> String { + if self.c_str.is_null() || self.len == 0 { + unsafe { DeleteChar(self.c_str) }; + return String::new(); + } + let bytes = unsafe { slice::from_raw_parts(self.c_str as *const u8, self.len as usize) }; + let str = unsafe { String::from_utf8_unchecked(bytes.to_vec()) }; + unsafe { DeleteChar(self.c_str) }; + str + } +} + +#[repr(C)] +pub(crate) struct CFilter { + bundle: CStringWrapper, + common_data: CommonFilter, +} + +impl Filter { + pub(crate) fn to_c_struct(&self) -> CFilter { + CFilter { + bundle: CStringWrapper::from(&self.bundle), + common_data: self.common_data, + } + } +} + +#[repr(C)] +pub(crate) struct CVectorWrapper { + pub(crate) ptr: *const u32, + pub(crate) len: u64, +} + +#[repr(C)] +pub(crate) struct CFileSpec { + pub(crate) name: CStringWrapper, + pub(crate) path: CStringWrapper, + pub(crate) file_name: CStringWrapper, + pub(crate) mime_type: CStringWrapper, +} + +impl FileSpec { + pub(crate) fn to_c_struct(&self) -> CFileSpec { + CFileSpec { + name: CStringWrapper::from(&self.name), + path: CStringWrapper::from(&self.path), + file_name: CStringWrapper::from(&self.file_name), + mime_type: CStringWrapper::from(&self.mime_type), + } + } + + pub(crate) fn from_c_struct(c_struct: &CFileSpec) -> Self { + FileSpec { + name: c_struct.name.to_string(), + path: c_struct.path.to_string(), + file_name: c_struct.file_name.to_string(), + mime_type: c_struct.mime_type.to_string(), + } + } +} + +#[repr(C)] +pub(crate) struct CFormItem { + pub(crate) name: CStringWrapper, + pub(crate) value: CStringWrapper, +} + +impl FormItem { + pub(crate) fn to_c_struct(&self) -> CFormItem { + CFormItem { + name: CStringWrapper::from(&self.name), + value: CStringWrapper::from(&self.value), + } + } + + pub(crate) fn from_c_struct(c_struct: &CFormItem) -> Self { + FormItem { + name: c_struct.name.to_string(), + value: c_struct.value.to_string(), + } + } +} + +extern "C" { + pub(crate) fn DeleteChar(ptr: *const c_char); + pub(crate) fn DeleteCFormItem(ptr: *const CFormItem); + pub(crate) fn DeleteCFileSpec(ptr: *const CFileSpec); + pub(crate) fn DeleteCVectorWrapper(ptr: *const u32); + pub(crate) fn DeleteCStringPtr(ptr: *const CStringWrapper); +} diff --git a/services/service/request/src/utils/filter.rs b/services/service/request/src/utils/filter.rs new file mode 100644 index 00000000..30d1a2fa --- /dev/null +++ b/services/service/request/src/utils/filter.rs @@ -0,0 +1,28 @@ +// Copyright (C) 2023 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. + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub(crate) struct CommonFilter { + pub(crate) before: i64, + pub(crate) after: i64, + pub(crate) state: u8, + pub(crate) action: u8, + pub(crate) mode: u8, +} + +#[derive(Debug)] +pub(crate) struct Filter { + pub(crate) bundle: String, + pub(crate) common_data: CommonFilter, +} diff --git a/services/service/request/src/utils/form_item.rs b/services/service/request/src/utils/form_item.rs new file mode 100644 index 00000000..7d164bb9 --- /dev/null +++ b/services/service/request/src/utils/form_item.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2023 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. + +#[derive(Clone, Debug)] +pub(crate) struct FileSpec { + pub(crate) name: String, + pub(crate) path: String, + pub(crate) file_name: String, + pub(crate) mime_type: String, +} + +#[derive(Clone, Debug)] +pub(crate) struct FormItem { + pub(crate) name: String, + pub(crate) value: String, +} diff --git a/services/service/request/src/utils/mod.rs b/services/service/request/src/utils/mod.rs new file mode 100644 index 00000000..037a63b6 --- /dev/null +++ b/services/service/request/src/utils/mod.rs @@ -0,0 +1,95 @@ +// Copyright (C) 2023 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. + +pub(crate) mod c_wrapper; +pub(crate) mod filter; +pub(crate) mod form_item; + +use std::collections::HashMap; +use std::io::Write; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use ylong_runtime::sync::oneshot::Receiver; + +pub(crate) struct Recv { + rx: Receiver, +} + +impl Recv { + pub(crate) fn new(rx: Receiver) -> Self { + Self { rx } + } + + pub(crate) fn get(self) -> Option { + // Here `self.rx` can never be hung up. + ylong_runtime::block_on(self.rx).ok() + } +} + +pub(crate) fn build_vec(ptr: *const A, len: usize, func: C) -> Vec +where + C: Fn(&A) -> B, +{ + if ptr.is_null() || len == 0 { + return Vec::::new(); + } + let slice = unsafe { std::slice::from_raw_parts(ptr, len) }; + slice.iter().map(func).collect() +} + +pub(crate) fn get_current_timestamp() -> u64 { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(n) => n.as_millis() as u64, + Err(_) => panic!("SystemTime before UNIX EPOCH!"), + } +} + +pub(crate) fn generate_task_id() -> u32 { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(time) => time.subsec_nanos(), + Err(e) => { + static ID: AtomicU32 = AtomicU32::new(0); + error!("Generate task id from system time failed {:?}", e); + ID.fetch_add(1, Ordering::Relaxed) + } + } +} + +pub(crate) fn hashmap_to_string(map: &HashMap) -> String { + let mut res = Vec::new(); + for (n, (k, v)) in map.iter().enumerate() { + if n != 0 { + let _ = write!(res, "\r\n"); + } + let _ = write!(res, "{k}\t{v}"); + } + unsafe { String::from_utf8_unchecked(res) } +} + +pub(crate) fn string_to_hashmap(str: &mut String) -> HashMap { + let mut map = HashMap::::new(); + if str.is_empty() { + return map; + } + for item in str.split("\r\n") { + let (k, v) = item.split_once('\t').unwrap(); + map.insert(k.into(), v.into()); + } + map +} + +pub(crate) fn split_string(str: &mut str) -> std::str::Split<'_, &str> { + let pat: &[_] = &['[', ']']; + str.trim_matches(pat).split(", ") +} diff --git a/services/service/rust/src/c_string_wrapper.rs b/services/service/rust/src/c_string_wrapper.rs deleted file mode 100644 index 02f01630..00000000 --- a/services/service/rust/src/c_string_wrapper.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use std::{slice, ffi::c_char}; -use super::request_binding::*; -#[repr(C)] -pub struct CStringWrapper { - c_str: *const c_char, - len: u32, -} - -impl CStringWrapper { - pub fn from(s: &str) -> Self { - let c_str = s.as_ptr() as *const c_char; - let len = s.len() as u32; - CStringWrapper { c_str, len } - } - - pub fn to_string(&self) -> String { - if self.c_str.is_null() || self.len == 0 { - unsafe { DeleteChar(self.c_str) }; - return String::new(); - } - let bytes = unsafe { slice::from_raw_parts(self.c_str as *const u8, self.len as usize) }; - let str = unsafe { String::from_utf8_unchecked(bytes.to_vec()) }; - unsafe { DeleteChar(self.c_str) }; - str - } -} diff --git a/services/service/rust/src/enumration.rs b/services/service/rust/src/enumration.rs deleted file mode 100644 index 4be2aacb..00000000 --- a/services/service/rust/src/enumration.rs +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub enum Action { - DOWNLOAD = 0, - UPLOAD, - ANY, -} - -impl From for Action { - fn from(value: u8) -> Self { - match value { - 0 => Action::DOWNLOAD, - 1 => Action::UPLOAD, - _ => Action::ANY, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub enum Mode { - BACKGROUND = 0, - FRONTEND, - ANY, -} - -impl From for Mode { - fn from(value: u8) -> Self { - match value { - 0 => Mode::BACKGROUND, - 1 => Mode::FRONTEND, - _ => Mode::ANY, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8, C)] -pub enum Network { - ANY = 0, - WIFI, - CELLULAR, -} - -#[derive(Debug)] -#[repr(C)] -pub struct NetworkInfo { - pub networkType: Network, - pub isMetered: bool, - pub isRoaming: bool, -} - -impl From for Network { - fn from(value: u8) -> Self { - match value { - 0 => Network::ANY, - 2 => Network::CELLULAR, - _ => Network::WIFI, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub enum State { - INITIALIZED = 0x00, - WAITING = 0x10, - RUNNING = 0x20, - RETRYING = 0x21, - PAUSED = 0x30, - STOPPED = 0x31, - COMPLETED = 0x40, - FAILED = 0x41, - REMOVED = 0x50, - CREATED = 0x60, - ANY = 0x61, -} - -impl From for State { - fn from(value: u8) -> Self { - match value { - 0 => State::INITIALIZED, - 16 => State::WAITING, - 32 => State::RUNNING, - 33 => State::RETRYING, - 48 => State::PAUSED, - 49 => State::STOPPED, - 64 => State::COMPLETED, - 65 => State::FAILED, - 80 => State::REMOVED, - 96 => State::CREATED, - _ => State::ANY, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(i32)] -pub enum ApplicationState { - AppStateBegin = 0, - AppStateReady, - AppStateForeground, - AppStateFocus, - AppStateBackground, - AppStateTerminated, - AppStateEnd, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(i32)] -pub enum ErrorCode { - ErrOk = 0, - UnloadingSA = 1, - Ipc_size_too_large = 2, - MimeType_not_found = 3, - Task_index_too_large = 4, - Permission = 201, - SystemApi = 202, - Parameter_check = 401, - FileOperationErr = 13400001, - ServiceAbilityErr = 13400003, - Other = 13499999, - TaskEnqueueErr = 21900004, - TaskModeErr, - TaskNotFound, - TaskStateErr, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub enum Version { - API9 = 1, - API10, -} - -impl From for Version { - fn from(value: u8) -> Self { - match value { - 2 => Version::API10, - _ => Version::API9, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub enum Reason { - Default = 0, - TaskSurvivalOneMonth, - WaittingNetWorkOneday, - StoppedByNewFrontTask, - RunningTaskMeetLimits, - UserOperation, - AppBackgroundOrTerminate, - NetWorkOffline, - UnSupportedNetWorkType, - BuildClientFailed, - BuildRequestFailed, - GetFileSizeFailed, - ContinuousTaskTimeOut, - ConnectError, - RequestError, - UploadFileError, - RedirectError, - ProtocolError, - IoError, - UnSupportRangeRequest, - OthersError, -} - -impl From for Reason { - fn from(value: u8) -> Self { - match value { - 0 => Reason::Default, - 1 => Reason::TaskSurvivalOneMonth, - 2 => Reason::WaittingNetWorkOneday, - 3 => Reason::StoppedByNewFrontTask, - 4 => Reason::RunningTaskMeetLimits, - 5 => Reason::UserOperation, - 6 => Reason::AppBackgroundOrTerminate, - 7 => Reason::NetWorkOffline, - 8 => Reason::UnSupportedNetWorkType, - 9 => Reason::BuildClientFailed, - 10 => Reason::BuildRequestFailed, - 11 => Reason::GetFileSizeFailed, - 12 => Reason::ContinuousTaskTimeOut, - 13 => Reason::ConnectError, - 14 => Reason::RequestError, - 15 => Reason::UploadFileError, - 16 => Reason::RedirectError, - 17 => Reason::ProtocolError, - 18 => Reason::IoError, - 19 => Reason::UnSupportRangeRequest, - _ => Reason::OthersError, - } - } -} - -impl Reason { - pub fn to_str(&self) -> &'static str { - match self { - Reason::Default => "".into(), - Reason::TaskSurvivalOneMonth => "The task has not been completed for a month yet", - Reason::WaittingNetWorkOneday => "The task waiting for network recovery has not been completed for a day yet", - Reason::StoppedByNewFrontTask => "Stopped by a new front task", - Reason::RunningTaskMeetLimits => "Too many task in running state", - Reason::UserOperation => "User operation", - Reason::AppBackgroundOrTerminate => "The app is background or terminate", - Reason::NetWorkOffline => "NetWork is offline", - Reason::UnSupportedNetWorkType => "NetWork type not meet the task config", - Reason::BuildClientFailed => "Build client error", - Reason::BuildRequestFailed => "Build request error", - Reason::GetFileSizeFailed => "Failed because cannot get the file size from the server and the precise is setted true by user", - Reason::ContinuousTaskTimeOut => "Continuous processing task time out", - Reason::ConnectError => "Connect error", - Reason::RequestError => "Request error", - Reason::UploadFileError => "There are some files upload failed", - Reason::RedirectError => "Redirect error", - Reason::ProtocolError => "Http protocol error", - Reason::IoError => "Io Error", - Reason::UnSupportRangeRequest => "The server is not support range request", - Reason::OthersError => "Some other error occured", - } - } -} - -#[derive(PartialEq)] -#[repr(u8, C)] -pub enum QueryPermission { - NoPermisson = 0, - QueryDownLoad, - QueryUpload, - QueryAll, -} \ No newline at end of file diff --git a/services/service/rust/src/filter.rs b/services/service/rust/src/filter.rs deleted file mode 100644 index 8289a2cd..00000000 --- a/services/service/rust/src/filter.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use super::c_string_wrapper::*; -pub struct Filter { - pub bundle: String, - pub common_data: CommonFilter, -} - -#[derive(Clone, Copy)] -#[repr(C)] -pub struct CommonFilter { - pub before: i64, - pub after: i64, - pub state: u8, - pub action: u8, - pub mode: u8 -} - - -#[repr(C)] -pub struct CFilter { - bundle: CStringWrapper, - common_data: CommonFilter, -} - -impl Filter { - pub fn to_c_struct(&self) -> CFilter { - CFilter { - bundle: CStringWrapper::from(&self.bundle), - common_data: self.common_data - } - } -} - -#[repr(C)] -pub struct CVectorWrapper { - pub ptr: *const u32, - pub len: u64, -} \ No newline at end of file diff --git a/services/service/rust/src/form_item.rs b/services/service/rust/src/form_item.rs deleted file mode 100644 index 1d5efab3..00000000 --- a/services/service/rust/src/form_item.rs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use super::c_string_wrapper::*; -#[derive(Clone, Debug)] -pub struct FileSpec { - pub name: String, - pub path: String, - pub file_name: String, - pub mime_type: String, -} - -#[repr(C)] -pub struct CFileSpec { - pub name: CStringWrapper, - pub path: CStringWrapper, - pub file_name: CStringWrapper, - pub mime_type: CStringWrapper, -} - -impl FileSpec { - pub fn to_c_struct(&self) -> CFileSpec { - CFileSpec { - name: CStringWrapper::from(&self.name), - path: CStringWrapper::from(&self.path), - file_name: CStringWrapper::from(&self.file_name), - mime_type: CStringWrapper::from(&self.mime_type), - } - } - - pub fn from_c_struct(c_struct: &CFileSpec) -> Self { - FileSpec { - name: c_struct.name.to_string(), - path: c_struct.path.to_string(), - file_name: c_struct.file_name.to_string(), - mime_type: c_struct.mime_type.to_string(), - } - } -} -#[derive(Clone, Debug)] -pub struct FormItem { - pub name: String, - pub value: String, -} - -#[repr(C)] -pub struct CFormItem { - pub name: CStringWrapper, - pub value: CStringWrapper, -} - -impl FormItem { - pub fn to_c_struct(&self) -> CFormItem { - CFormItem { - name: CStringWrapper::from(&self.name), - value: CStringWrapper::from(&self.value), - } - } - - pub fn from_c_struct(c_struct: &CFormItem) -> Self { - FormItem { - name: c_struct.name.to_string(), - value: c_struct.value.to_string(), - } - } -} diff --git a/services/service/rust/src/lib.rs b/services/service/rust/src/lib.rs deleted file mode 100644 index 20a6ed28..00000000 --- a/services/service/rust/src/lib.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ -//! This create implement the request proxy and stub -#![allow(dead_code, unused_imports, unused_variables)] -extern crate ipc_rust; -#[macro_use] -extern crate hilog_rust; -pub mod enumration; -mod request_binding; -mod request_service; -mod request_service_ability; -pub mod request_task; -pub mod task_config; -pub mod task_info; -pub mod task_manager; -pub mod form_item; -mod log; -pub mod progress; -pub mod utils; -pub mod c_string_wrapper; -mod download_server_ipc_interface_code; -pub mod filter; - -pub(crate) mod trace; -pub(crate) mod sys_event; - -use enumration::ErrorCode; -use hilog_rust::*; -use ipc_rust::{ - define_remote_object, BorrowedMsgParcel, IRemoteBroker, InterfaceToken, IpcResult, - IpcStatusCode, RemoteObj, RemoteStub, -}; -pub use log::LOG_LABEL; -pub use request_service::{start, stop, RequestService}; -use std::convert::{TryFrom, TryInto}; -use std::{ - ffi::{c_char, CString}, - file, - option::Option, -}; -use task_manager::*; -use download_server_ipc_interface_code::*; - -impl TryFrom for RequestInterfaceCode { - type Error = IpcStatusCode; - fn try_from(code: u32) -> IpcResult { - match code { - _ if code == RequestInterfaceCode::Construct as u32 => Ok(RequestInterfaceCode::Construct), - _ if code == RequestInterfaceCode::Pause as u32 => Ok(RequestInterfaceCode::Pause), - _ if code == RequestInterfaceCode::Query as u32 => Ok(RequestInterfaceCode::Query), - _ if code == RequestInterfaceCode::QueryMimeType as u32 => Ok(RequestInterfaceCode::QueryMimeType), - _ if code == RequestInterfaceCode::Remove as u32 => Ok(RequestInterfaceCode::Remove), - _ if code == RequestInterfaceCode::Resume as u32 => Ok(RequestInterfaceCode::Resume), - _ if code == RequestInterfaceCode::On as u32 => Ok(RequestInterfaceCode::On), - _ if code == RequestInterfaceCode::Off as u32 => Ok(RequestInterfaceCode::Off), - _ if code == RequestInterfaceCode::Start as u32 => Ok(RequestInterfaceCode::Start), - _ if code == RequestInterfaceCode::Stop as u32 => Ok(RequestInterfaceCode::Stop), - _ if code == RequestInterfaceCode::Show as u32 => Ok(RequestInterfaceCode::Show), - _ if code == RequestInterfaceCode::Touch as u32 => Ok(RequestInterfaceCode::Touch), - _ if code == RequestInterfaceCode::Search as u32 => Ok(RequestInterfaceCode::Search), - _ => Err(IpcStatusCode::Failed), - } - } -} - -/// Function between proxy and stub of RequestServiceInterface -pub trait RequestServiceInterface: IRemoteBroker { - /// request construct--create task - fn construct(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// pause--task object - fn pause(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// resume--task object - fn resume(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// on--task object - fn on(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// off--task object - fn off(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// start task--task object - fn start(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// stop task--task object - fn stop(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// query mime type - fn query_mime_type( - &self, - data: &BorrowedMsgParcel, - reply: &mut BorrowedMsgParcel, - ) -> IpcResult<()>; - /// remove - fn remove(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// Shows specified task details belongs to the caller. - fn show(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// Touches specified task with token. - fn touch(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// Searches tasks, for system. - fn search(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; - /// Searches tasks - fn query(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()>; -} - -fn on_remote_request( - stub: &dyn RequestServiceInterface, - code: u32, - data: &BorrowedMsgParcel, - reply: &mut BorrowedMsgParcel, -) -> IpcResult<()> { - info!(LOG_LABEL, "on_remote_request code {}", @public(code)); - let service_token: InterfaceToken = - InterfaceToken::new("OHOS.Download.RequestServiceInterface"); - let token: InterfaceToken = match data.read::() { - Ok(i) => i, - _ => InterfaceToken::new("token error"), - }; - if service_token.get_token() != token.get_token() { - error!(LOG_LABEL, "token error"); - return Err(IpcStatusCode::Failed); - } - match code.try_into()? { - RequestInterfaceCode::Construct => stub.construct(data, reply), - RequestInterfaceCode::Pause => stub.pause(data, reply), - RequestInterfaceCode::Query => stub.query(data, reply), - RequestInterfaceCode::QueryMimeType => stub.query_mime_type(data, reply), - RequestInterfaceCode::Remove => stub.remove(data, reply), - RequestInterfaceCode::Resume => stub.resume(data, reply), - RequestInterfaceCode::On => stub.on(data, reply), - RequestInterfaceCode::Off => stub.off(data, reply), - RequestInterfaceCode::Start => stub.start(data, reply), - RequestInterfaceCode::Stop => stub.stop(data, reply), - RequestInterfaceCode::Show => stub.show(data, reply), - RequestInterfaceCode::Touch => stub.touch(data, reply), - RequestInterfaceCode::Search => stub.search(data, reply), - RequestInterfaceCode::Clear => todo!(), - } -} - -define_remote_object!( - RequestServiceInterface["ohos.request.service"] { - stub: RequestServiceStub(on_remote_request), - proxy: RequestServiceProxy, - } -); - -impl RequestServiceInterface for RequestServiceProxy { - fn construct(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn pause(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn query_mime_type( - &self, - data: &BorrowedMsgParcel, - reply: &mut BorrowedMsgParcel, - ) -> IpcResult<()> { - Ok(()) - } - - fn remove(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn resume(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn on(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn off(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn start(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn stop(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn search(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn show(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn touch(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } - - fn query(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - Ok(()) - } -} diff --git a/services/service/rust/src/log.rs b/services/service/rust/src/log.rs deleted file mode 100644 index c3dcf386..00000000 --- a/services/service/rust/src/log.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ -use hilog_rust::*; -use std::{ - ffi::{c_char, CString}, - file, - option::Option, -}; - -/// hilog label -pub const LOG_LABEL: HiLogLabel = HiLogLabel { - log_type: LogType::LogCore, - domain: 0xD001C00, - tag: "RequestService", -}; - -/// print debug log -#[macro_export] -macro_rules! log_debug { - ($($arg:tt)*) => ({ - debug!(LOG_LABEL, "[{}:{}]:{}",file!().rsplit('/').collect::>().first().unwrap(), - line!(), format!($($arg)*)); - }) -} -/// print info log -#[macro_export] -macro_rules! log_info { - ($($arg:tt)*) => ({ - info!(LOG_LABEL, "[{}:{}]:{}", file!().rsplit('/').collect::>().first().unwrap(), - line!(), format!($($arg)*)); - }) -} -/// print warn log -#[macro_export] -macro_rules! log_warn { - ($($arg:tt)*) => ({ - warn!(LOG_LABEL, "[{}:{}]:{}", file!().rsplit('/').collect::>().first().unwrap(), - line!(), format!($($arg)*)); - }) -} -/// print error log -#[macro_export] -macro_rules! log_error { - ($($arg:tt)*) => ({ - error!(LOG_LABEL, "[{}:{}]:{}", file!().rsplit('/').collect::>().first().unwrap(), - line!(), format!($($arg)*)); - }) -} -/// print fatal log -#[macro_export] -macro_rules! log_fatal { - ($($arg:tt)*) => ({ - fatal!(LOG_LABEL, "[{}:{}]:{}", file!().rsplit('/').collect::>().first().unwrap(), - line!(), format!($($arg)*)); - }) -} diff --git a/services/service/rust/src/progress.rs b/services/service/rust/src/progress.rs deleted file mode 100644 index eac8238a..00000000 --- a/services/service/rust/src/progress.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use super::{c_string_wrapper::*, enumration::*, utils::*}; -use std::collections::HashMap; - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct CommonProgress { - pub state: u8, - pub index: usize, - pub total_processed: usize, -} - -#[derive(Debug)] -pub struct Progress { - pub common_data: CommonProgress, - pub sizes: Vec, - pub processed: Vec, - pub extras: HashMap, -} - -#[repr(C)] -pub struct CProgress { - pub common_data: CommonProgress, - pub sizes: CStringWrapper, - pub processed: CStringWrapper, - pub extras: CStringWrapper, -} - -impl Progress { - pub fn new(sizes: Vec) -> Self { - let len = sizes.len(); - Progress { - common_data: CommonProgress { - state: State::CREATED as u8, - index: 0, - total_processed: 0, - }, - sizes, - processed: vec![0; len], - extras: HashMap::::new(), - } - } - - pub fn to_c_struct(&self, sizes: &String, processed: &String, extras: &String) -> CProgress { - CProgress { - common_data: self.common_data, - sizes: CStringWrapper::from(sizes), - processed: CStringWrapper::from(processed), - extras: CStringWrapper::from(extras), - } - } - - pub fn from_c_struct(c_struct: &CProgress) -> Self { - Progress { - common_data: c_struct.common_data, - sizes: split_string(&mut c_struct.sizes.to_string()).map(|s| s.parse::().unwrap()).collect(), - processed: split_string(&mut c_struct.processed.to_string()).map(|s| s.parse::().unwrap()).collect(), - extras: string_to_hashmap(&mut c_struct.extras.to_string()), - } - } -} - -impl Clone for Progress { - fn clone(&self) -> Self { - Progress { - common_data: self.common_data, - sizes: self.sizes.clone(), - processed: self.processed.clone(), - extras: self.extras.clone(), - } - } -} - -#[repr(C)] -pub struct RequestTaskMsg { - pub task_id: u32, - pub uid: i32, - pub action: u8, -} diff --git a/services/service/rust/src/request_binding.rs b/services/service/rust/src/request_binding.rs deleted file mode 100644 index 65b1136f..00000000 --- a/services/service/rust/src/request_binding.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ -//! rust to C++ interface -#![allow(unused_variables)] -// C interface for check permission -use super::{enumration::*, progress::RequestTaskMsg, form_item::CFileSpec, - form_item::CFormItem, task_info::*, task_config::*, c_string_wrapper::*, filter::*}; -use std::ffi::{c_char, c_void}; - -type APPSTATECB = extern "C" fn(i32, i32); -type NETWORKCB = extern "C" fn(); - -extern "C" { - pub fn RequestCheckPermission(tokenId: u64, permission: CStringWrapper) -> bool; - pub fn RequestInitServiceHandler(); - pub fn RequestPostTask(f: extern "C" fn()); - pub fn RequestBackgroundNotify( - msg: RequestTaskMsg, - path: *const c_char, - pathLen: i32, - percent: u32, - ); - pub fn IsOnline() -> bool; - pub fn RegisterNetworkCallback(f: NETWORKCB); - pub fn RegisterAPPStateCallback(f: APPSTATECB); - pub fn GetNetworkInfo() -> *const NetworkInfo; - pub fn GetTopBundleName() -> CStringWrapper; - pub fn DeleteCTaskInfo(ptr: *const CTaskInfo); - pub fn DeleteCTaskConfig(ptr: *const CTaskConfig); - pub fn DeleteCTaskConfigs(ptr: *const*const CTaskConfig); - pub fn DeleteCStringPtr(ptr: *const CStringWrapper); - pub fn DeleteChar(ptr: *const c_char); - pub fn DeleteCFormItem(ptr: *const CFormItem); - pub fn DeleteCFileSpec(ptr: *const CFileSpec); - pub fn DeleteCEachFileStatus(ptr: *const CEachFileStatus); - pub fn DeleteCVectorWrapper(ptr: *const u32); - pub fn HasRequestTaskRecord(taskId: u32) -> bool; - pub fn RecordRequestTaskInfo(taskInfo: *const CTaskInfo) -> bool; - pub fn UpdateRequestTaskInfo(taskId: u32, updateInfo: *const CUpdateInfo) -> bool; - pub fn Touch(taskId: u32, uid: u64, token: CStringWrapper) -> *const CTaskInfo; - pub fn Query(taskId: u32, queryAction: Action) -> *const CTaskInfo; - pub fn Search(filter: CFilter) -> CVectorWrapper; - pub fn HasTaskConfigRecord(taskId: u32) -> bool; - pub fn RecordRequestTaskConfig(taskConfig: *const CTaskConfig) -> bool; - pub fn QueryAllTaskConfig() -> *const*const CTaskConfig; - pub fn QueryTaskConfigLen() -> i32; - pub fn CleanTaskConfigTable(taskId: u32, uid: u64) -> bool; - pub fn RequestIsSystemAPI(tokenId: u64) -> bool; - pub fn GetCallingBundle(tokenId: u64) -> CStringWrapper; - pub fn PublishStateChangeEvents(bundleName: *const c_char, bundleNameLen: u32, taskId: u32, state: i32); -} diff --git a/services/service/rust/src/request_service.rs b/services/service/rust/src/request_service.rs deleted file mode 100644 index 114976b4..00000000 --- a/services/service/rust/src/request_service.rs +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ -//! This create implement the request server -#![allow(unused_variables, clippy::vec_init_then_push)] -extern crate ipc_rust; -extern crate system_ability_fwk_rust; - -use ipc_rust::{ - get_calling_uid, BorrowedMsgParcel, FileDesc, IMsgParcel, IRemoteBroker, IRemoteObj, - InterfaceToken, IpcResult, IpcStatusCode, MsgParcel, RemoteObj, String16 -}; -use std::ffi::{c_char, CString}; -use std::{ - collections::HashMap, - fs::File, - option::Option, - string::String, - sync::{Arc, Mutex}, - io::Write, -}; - -use super::{ - enumration::*, form_item::*, log::LOG_LABEL, request_service_ability::*, - task_config::*, task_info::*, RequestServiceInterface, filter::*, request_binding, -}; -use hilog_rust::*; - -static INTERNET_PERMISSION: &str = "ohos.permission.INTERNET"; -static HELP_MSG: &str = "usage:\n\ - -h help text for the tool\n\ - -t [taskid] without taskid: display all task summary info; \ - taskid: display one task detail info\n"; - -/// RequestService type -pub struct RequestService; - -impl RequestServiceInterface for RequestService { - fn construct(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - debug!(LOG_LABEL, "construct"); - let action: u32 = data.read()?; - let action: Action = Action::from(action as u8); - let version: u32 = data.read()?; - let version: Version = Version::from(version as u8); - let mode: u32 = data.read()?; - let mode: Mode = Mode::from(mode as u8); - let cover: bool = data.read()?; - let network: u32 = data.read()?; - let network: Network = Network::from(network as u8); - let metered: bool = data.read()?; - let roaming: bool = data.read()?; - let retry: bool = data.read()?; - let redirect: bool = data.read()?; - let background: bool = data.read()?; - let index: u32 = data.read()?; - let begins: i64 = data.read()?; - let ends: i64 = data.read()?; - let gauge: bool = data.read()?; - let precise: bool = data.read()?; - let url: String = data.read()?; - let title: String = data.read()?; - let method: String = data.read()?; - let token: String = data.read()?; - let description: String = data.read()?; - let data_base: String = data.read()?; - - let bundle = RequestAbility::get_ability_instance().get_calling_bundle(); - let mut task_id: u32 = 0; - let uid: u64 = get_calling_uid(); - - let mut form_items = Vec::::new(); - let form_size: u32 = data.read()?; - if form_size > data.get_readable_bytes() { - error!(LOG_LABEL, "size is too large"); - reply.write(&(ErrorCode::Ipc_size_too_large as i32)); - return Err(IpcStatusCode::Failed); - } - for i in 0..form_size { - let name: String = data.read()?; - let value: String = data.read()?; - form_items.push(FormItem { name, value }); - } - - let mut files = Vec::::new(); - let mut file_specs: Vec = Vec::new(); - let file_size: u32 = data.read()?; - if file_size > data.get_readable_bytes() { - error!(LOG_LABEL, "size is too large"); - reply.write(&(ErrorCode::Ipc_size_too_large as i32)); - return Err(IpcStatusCode::Failed); - } - for i in 0..file_size { - let name: String = data.read()?; - let path: String = data.read()?; - let file_name: String = data.read()?; - let mime_type: String = data.read()?; - if action == Action::UPLOAD{ - let file = RequestAbility::open_file_readonly(uid, &bundle, &path)?; - files.push(file); - } else { - let file = RequestAbility::open_file_readwrite(uid, &bundle, &path)?; - files.push(file); - } - let fd_error: i32 = data.read()?; - file_specs.push(FileSpec { - name, - path, - file_name, - mime_type, - }); - } - - // Response Bodys fd. - let body_file_size: u32 = data.read()?; - let mut body_files = Vec::new(); - let mut body_file_names: Vec = Vec::new(); - for i in 0..body_file_size { - let file_name: String = data.read()?; - let body_file = RequestAbility::open_file_readwrite(uid, &bundle, &file_name)?; - body_file_names.push(file_name); - body_files.push(body_file); - } - - let header_size: u32 = data.read()?; - if header_size > data.get_readable_bytes() { - error!(LOG_LABEL, "size is too large"); - reply.write(&(ErrorCode::Ipc_size_too_large as i32)); - return Err(IpcStatusCode::Failed); - } - let mut headers: HashMap = HashMap::new(); - for i in 0..header_size { - let key: String = data.read()?; - let value: String = data.read()?; - headers.insert(key, value); - } - - let extras_size: u32 = data.read()?; - if extras_size > data.get_readable_bytes() { - error!(LOG_LABEL, "size is too large"); - reply.write(&(ErrorCode::Ipc_size_too_large as i32)); - return Err(IpcStatusCode::Failed); - } - let mut extras: HashMap = HashMap::new(); - for i in 0..extras_size { - let key: String = data.read()?; - let value: String = data.read()?; - extras.insert(key, value); - } - - let task_config = TaskConfig { - bundle, - url, - title, - description, - method, - headers, - data: data_base, - token, - extras, - version, - form_items, - file_specs, - body_file_names, - common_data: CommonTaskConfig { - task_id , - uid , - action, - mode, - cover, - network, - metered, - roaming, - retry, - redirect, - index, - begins: begins as u64, - ends, - gauge, - precise, - background, - }, - }; - debug!(LOG_LABEL, "files {:?}", @public(files)); - let ret = - RequestAbility::get_ability_instance().construct(task_config, files, body_files, &mut task_id); - let remote_object: RemoteObj = data.read::()?; - RequestAbility::get_ability_instance().on(task_id, "done".to_string(), remote_object); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - return Err(IpcStatusCode::Failed); - } - debug!(LOG_LABEL, "task id {}", @public(task_id)); - reply.write(&(task_id as i32))?; - Ok(()) - } - - fn pause(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "Pause"); - let version: u32 = data.read()?; - if Version::from(version as u8) == Version::API9 { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - } - - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let ret = RequestAbility::get_ability_instance().pause(id); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "Pause fail ret {}", @public(ret as u32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn query_mime_type( - &self, - data: &BorrowedMsgParcel, - reply: &mut BorrowedMsgParcel, - ) -> IpcResult<()> { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - debug!(LOG_LABEL, "QueryMimeType"); - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let mime = RequestAbility::get_ability_instance().query_mime_type(id); - reply.write(&(ErrorCode::ErrOk as i32))?; - reply.write(&mime)?; - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn remove(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "remove"); - let version: u32 = data.read()?; - if Version::from(version as u8) == Version::API9 { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - } - - let id: String = data.read()?; - debug!(LOG_LABEL, "id {}", @public(id)); - match id.parse::() { - Ok(id) => { - let ret = RequestAbility::get_ability_instance().remove(id); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "Remove fail ret {}", @public(ret as i32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn resume(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - debug!(LOG_LABEL, "resume"); - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let ret = RequestAbility::get_ability_instance().resume(id); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "Resume fail ret {}", @public(ret as i32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn on(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "on"); - let on_type: String = data.read()?; - if on_type.is_empty() { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::Parameter_check as i32)); - return Err(IpcStatusCode::Failed); - } - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let remote_object: RemoteObj = data.read::()?; - let ret = RequestAbility::get_ability_instance().on(id, on_type, remote_object); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "on fail"); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn off(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "off"); - let off_type: String = data.read()?; - debug!(LOG_LABEL, "off_type: {:?}", @public(off_type)); - if off_type.is_empty() { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::Parameter_check as i32)); - return Err(IpcStatusCode::Failed); - } - let id: String = data.read()?; - debug!(LOG_LABEL, "id {}", @public(id)); - match id.parse::() { - Ok(id) => { - debug!(LOG_LABEL, "int id: {:?}", @public(id)); - let ret = RequestAbility::get_ability_instance().off(id, off_type); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "Off fail ret {}", @public(ret as i32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn start(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - debug!(LOG_LABEL, "start"); - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let ret = RequestAbility::get_ability_instance().start_task(id); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "start fail ret {}", @public(ret as i32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn stop(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "stop"); - let id: String = data.read()?; - match id.parse::() { - Ok(id) => { - let ret = RequestAbility::get_ability_instance().stop_task(id); - reply.write(&(ret as i32))?; - if ret != ErrorCode::ErrOk { - error!(LOG_LABEL, "stop fail ret {}", @public(ret as i32)); - return Err(IpcStatusCode::Failed); - } - Ok(()) - } - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn show(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "show"); - if !RequestAbility::get_ability_instance().check_permission(INTERNET_PERMISSION) { - error!(LOG_LABEL, "permission denied"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - let id: String = data.read()?; - debug!(LOG_LABEL, "id: {}", @public(id)); - match id.parse::() { - Ok(id) => match RequestAbility::get_ability_instance().show_task(id) { - Some(tf) => { - reply.write(&(ErrorCode::ErrOk as i32)); - debug!(LOG_LABEL, "tf: {:?}", @public(tf)); - RequestAbility::get_ability_instance().serialize_task_info(tf, reply)?; - Ok(()) - } - None => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - }, - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } - - fn touch(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "touch"); - let id: String = data.read()?; - debug!(LOG_LABEL, "id: {}", @public(id)); - match id.parse::() { - Ok(id) => { - let token: String = data.read()?; - match RequestAbility::get_ability_instance().touch_task(id, token) { - Some(tf) => { - reply.write(&(ErrorCode::ErrOk as i32)); - debug!(LOG_LABEL, "tf: {:?}", @public(tf)); - RequestAbility::get_ability_instance().serialize_task_info(tf, reply)?; - return Ok(()); - } - None => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - }, - _ => { - error!(LOG_LABEL, "id or token is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32))?; - return Err(IpcStatusCode::Failed); - } - } - } - - fn search(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "search"); - let mut bundle: String = data.read()?; - if !RequestAbility::get_ability_instance().is_system_api() { - bundle = RequestAbility::get_ability_instance().get_calling_bundle(); - } - debug!(LOG_LABEL, "search bundle is {}", @public(bundle)); - let before: i64 = data.read()?; - debug!(LOG_LABEL, "search before is {}", @public(before)); - let after: i64 = data.read()?; - debug!(LOG_LABEL, "search after is {}", @public(after)); - let state: u32 = data.read()?; - debug!(LOG_LABEL, "search state is {}", @public(state)); - let action: u32 = data.read()?; - debug!(LOG_LABEL, "search action is {}", @public(action)); - let mode: u32 = data.read()?; - debug!(LOG_LABEL, "search mode is {}", @public(mode)); - let common_data = CommonFilter { - before, - after, - state: state as u8, - action: action as u8, - mode: mode as u8, - }; - let filter = Filter { - bundle, - common_data, - }; - let ids = RequestAbility::get_ability_instance().search_task(filter); - debug!(LOG_LABEL, "search task ids is {:?}", @public(ids)); - reply.write(&(ids.len() as u32)); - for it in ids.iter() { - reply.write(&(it.to_string()))?; - } - Ok(()) - } - - fn query(&self, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - debug!(LOG_LABEL, "query"); - if !RequestAbility::get_ability_instance().is_system_api() { - error!(LOG_LABEL, "not system api"); - reply.write(&(ErrorCode::SystemApi as i32)); - return Err(IpcStatusCode::Failed); - } - let ret = RequestAbility::get_ability_instance().get_query_permission(); - if ret == QueryPermission::NoPermisson { - error!(LOG_LABEL, "no query permission"); - reply.write(&(ErrorCode::Permission as i32)); - return Err(IpcStatusCode::Failed); - } - let id: String = data.read()?; - debug!(LOG_LABEL, "id: {}", @public(id)); - match id.parse::() { - Ok(id) => match RequestAbility::get_ability_instance().query_task(id, ret) { - Some(tf) => { - reply.write(&(ErrorCode::ErrOk as i32)); - debug!(LOG_LABEL, "tf: {:?}", @public(tf)); - RequestAbility::get_ability_instance().serialize_task_info(tf, reply)?; - Ok(()) - } - None => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - }, - _ => { - error!(LOG_LABEL, "id is not a valid"); - reply.write(&(ErrorCode::TaskNotFound as i32)); - Err(IpcStatusCode::Failed) - } - } - } -} - -/// start -pub fn start() { - RequestAbility::get_ability_instance(); -} - -/// stop -pub fn stop() { - RequestAbility::get_ability_instance().stop(); -} - -impl IRemoteBroker for RequestService { - fn dump(&self, file: &FileDesc, args: &mut Vec) -> i32 { - let len = args.len(); - if len == 0 || args[0].get_string().eq("-h") { - file.as_ref().write(HELP_MSG.as_bytes()); - return IpcStatusCode::Ok as i32; - } - - if !args[0].get_string().eq("-t") { - file.as_ref().write("invalid args".as_bytes()); - return IpcStatusCode::Ok as i32; - } - - match len { - 1 => { - RequestAbility::get_ability_instance().dump_all_task_info(file); - } - 2 => { - let task_id = args[1].get_string().parse::(); - match task_id { - Ok(id) => RequestAbility::get_ability_instance().dump_one_task_info(file, id), - Err(_) => { file.as_ref().write("-t accept a number".as_bytes()); } - } - } - _ => { - file.as_ref().write("too many args, -t accept no arg or one arg".as_bytes()); - } - } - IpcStatusCode::Ok as i32 - } -} diff --git a/services/service/rust/src/request_service_ability.rs b/services/service/rust/src/request_service_ability.rs deleted file mode 100644 index 88ac64af..00000000 --- a/services/service/rust/src/request_service_ability.rs +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ -//! ipc and task manger for service ability -#![allow(unused_variables, clippy::vec_init_then_push)] -extern crate ipc_rust; -extern crate system_ability_fwk_rust; -use super::{ - enumration::*, log::LOG_LABEL, progress::*, request_binding, task_config::*, - task_info::*, task_manager::*, download_server_ipc_interface_code::*, filter::*, c_string_wrapper::* -}; -use hilog_rust::*; -use ipc_rust::{ - get_calling_token_id, get_calling_uid, FileDesc, IRemoteBroker, IRemoteObj, InterfaceToken, - IpcResult, IpcStatusCode, MsgParcel, RemoteObj, BorrowedMsgParcel, get_self_token_id, -}; -use std::ffi::{c_char, CString}; -use std::{ - collections::HashMap, - fmt::Debug, - fs::File, - fs::OpenOptions, - mem::MaybeUninit, - result::Result, - string::String, - sync::{Arc, Mutex, Once, atomic::Ordering}, - thread, - io::Write, -}; - -#[derive(PartialEq, Debug)] -pub enum ServerRunState { - NoStart, - Running, -} - -pub struct RequestAbility { - pub server_state: ServerRunState, - pub reg_remote_obj: Mutex>, - unregistered_notify: Mutex>, -} - -impl RequestAbility { - fn new(state: ServerRunState, obj: Mutex>) -> Self { - RequestAbility { - server_state: state, - reg_remote_obj: obj, - unregistered_notify: Mutex::new(HashMap::new()), - } - } - - pub fn init(&mut self) -> i32 { - debug!(LOG_LABEL, "init"); - TaskManager::get_instance().register_callback(Box::new(RequestAbility::notify_client), - Box::new(RequestAbility::notify_task_info)); - monitor_network(); - monitor_app_state(); - monitor_task(); - ylong_runtime::spawn(restore_all_tasks()); - ylong_runtime::spawn(unload_sa()); - TaskManager::get_instance().dump_all_task_info(); - 0 - } - - pub fn start(&mut self) { - debug!(LOG_LABEL, "start"); - if self.server_state == ServerRunState::Running { - info!(LOG_LABEL, "DownloadServiceAbility is already running"); - return; - } - unsafe { - request_binding::RequestInitServiceHandler(); - } - let ret = self.init(); - if ret != 0 { - unsafe { - extern "C" fn ability_init() { - RequestAbility::get_ability_instance().init(); - } - request_binding::RequestPostTask(ability_init); - } - } - self.server_state = ServerRunState::Running; - } - - pub fn stop(&mut self) { - debug!(LOG_LABEL, "stop"); - if ServerRunState::NoStart == self.server_state { - return; - } - self.server_state = ServerRunState::NoStart; - } - - pub fn construct(&self, config: TaskConfig, files: Vec, body_files: Vec, task_id: &mut u32) -> ErrorCode { - debug!(LOG_LABEL, "construct"); - let uid = get_calling_uid(); - let version = config.version.clone(); - let error = TaskManager::get_instance().construct_task( - Arc::new(config), - uid, - task_id, - files, - body_files, - ); - if version != Version::API10 { - TaskManager::get_instance().start(uid, *task_id); - } - error - } - - pub fn pause(&self, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "pause"); - TaskManager::get_instance().pause(get_calling_uid(), task_id) - } - - pub fn query_mime_type(&self, task_id: u32) -> String { - TaskManager::get_instance().query_mime_type(get_calling_uid(), task_id) - } - - pub fn remove(&self, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "remove"); - TaskManager::get_instance().remove(get_calling_uid(), task_id) - } - - pub fn resume(&self, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "resume"); - TaskManager::get_instance().resume(get_calling_uid(), task_id) - } - - pub fn on(&self, task_id: u32, on_type: String, obj: RemoteObj) -> ErrorCode { - let key = on_type.clone() + &String::from("-") + &task_id.to_string(); - debug!(LOG_LABEL, "on key {}", @public(key)); - self.reg_remote_obj.lock().unwrap().insert(key, obj); - RequestAbility::get_ability_instance().do_unregistered_notify(task_id, on_type); - ErrorCode::ErrOk - } - - pub fn off(&self, task_id: u32, off_type: String) -> ErrorCode { - debug!(LOG_LABEL, "off"); - let key = off_type + &String::from("-") + &task_id.to_string(); - debug!(LOG_LABEL, "off key {}", @public(key)); - let reg_obj = self.reg_remote_obj.lock().unwrap().clone(); - if !reg_obj.contains_key(&key) { - error!(LOG_LABEL, "off {} nonexistence", @public(key)); - return ErrorCode::Other; - } - self.reg_remote_obj.lock().unwrap().remove(&key); - debug!(LOG_LABEL, "off end {}", @public(&key)); - ErrorCode::ErrOk - } - - pub fn check_permission(&self, permission: &str) -> bool { - debug!(LOG_LABEL, "check_permission"); - let token_id = get_calling_token_id(); - unsafe { request_binding::RequestCheckPermission(token_id, CStringWrapper::from(permission)) } - } - - pub fn get_query_permission(&self) -> QueryPermission { - debug!(LOG_LABEL, "get_query_action"); - let token_id = get_calling_token_id(); - let query_download_permission = "ohos.permission.DOWNLOAD_SESSION_MANAGER".to_string(); - let query_upload_permission = "ohos.permission.UPLOAD_SESSION_MANAGER".to_string(); - let query_download = unsafe { - request_binding::RequestCheckPermission(token_id, CStringWrapper::from(&query_download_permission)) - }; - let query_upload = unsafe { - request_binding::RequestCheckPermission(token_id, CStringWrapper::from(&query_upload_permission)) - }; - info!(LOG_LABEL, "query download task permission is {}, query upload task permission is {}", - @public(query_download), @public(query_upload)); - - if query_download && query_download { - return QueryPermission::QueryAll; - } - if query_download { - return QueryPermission::QueryDownLoad; - } - if query_upload { - return QueryPermission::QueryUpload; - } - return QueryPermission::NoPermisson; - } - - pub fn start_task(&self, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "start_task"); - TaskManager::get_instance().start(get_calling_uid(), task_id) - } - - pub fn stop_task(&self, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "stop_task"); - TaskManager::get_instance().stop(get_calling_uid(), task_id) - } - - pub fn show_task(&self, task_id: u32) -> Option { - debug!(LOG_LABEL, "show_task"); - TaskManager::get_instance().show(get_calling_uid(), task_id) - } - - pub fn touch_task(&self, task_id: u32, token: String) -> Option { - debug!(LOG_LABEL, "touch_task"); - TaskManager::get_instance().touch(get_calling_uid(), task_id, token) - } - - pub fn search_task(&self, filter: Filter) -> Vec { - debug!(LOG_LABEL, "search_task"); - TaskManager::get_instance().search(filter) - } - - pub fn is_system_api(&self) -> bool { - debug!(LOG_LABEL, "is_system_api"); - let token_id = get_calling_token_id(); - debug!(LOG_LABEL, "token_id {}", @public(&token_id)); - unsafe { request_binding::RequestIsSystemAPI(token_id) } - } - - pub fn get_calling_bundle(&self) -> String { - debug!(LOG_LABEL, "get_calling_bundle"); - let token_id = get_calling_token_id(); - debug!(LOG_LABEL, "token_id {}", @public(&token_id)); - unsafe { request_binding::GetCallingBundle(token_id).to_string() } - } - pub fn query_task(&self, task_id: u32, query_permission: QueryPermission) -> Option { - debug!(LOG_LABEL, "touch_task"); - match query_permission { - QueryPermission::NoPermisson => None, - QueryPermission::QueryDownLoad => TaskManager::get_instance().query(task_id, Action::DOWNLOAD), - QueryPermission::QueryUpload => TaskManager::get_instance().query(task_id, Action::UPLOAD), - QueryPermission::QueryAll => TaskManager::get_instance().query(task_id, Action::ANY), - } - } - - pub fn add_unregister_notify(&self, task_id: u32, reg_type: String) { - match reg_type.as_str() { - "complete" | "fail" | "progress" | "pause" | "resume" | "remove" => { - let key = reg_type.clone() + &String::from("-") + &task_id.to_string(); - let notify = self.unregistered_notify.lock().unwrap().clone(); - if notify.contains_key(&key) { - return; - } - self.unregistered_notify - .lock() - .unwrap() - .insert(key, task_id); - } - _ => {} - } - } - - fn do_unregistered_notify(&self, task_id: u32, reg_type: String) { - match TaskManager::get_instance().query_one_task(task_id) { - Some(task) => { - let key = reg_type.clone() + &String::from("-") + &task_id.to_string(); - let notify = self.unregistered_notify.lock().unwrap().clone(); - if notify.contains_key(&key) { - debug!(LOG_LABEL, "notify taskId: {} event: {}", @public(task_id), @public(reg_type)); - let notify_data = task.build_notify_data(); - RequestAbility::notify_client(reg_type, ¬ify_data); - self.unregistered_notify.lock().unwrap().remove(&key); - } - } - None => { - error!(LOG_LABEL, "the task has been removed from the map"); - } - } - } - - pub fn get_ability_instance() -> &'static mut RequestAbility { - static mut REQUESTABILITY: Option = None; - static ONCE: Once = Once::new(); - unsafe { - ONCE.call_once(|| { - REQUESTABILITY = Some(RequestAbility::new( - ServerRunState::NoStart, - Mutex::new(HashMap::new()), - )); - REQUESTABILITY.as_mut().unwrap().start(); - }); - REQUESTABILITY.as_mut().unwrap() - } - } - - // Send to "OHOS.Download.NotifyInterface" CallBack(). - pub fn notify_client(cb_type: String, notify_data: &NotifyData) { - debug!(LOG_LABEL, "notify_client"); - if notify_data.progress.common_data.index >= notify_data.progress.sizes.len() { - error!(LOG_LABEL, "index out of range"); - return; - } - let common_data = notify_data.progress.common_data; - if (common_data.state == State::RUNNING as u8 || common_data.state == State::RETRYING as u8) && - common_data.total_processed == 0 { - return; - } - debug!(LOG_LABEL, "notify_data {:?}", @public(notify_data)); - let key = cb_type.clone() + &String::from("-") + ¬ify_data.task_id.to_string(); - debug!(LOG_LABEL, "key {}", @public(key)); - { - let reg_obj = RequestAbility::get_ability_instance() - .reg_remote_obj - .lock() - .unwrap() - .clone(); - if reg_obj.contains_key(&key) { - let obj = reg_obj.get(&key).unwrap().clone(); - let mut client_data = MsgParcel::new().expect("MsgParcel should success"); - let notify_token: InterfaceToken = - InterfaceToken::new("OHOS.Download.NotifyInterface"); - client_data.write::(¬ify_token).ok(); - client_data.write(&cb_type).ok(); - client_data.write(&(notify_data.task_id.to_string())).ok(); - client_data - .write(&(notify_data.progress.common_data.state as u32)) - .ok(); - let index = notify_data.progress.common_data.index; - client_data.write(&(index as u32)).ok(); - client_data - .write(&(notify_data.progress.processed[index] as u64)) - .ok(); - client_data - .write(&(notify_data.progress.common_data.total_processed as u64)) - .ok(); - client_data.write(&(notify_data.progress.sizes)).ok(); - client_data - .write(&(notify_data.progress.extras.len() as u32)) - .ok(); - for (k, v) in notify_data.progress.extras.iter() { - client_data.write(&k).ok(); - client_data.write(&v).ok(); - } - client_data.write(&(notify_data.action as u32)); - client_data.write(&(notify_data.version as u32)).ok(); - - client_data - .write(&(notify_data.each_file_status.len() as u32)) - .ok(); - for item in notify_data.each_file_status.iter() { - client_data.write(&(item.path)).ok(); - client_data.write(&(item.reason as u32)).ok(); - client_data.write(&(item.message)).ok(); - } - debug!(LOG_LABEL, "send_request"); - let reply = obj.send_request(RequestNotifyInterfaceCode::Notify as u32, &client_data, false).ok(); - return; - } - debug!(LOG_LABEL, "key not find"); - } - if notify_data.version != Version::API10 { - RequestAbility::get_ability_instance() - .add_unregister_notify(notify_data.task_id, cb_type); - } - } - - // Send to "OHOS.Download.NotifyInterface" Down(). - pub fn notify_task_info(task_info: &TaskInfo) { - debug!(LOG_LABEL, "notify_task_info"); - if task_info.progress.common_data.index >= task_info.progress.sizes.len() { - error!(LOG_LABEL, "index is out of bounds"); - return ; - } - let key = String::from("done") + &String::from("-") + &task_info.common_data.task_id.to_string(); - debug!(LOG_LABEL, "key {}", @public(key)); - let reg_obj = RequestAbility::get_ability_instance() - .reg_remote_obj - .lock() - .unwrap() - .clone(); - if reg_obj.contains_key(&key) { - debug!(LOG_LABEL, "notify_task_info contain the key"); - let obj = reg_obj.get(&key).unwrap().clone(); - let mut reply = MsgParcel::new().expect("MsgParcel should success"); - let notify_token: InterfaceToken = - InterfaceToken::new("OHOS.Download.NotifyInterface"); - reply.write::(¬ify_token).ok(); - reply.write(&(task_info.common_data.gauge)).ok(); - reply.write(&(task_info.common_data.retry)).ok(); - reply.write(&(task_info.common_data.action as u32)).ok(); - reply.write(&(task_info.common_data.mode as u32)).ok(); - reply.write(&(task_info.common_data.reason as u32)).ok(); - reply.write(&(task_info.common_data.tries)).ok(); - reply.write(&(task_info.common_data.uid.to_string())).ok(); - reply.write(&(task_info.bundle)).ok(); - reply.write(&task_info.url).ok(); - reply.write(&(task_info.common_data.task_id.to_string())).ok(); - reply.write(&task_info.title).ok(); - reply.write(&task_info.mime_type).ok(); - reply.write(&(task_info.common_data.ctime)).ok(); - reply.write(&(task_info.common_data.mtime)).ok(); - reply.write(&(task_info.data)).ok(); - reply.write(&(task_info.description)).ok(); - reply.write(&(task_info.form_items.len() as u32)).ok(); - for i in 0..task_info.form_items.len() { - reply.write(&(task_info.form_items[i].name)).ok(); - reply.write(&(task_info.form_items[i].value)).ok(); - } - reply.write(&(task_info.file_specs.len() as u32)).ok(); - for i in 0..task_info.file_specs.len() { - reply.write(&(task_info.file_specs[i].name)).ok(); - reply.write(&(task_info.file_specs[i].path)).ok(); - reply.write(&(task_info.file_specs[i].file_name)).ok(); - reply.write(&(task_info.file_specs[i].mime_type)).ok(); - } - reply.write(&(task_info.progress.common_data.state as u32)).ok(); - let index = task_info.progress.common_data.index; - reply.write(&(index as u32)).ok(); - reply.write(&(task_info.progress.processed[index] as u64)).ok(); - reply.write(&(task_info.progress.common_data.total_processed as u64)).ok(); - reply.write(&(task_info.progress.sizes)).ok(); - reply.write(&(task_info.progress.extras.len() as u32)).ok(); - for (k, v) in task_info.progress.extras.iter() { - reply.write(&(k)).ok(); - reply.write(&(v)).ok(); - } - reply.write(&(task_info.extras.len() as u32)).ok(); - for (k, v) in task_info.extras.iter() { - reply.write(&(k)).ok(); - reply.write(&(v)).ok(); - } - reply.write(&(task_info.common_data.version as u32)).ok(); - reply.write(&(task_info.each_file_status.len() as u32)).ok(); - for item in task_info.each_file_status.iter() { - reply.write(&(item.path)).ok(); - reply.write(&(item.reason as u32)).ok(); - reply.write(&(item.message)).ok(); - } - debug!(LOG_LABEL, "send_request"); - let reply = obj.send_request(RequestNotifyInterfaceCode::DoneNotify as u32, &reply, false).ok(); - - RequestAbility::get_ability_instance().off(task_info.common_data.task_id, String::from("done")); - } - } - - pub fn serialize_task_info(&self, tf: TaskInfo, reply: &mut BorrowedMsgParcel) -> IpcResult<()> { - reply.write(&(tf.common_data.gauge))?; - reply.write(&(tf.common_data.retry))?; - reply.write(&(tf.common_data.action as u32))?; - reply.write(&(tf.common_data.mode as u32))?; - reply.write(&(tf.common_data.reason as u32))?; - reply.write(&(tf.common_data.tries))?; - reply.write(&(tf.common_data.uid.to_string()))?; - reply.write(&(tf.bundle))?; - reply.write(&(tf.url))?; - reply.write(&(tf.common_data.task_id.to_string()))?; - reply.write(&tf.title)?; - reply.write(&tf.mime_type)?; - reply.write(&(tf.common_data.ctime))?; - reply.write(&(tf.common_data.mtime))?; - reply.write(&(tf.data))?; - reply.write(&(tf.description))?; - - reply.write(&(tf.form_items.len() as u32))?; - for i in 0..tf.form_items.len() { - reply.write(&(tf.form_items[i].name))?; - reply.write(&(tf.form_items[i].value))?; - } - - reply.write(&(tf.file_specs.len() as u32))?; - for i in 0..tf.file_specs.len() { - reply.write(&(tf.file_specs[i].name))?; - reply.write(&(tf.file_specs[i].path))?; - reply.write(&(tf.file_specs[i].file_name))?; - reply.write(&(tf.file_specs[i].mime_type))?; - } - - reply.write(&(tf.progress.common_data.state as u32))?; - let index = tf.progress.common_data.index; - reply.write(&(index as u32))?; - reply.write(&(tf.progress.processed[index] as u64))?; - reply.write(&(tf.progress.common_data.total_processed as u64))?; - reply.write(&(tf.progress.sizes))?; - - reply.write(&(tf.progress.extras.len() as u32))?; - for (k, v) in tf.progress.extras.iter() { - reply.write(&(k))?; - reply.write(&(v))?; - } - - reply.write(&(tf.extras.len() as u32))?; - for (k, v) in tf.extras.iter() { - reply.write(&(k))?; - reply.write(&(v))?; - } - reply.write(&(tf.common_data.version as u32))?; - reply.write(&(tf.each_file_status.len() as u32))?; - for item in tf.each_file_status.iter() { - reply.write(&(item.path))?; - reply.write(&(item.reason as u32))?; - reply.write(&(item.message))?; - } - Ok(()) - } - - pub fn dump_all_task_info(&self, file: &FileDesc) { - let vec = TaskManager::get_instance().query_all_task(); - let len = vec.len(); - let mut file = file.as_ref(); - file.write(format!("task num: {}\n", len).as_bytes()); - if len > 0 { - file.write(format!("{:<20}{:<12}{:<12}{:<12}\n", "id", "action", "state", "reason").as_bytes()); - for task in vec.iter() { - let guard = task.status.lock().unwrap(); - file.write(format!("{:<20}{:<12}{:<12}{:<12}\n", - task.task_id, task.conf.common_data.action as u8, - guard.state as u8, guard.reason as u8).as_bytes()); - } - } - } - - pub fn dump_one_task_info(&self, file: &FileDesc, task_id: u32) { - let task = TaskManager::get_instance().query_one_task(task_id); - let mut file = file.as_ref(); - if task.is_none() { - file.write(format!("invalid task id {}", task_id).as_bytes()); - return; - } - let task = task.unwrap(); - file.write(format!("{:<20}{:<12}{:<12}{:<12}{:<12}{:<12}{}\n", - "id", "action", "state", "reason", "total_size", "tran_size", "url").as_bytes()); - let guard = task.status.lock().unwrap(); - file.write(format!("{:<20}{:<12}{:<12}{:<12}{:<12}{:<12}{}\n", - task.task_id, task.conf.common_data.action as u8, guard.state as u8, guard.reason as u8, - task.file_total_size.load(Ordering::SeqCst), - task.progress.lock().unwrap().common_data.total_processed, task.conf.url).as_bytes()); - } - - pub fn convert_path(&self, uid: u64, bundle: &str, path: &str) -> String { - let uuid = uid / 200000; - let base = "/data/storage/el2/base/"; - format!("/data/app/el2/{}/base/{}/{}", uuid, bundle, path.replace(base, "")) - } - - pub fn open_file_readwrite(uid: u64, bundle: &String, path: &String) -> IpcResult { - match OpenOptions::new().read(true).write(true).append(true) - .open(RequestAbility::get_ability_instance().convert_path(uid, &bundle, &path)) { - Ok(file) => { Ok(file) }, - Err(e) => { - error!(LOG_LABEL, "open_file_readwrite failed, err is {:?}", @public(e)); - Err(IpcStatusCode::Failed) - }, - } - } - - pub fn open_file_readonly(uid: u64, bundle: &String, path: &String) -> IpcResult { - match OpenOptions::new().read(true) - .open(RequestAbility::get_ability_instance().convert_path(uid, &bundle, &path)) { - Ok(file) => { Ok(file) }, - Err(e) => { - error!(LOG_LABEL, "open_file_readonly failed, err is {:?}", @public(e)); - Err(IpcStatusCode::Failed) - }, - } - } -} diff --git a/services/service/rust/src/request_task.rs b/services/service/rust/src/request_task.rs deleted file mode 100644 index 05b6fa3a..00000000 --- a/services/service/rust/src/request_task.rs +++ /dev/null @@ -1,1466 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use std::{ffi::CString, ffi::c_char, fs::File, pin::Pin, thread::sleep, time::Duration, cell::UnsafeCell}; -use super::{ - enumration::*, progress::*, task_info::*, task_config::*, task_manager::*, utils::*, request_binding::*, - log::LOG_LABEL, request_service_ability::*, -}; -use crate::trace::TraceScope; -use crate::sys_event::{SysEvent, build_number_param, build_str_param}; -use hilog_rust::*; -use std::io::{Read, SeekFrom}; -use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering}; -use std::sync::{Arc, Mutex}; -use std::task::{Context, Poll}; - -use ylong_http_client::async_impl::{ - Client, DownloadOperator, Downloader, MultiPart, Part, UploadOperator, Uploader, -}; -use ylong_http_client::{ - Body, Certificate, ErrorKind, HttpClientError, Method, Redirect, Request, RequestBuilder, - Response, SpeedLimit, Timeout, TlsVersion, -}; -use ylong_runtime::fs::File as YlongFile; -use ylong_runtime::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf, AsyncWriteExt, AsyncSeekExt}; - -static CONNECT_TIMEOUT: u64 = 60; -static LOW_SPEED_TIME: u64 = 60; -static LOW_SPEED_LIMIT: u64 = 1; -static SECONDS_IN_ONE_WEEK: u64 = 7 * 24 * 60 * 60; -static FRONT_NOTIFY_INTERVAL: u64 = 1000; -static BACKGROUND_NOTIFY_INTERVAL: u64 = 3000; -static RETRY_INTERVAL: u64 = 20; -#[derive(Clone, Debug)] -pub struct TaskStatus { - pub waitting_network_time: Option, - pub mtime: u64, - pub state: State, - pub reason: Reason, -} - -impl Default for TaskStatus { - fn default() -> Self { - TaskStatus { - waitting_network_time: None, - mtime: get_current_timestamp(), - state: State::CREATED, - reason: Reason::Default, - } - } -} - -struct Files(UnsafeCell>); - -impl Files { - fn get(&self, index: usize) -> Option<&YlongFile> { - unsafe { &*self.0.get() }.get(index) - } -} - -unsafe impl Sync for Files {} -unsafe impl Send for Files {} - -// Need to release file timely. -struct BodyFiles(UnsafeCell>>); -unsafe impl Sync for BodyFiles {} -unsafe impl Send for BodyFiles {} - -pub struct RequestTask { - pub conf: Arc, - pub uid: u64, - pub task_id: u32, - pub ctime: u64, - pub mime_type: Mutex, - pub progress: Mutex, - pub tries: AtomicU32, - pub status: Mutex, - pub retry: AtomicBool, - pub get_file_info: AtomicBool, - pub retry_for_request: AtomicBool, - pub retry_for_speed: AtomicBool, - pub code: Mutex>, - pub background_notify_time: AtomicU64, - pub file_total_size: AtomicI64, - pub resume: AtomicBool, - files: Files, - body_files: BodyFiles, - seek_flag: AtomicBool, - range_request: AtomicBool, - range_response: AtomicBool, - restored: AtomicBool, - skip_bytes: AtomicU64, - upload_counts: AtomicU32, - client: Option, -} - -struct TaskReader { - task: Arc, -} - -struct TaskOperator { - task: Arc, -} - -impl TaskOperator { - fn poll_progress_common(&self, _cx: &mut Context<'_>) -> Poll> { - let state = self.task.status.lock().unwrap().state; - if (state != State::RUNNING && state != State::RETRYING) - || (self.task.conf.version == Version::API10 && !self.task.check_net_work_status()) - { - debug!(LOG_LABEL, "pause the task"); - return Poll::Ready(Err(HttpClientError::user_aborted())); - } - let last_front_notify_time = TaskManager::get_instance().front_notify_time; - let version = self.task.conf.version; - let mode = self.task.conf.common_data.mode; - if get_current_timestamp() - last_front_notify_time >= FRONT_NOTIFY_INTERVAL { - let notify_data = self.task.build_notify_data(); - TaskManager::get_instance().front_notify("progress".into(), ¬ify_data); - } - let gauge = self.task.conf.common_data.gauge; - if version == Version::API9 || gauge { - let last_background_notify_time = - self.task.background_notify_time.load(Ordering::SeqCst); - if get_current_timestamp() - last_background_notify_time >= BACKGROUND_NOTIFY_INTERVAL { - self.task.background_notify(); - } - } - Poll::Ready(Ok(())) - } - - fn poll_write_partial_file( - &self, - cx: &mut Context<'_>, - data: &[u8], - begins: u64, - ends: i64, - ) -> Poll> { - let data_size = data.len(); - let skip_size = self.task.skip_bytes.load(Ordering::SeqCst); - if skip_size + data_size as u64 <= begins { - self.task.skip_bytes.fetch_add(data_size as u64, Ordering::SeqCst); - return Poll::Ready(Ok(data_size)); - } - let remain_skip_bytes = (begins - skip_size) as usize; - let mut data = &data[remain_skip_bytes..]; - self.task.skip_bytes.store(begins, Ordering::SeqCst); - if ends >= 0 { - let total_bytes = ends as u64 - begins + 1; - let written_bytes = self.task.progress.lock().unwrap().processed[0] as u64; - if written_bytes == total_bytes { - return Poll::Ready(Err(HttpClientError::user_aborted())); - } - if data.len() as u64 + written_bytes >= total_bytes { - let remain_bytes = (total_bytes - written_bytes) as usize; - data = &data[..remain_bytes]; - } - } - self.poll_write_file(cx, data, remain_skip_bytes) - } - - fn poll_write_file( - &self, - cx: &mut Context<'_>, - data: &[u8], - skip_size: usize, - ) -> Poll> { - let file = unsafe { &mut *self.task.files.0.get() }.get_mut(0).unwrap(); - let mut progress_guard = self.task.progress.lock().unwrap(); - match Pin::new(file).poll_write(cx, data) { - Poll::Ready(Ok(size)) => { - progress_guard.processed[0] += size; - progress_guard.common_data.total_processed += size; - Poll::Ready(Ok(size + skip_size)) - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(HttpClientError::other(Some(e)))), - } - } -} - -impl DownloadOperator for TaskOperator { - fn poll_download( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - data: &[u8], - ) -> Poll> { - if self.task.range_request.load(Ordering::SeqCst) { - if self.task.range_response.load(Ordering::SeqCst) { - return self.poll_write_file(cx, data, 0); - } - // write partial response data - let begins = self.task.conf.common_data.begins; - let ends = self.task.conf.common_data.ends; - return self.poll_write_partial_file(cx, data, begins, ends); - } - return self.poll_write_file(cx, data, 0); - } - - fn poll_progress( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - _downloaded: u64, - _total: Option, - ) -> Poll> { - self.poll_progress_common(cx) - } -} - -impl UploadOperator for TaskOperator { - fn poll_progress( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - _uploaded: u64, - _total: Option, - ) -> Poll> { - self.poll_progress_common(cx) - } -} - -impl AsyncRead for TaskReader { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - let index = self.task.progress.lock().unwrap().common_data.index; - let file = unsafe { &mut *self.task.files.0.get() } - .get_mut(index) - .unwrap(); - let (is_partial_upload, total_upload_bytes) = self.task.get_upload_info(index); - let mut progress_guard = self.task.progress.lock().unwrap(); - if !is_partial_upload { - let filled_len = buf.filled().len(); - match Pin::new(file).poll_read(cx, buf) { - Poll::Ready(Ok(_)) => { - let current_filled_len = buf.filled().len(); - let upload_size = current_filled_len - filled_len; - progress_guard.processed[index] += upload_size; - progress_guard.common_data.total_processed += upload_size; - return Poll::Ready(Ok(())); - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - } - } else { - let begins = self.task.conf.common_data.begins; - if !self.task.seek_flag.load(Ordering::SeqCst) { - match Pin::new(file).poll_seek(cx,SeekFrom::Start(begins)) { - Poll::Ready(Err(e)) => { - error!(LOG_LABEL, "seek err is {:?}", e); - return Poll::Ready(Err(e)); - } - _ => self.task.seek_flag.store(true, Ordering::SeqCst), - } - } - let buf_filled_len = buf.filled().len(); - let mut read_buf = buf.take(total_upload_bytes as usize); - let filled_len = read_buf.filled().len(); - let file = unsafe { &mut *self.task.files.0.get() } - .get_mut(index) - .unwrap(); - match Pin::new(file).poll_read(cx, &mut read_buf) { - Poll::Ready(Ok(_)) => { - let current_filled_len = read_buf.filled().len(); - let upload_size = current_filled_len - filled_len; - // need update buf.filled and buf.initialized - unsafe { - buf.assume_init(upload_size); - } - buf.set_filled(buf_filled_len + upload_size); - progress_guard.processed[index] += upload_size; - progress_guard.common_data.total_processed += upload_size; - Poll::Ready(Ok(())) - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - } - } - } -} - -impl RequestTask { - pub fn constructor(conf: Arc, uid: u64, task_id: u32, files: Vec, body_files: Vec) -> Self { - let mut sizes: Vec = Vec::::new(); - match conf.common_data.action { - Action::DOWNLOAD => sizes.push(-1), - Action::UPLOAD => { - for f in files.iter() { - let file_size = f.metadata().unwrap().len() as i64; - debug!(LOG_LABEL, "file size size is {}", @public(file_size)); - sizes.push(file_size); - } - } - _ => {}, - } - let file_count = files.len(); - let action = conf.common_data.action; - let mut task = RequestTask { - conf, - uid, - task_id, - ctime: get_current_timestamp(), - files: Files(UnsafeCell::new( - files.into_iter().map(|f| YlongFile::new(f)).collect(), - )), - body_files: BodyFiles(UnsafeCell::new( - body_files.into_iter().map(|f| Some(YlongFile::new(f))).collect(), - )), - mime_type: Mutex::new(String::new()), - progress: Mutex::new(Progress::new(sizes)), - tries: AtomicU32::new(0), - status: Mutex::new(TaskStatus::default()), - retry: AtomicBool::new(false), - get_file_info: AtomicBool::new(false), - retry_for_request: AtomicBool::new(false), - retry_for_speed: AtomicBool::new(false), - code: Mutex::new(vec![Reason::Default; file_count]), - background_notify_time: AtomicU64::new(get_current_timestamp()), - file_total_size: AtomicI64::new(-1), - resume: AtomicBool::new(false), - seek_flag: AtomicBool::new(false), - range_request: AtomicBool::new(false), - range_response: AtomicBool::new(false), - restored: AtomicBool::new(false), - skip_bytes: AtomicU64::new(0), - upload_counts: AtomicU32::new(0), - client: None, - }; - task.client = task.build_client(); - if action == Action::UPLOAD { - task.file_total_size.store(task.get_upload_file_total_size() as i64, Ordering::SeqCst); - } - task - } - - pub fn restore_task(conf: Arc, info: TaskInfo) -> Self { - let progress_index = info.progress.common_data.index; - let uid = info.common_data.uid; - let action = conf.common_data.action; - let files = get_restore_files(&conf, uid); - let body_files = get_restore_body_files(&conf, uid); - let file_count = files.len(); - let mut task = RequestTask { - conf, - uid, - task_id: info.common_data.task_id, - ctime: info.common_data.ctime, - files: Files(UnsafeCell::new( - files.into_iter().map(|f| YlongFile::new(f)).collect(), - )), - body_files: BodyFiles(UnsafeCell::new( - body_files.into_iter().map(|f| Some(YlongFile::new(f))).collect(), - )), - mime_type: Mutex::new(info.mime_type), - progress: Mutex::new(info.progress.clone()), - tries: AtomicU32::new(info.common_data.tries), - status: Mutex::new(TaskStatus { - waitting_network_time: None, - mtime: get_current_timestamp(), - state: State::from(info.progress.common_data.state), - reason: Reason::from(info.common_data.reason), - }), - retry: AtomicBool::new(info.common_data.retry), - get_file_info: AtomicBool::new(false), - retry_for_request: AtomicBool::new(false), - retry_for_speed: AtomicBool::new(false), - code: Mutex::new(vec![Reason::Default; file_count]), - background_notify_time: AtomicU64::new(get_current_timestamp()), - file_total_size: AtomicI64::new(-1), - resume: AtomicBool::new(false), - seek_flag: AtomicBool::new(false), - range_request: AtomicBool::new(false), - range_response: AtomicBool::new(false), - restored: AtomicBool::new(true), - skip_bytes: AtomicU64::new(0), - upload_counts: AtomicU32::new(progress_index as u32), - client: None, - }; - task.client = task.build_client(); - match action { - Action::UPLOAD => - task.file_total_size.store(task.get_upload_file_total_size() as i64, Ordering::SeqCst), - Action::DOWNLOAD => - task.file_total_size.store(info.progress.sizes[progress_index] as i64, Ordering::SeqCst), - _ => {} - } - task - } - - pub fn build_notify_data(&self) -> NotifyData { - let mut vec = Vec::new(); - let size = self.conf.file_specs.len(); - let guard = self.code.lock().unwrap(); - for i in 0..size { - vec.push(EachFileStatus { - path: self.conf.file_specs[i].path.clone(), - reason: guard[i], - message: guard[i].to_str().into(), - }); - } - NotifyData { - progress: self.progress.lock().unwrap().clone(), - action: self.conf.common_data.action, - version: self.conf.version, - each_file_status: vec, - task_id: self.task_id, - uid: self.uid, - bundle: self.conf.bundle.clone(), - } - } - - pub fn record_waitting_network_time(&self) { - let mut staus = self.status.lock().unwrap(); - staus.waitting_network_time = Some(get_current_timestamp()); - } - - pub fn check_net_work_status(&self) -> bool { - if !self.is_satisfied_configuration() { - if self.conf.version == Version::API10 - && self.conf.common_data.mode == Mode::BACKGROUND - && self.conf.common_data.retry - { - self.set_status(State::WAITING, Reason::UnSupportedNetWorkType); - } else { - self.set_status(State::FAILED, Reason::UnSupportedNetWorkType); - } - return false; - } - true - } - - pub fn net_work_online(&self) -> bool { - if unsafe { !IsOnline() } { - if self.conf.version == Version::API10 - && self.conf.common_data.mode == Mode::BACKGROUND - && self.conf.common_data.retry - { - self.set_status(State::WAITING, Reason::NetWorkOffline); - } else { - let retry_times = 20; - for _ in 0..retry_times { - if unsafe { IsOnline() } { - return true; - } - sleep(Duration::from_millis(RETRY_INTERVAL)); - } - self.set_status(State::FAILED, Reason::NetWorkOffline); - } - return false; - } - true - } - - fn build_client(&self) -> Option { - let mut client = Client::builder() - .connect_timeout(Timeout::from_secs(CONNECT_TIMEOUT)) - .request_timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK)) - .min_tls_version(TlsVersion::TLS_1_2); - - if self.conf.common_data.redirect { - client = client.redirect(Redirect::limited(usize::MAX)); - } else { - client = client.redirect(Redirect::none()); - } - - if self.conf.url.contains("https") { - let mut buf = Vec::new(); - let file = File::open("/etc/ssl/certs/cacert.pem"); - match file { - Ok(mut f) => { - f.read_to_end(&mut buf).unwrap(); - let cert = Certificate::from_pem(&buf).unwrap(); - client = client.add_root_certificate(cert); - } - Err(e) => { - error!(LOG_LABEL, "open cacert.pem failed, error is {:?}", @public(e)); - self.set_status(State::FAILED, Reason::IoError); - return None; - } - } - } - let result = client.build(); - match result { - Ok(value) => Some(value), - Err(e) => { - error!(LOG_LABEL, "build client error is {:?}", @public(e)); - self.set_status(State::FAILED, Reason::BuildClientFailed); - return None; - } - } - } - - fn build_request_builder(&self) -> RequestBuilder { - let url = self.conf.url.clone(); - let method = match self.conf.method.to_uppercase().as_str() { - "PUT" => "PUT", - "POST" => "POST", - "GET" => "GET", - _ => match self.conf.common_data.action { - Action::UPLOAD => { - if self.conf.version == Version::API10 { - "PUT" - } else { - "POST" - } - } - Action::DOWNLOAD => "GET", - _ => "", - }, - }; - let method = Method::try_from(method).unwrap(); - let mut request = RequestBuilder::new().method(method).url(url.as_str()); - for (key, value) in self.conf.headers.iter() { - request = request.header(key.as_str(), value.as_str()); - } - request - } - - async fn clear_downloaded_file(&self) -> bool { - let file = unsafe { &mut *self.files.0.get() }.get_mut(0).unwrap(); - let res = file.set_len(0).await; - match res { - Err(e) => { - error!(LOG_LABEL, "clear download file error: {:?}", e); - self.set_status(State::FAILED, Reason::IoError); - false - } - _ => { - debug!(LOG_LABEL, "set len success"); - match file.seek(SeekFrom::Start(0)).await{ - Err(e) => { - error!(LOG_LABEL, "seek err is {:?}", e); - self.set_status(State::FAILED, Reason::IoError); - false - } - Ok(_) => { - debug!(LOG_LABEL, "seek success"); - let mut progress_guard = self.progress.lock().unwrap(); - progress_guard.common_data.total_processed = 0; - progress_guard.processed[0] = 0; - true - } - } - } - } - } - - async fn build_download_request(&self) -> Option> { - let mut request_builder = self.build_request_builder(); - let mut begins = self.conf.common_data.begins; - let ends = self.conf.common_data.ends; - self.range_response.store(false, Ordering::SeqCst); - if self.resume.load(Ordering::SeqCst) || begins > 0 || ends >= 0 { - self.range_request.store(true, Ordering::SeqCst); - self.skip_bytes.store(0, Ordering::SeqCst); - if self.resume.load(Ordering::SeqCst) { - let if_range = { - let progress_guard = self.progress.lock().unwrap(); - let etag = progress_guard.extras.get("etag"); - let last_modified = progress_guard.extras.get("last-modified"); - if etag.is_some() { - request_builder = request_builder.header("If-Range", etag.unwrap().as_str()); - true - } else if last_modified.is_some() { - request_builder = request_builder.header("If-Range", last_modified.unwrap().as_str()); - true - } else { - false - } - }; - if !if_range { - // unable to verify file consistency, need download again - if begins == 0 && ends < 0 { - self.range_request.store(false, Ordering::SeqCst); - } - if !self.clear_downloaded_file().await { - return None; - } - } - } - begins += self.progress.lock().unwrap().processed[0] as u64; - if self.range_request.load(Ordering::SeqCst) { - let range = if ends < 0 { - format!("bytes={begins}-") - } else { - format!("bytes={begins}-{ends}") - }; - request_builder = request_builder.header("Range", range.as_str()); - } - } else { - self.range_request.store(false, Ordering::SeqCst); - } - let result = request_builder.body(self.conf.data.clone()); - match result { - Ok(value) => { - return Some(value); - } - Err(e) => { - error!(LOG_LABEL, "build download request error is {:?}", @public(e)); - self.set_status(State::FAILED, Reason::BuildRequestFailed); - return None; - } - } - } - - fn get_file_info(&self, response: &Response) -> bool { - if self.get_file_info.load(Ordering::SeqCst) { - return true; - } - self.get_file_info.store(true, Ordering::SeqCst); - let content_type = response.headers().get("content-type"); - if let Some(mime_type) = content_type { - if let Ok(value) = mime_type.to_str() { - *self.mime_type.lock().unwrap() = value.into(); - } - } - - let content_length = response.headers().get("content-length"); - if let Some(len) = content_length { - let length = len.to_str(); - match length { - Ok(value) => { - let len = value.parse::(); - match len { - Ok(v) => { - let mut guard = self.progress.lock().unwrap(); - if !self.restored.load(Ordering::SeqCst) { - guard.sizes[0] = v; - } - self.file_total_size.store(v, Ordering::SeqCst); - debug!(LOG_LABEL, "the download task content-length is {}", @public(v)); - } - Err(e) => { error!(LOG_LABEL, "convert string to i64 error: {:?}", @public(e)); }, - } - } - Err(e) => { error!(LOG_LABEL, "convert header value to string error: {:?}", @public(e)); }, - } - } else { - error!(LOG_LABEL, "cannot get content-length of the task"); - if self.conf.common_data.precise { - self.set_status(State::FAILED, Reason::GetFileSizeFailed); - return false; - } - } - true - } - - fn handle_body_transfer_error(&self) { - if unsafe { !IsOnline() } { - match self.conf.version { - Version::API9 => { - if self.conf.common_data.action == Action::DOWNLOAD { - self.set_status(State::WAITING, Reason::NetWorkOffline); - } else { - self.set_status(State::FAILED, Reason::NetWorkOffline); - } - } - Version::API10 => { - if self.conf.common_data.mode == Mode::FRONTEND || !self.conf.common_data.retry - { - self.set_status(State::FAILED, Reason::NetWorkOffline); - } else { - self.set_status(State::WAITING, Reason::NetWorkOffline); - } - } - } - } else { - let index = self.progress.lock().unwrap().common_data.index; - self.set_code(index, Reason::OthersError); - } - } - - fn handle_download_error(&self, result: &Result<(), HttpClientError>) -> bool { - match result { - Ok(_) => return true, - Err(err) => { - error!(LOG_LABEL, "download err is {:?}", @public(err)); - match err.error_kind() { - ErrorKind::Timeout => { - self.set_status(State::FAILED, Reason::ContinuousTaskTimeOut); - } - // user triggered - ErrorKind::UserAborted => return true, - ErrorKind::BodyTransfer => self.handle_body_transfer_error(), - _ => { - self.set_status(State::FAILED, Reason::OthersError); - } - } - return false; - } - } - } - - async fn handle_response_error(&self, response: &Result) -> bool { - let index = self.progress.lock().unwrap().common_data.index; - match response { - Ok(r) => { - let http_response_code = r.status(); - info!(LOG_LABEL, "the http response code is {}", @public(http_response_code)); - if http_response_code.is_server_error() - || (http_response_code.as_u16() != 408 - && http_response_code.is_client_error()) - || http_response_code.is_redirection() - { - self.set_code(index, Reason::ProtocolError); - return false; - } - if http_response_code.as_u16() == 408 { - if !self.retry_for_request.load(Ordering::SeqCst) { - self.retry_for_request.store(true, Ordering::SeqCst); - } else { - self.set_code(index, Reason::ProtocolError); - } - return false; - } - if self.range_request.load(Ordering::SeqCst) { - match http_response_code.as_u16() { - 206 => { self.range_response.store(true, Ordering::SeqCst); }, - 200 => { - self.range_response.store(false, Ordering::SeqCst); - if self.resume.load(Ordering::SeqCst) { - if !self.clear_downloaded_file().await { - return false; - } - } else { - self.set_code(index, Reason::UnSupportRangeRequest); - return false; - } - }, - _ => {}, - } - } - return true; - } - Err(e) => { - error!(LOG_LABEL, "http client err is {:?}", @public(e)); - match e.error_kind() { - ErrorKind::UserAborted => self.set_code(index, Reason::UserOperation), - ErrorKind::Timeout => self.set_code(index, Reason::ContinuousTaskTimeOut), - ErrorKind::Request => self.set_code(index, Reason::RequestError), - ErrorKind::Redirect => self.set_code(index, Reason::RedirectError), - ErrorKind::Connect | ErrorKind::ConnectionUpgrade => self.set_code(index, Reason::ConnectError), - ErrorKind::BodyTransfer => self.handle_body_transfer_error(), - _ => self.set_code(index, Reason::OthersError), - } - return false; - } - } - } - - fn record_response_header(&self, response: &Result) { - if let Ok(r) = response { - let mut guard = self.progress.lock().unwrap(); - guard.extras.clear(); - for (k, v) in r.headers() { - if let Ok(value) = v.to_str() { - guard.extras.insert(k.to_string().to_lowercase(), value.into()); - } - } - } - } - - async fn record_upload_response( - &self, - index: usize, - response: Result, - ) { - self.record_response_header(&response); - if let Ok(mut r) = response { - let mut yfile = match unsafe { &mut *self.body_files.0.get() }.get_mut(index) { - Some(yfile) => match yfile.take() { - Some(yf) => yf, - None => return, - }, - None => return, - }; - - loop { - let mut buf = [0u8; 1024]; - let size = r.body_mut().data(&mut buf).await; - let size = match size { - Ok(size) => size, - Err(_e) => break, - }; - - if size == 0 { - break; - } - let r = yfile.write_all(&buf[..size]).await; - } - // Makes sure all the data has been written to the target file. - let _ = yfile.sync_all().await; - } - if self.conf.version == Version::API9 && self.conf.common_data.action == Action::UPLOAD { - let notify_data = self.build_notify_data(); - TaskManager::get_instance().front_notify("headerReceive".into(), ¬ify_data); - } - } - - fn set_code(&self, index: usize, code: Reason) { - if code == Reason::UploadFileError { - return; - } - let mut code_guard = self.code.lock().unwrap(); - if index < code_guard.len() { - if code_guard[index] == Reason::Default { - debug!(LOG_LABEL, "set code"); - code_guard[index] = code; - } - } - } - - fn reset_code(&self, index: usize) { - let file_counts = self.conf.file_specs.len(); - let mut code_guard = self.code.lock().unwrap(); - if index < file_counts { - debug!(LOG_LABEL, "reset code"); - code_guard[index] = Reason::Default; - } - } - - pub fn set_status(&self, state: State, reason: Reason) -> bool { - debug!(LOG_LABEL, "set status"); - { - let mut current_status = self.status.lock().unwrap(); - if state == current_status.state && reason == current_status.reason { - return true; - } - let mut progress_guard = self.progress.lock().unwrap(); - let index = progress_guard.common_data.index; - let current_state = current_status.state; - debug!(LOG_LABEL, "set state {:?}, reason {:?} current_state {:?}", - @public(state), @public(reason), @public(current_state)); - match state { - State::PAUSED | State::STOPPED => { - if current_state != State::RUNNING - && current_state != State::RETRYING - && current_state != State::WAITING - { - return false; - } - self.set_code(index, reason); - } - State::COMPLETED => { - if current_state != State::RUNNING && current_state != State::RETRYING { - return false; - } - } - State::FAILED | State::WAITING => { - if current_state == State::COMPLETED || current_state == State::REMOVED - || current_state == State::STOPPED || current_state == State::FAILED - { - return false; - } - self.set_code(index, reason); - if state == State::FAILED { - let file_counts = self.conf.file_specs.len(); - for i in index..file_counts { - self.set_code(i, reason); - } - } - } - State::REMOVED => self.set_code(index, reason), - _ => {} - } - current_status.mtime = get_current_timestamp(); - progress_guard.common_data.state = state as u8; - current_status.state = state; - current_status.reason = reason; - info!(LOG_LABEL, "current state is {:?}, reason is {:?}", @public(state), @public(reason)); - } - if state == State::WAITING { - self.record_waitting_network_time(); - } - self.record_task_info(); - self.state_change_notify(state); - true - } - - fn state_change_notify(&self, state: State) { - if state == State::INITIALIZED - || (self.progress.lock().unwrap().common_data.total_processed == 0 - && (state == State::RUNNING || state == State::RETRYING)) - { - return; - } - debug!(LOG_LABEL, "state change notification"); - let version = self.conf.version; - let mode = self.conf.common_data.mode; - let notify_data = self.build_notify_data(); - let bundle = self.conf.bundle.clone(); - TaskManager::get_instance().front_notify("progress".into(), ¬ify_data); - match state { - State::COMPLETED => { - unsafe { PublishStateChangeEvents(CString::new(bundle.as_str()).unwrap().as_ptr(), bundle.len() as u32, self.task_id, State::COMPLETED as i32); } - TaskManager::get_instance().front_notify("complete".into(), ¬ify_data) - } - State::FAILED => { - unsafe { PublishStateChangeEvents(CString::new(bundle.as_str()).unwrap().as_ptr(), bundle.len() as u32, self.task_id, State::FAILED as i32); } - TaskManager::get_instance().front_notify("fail".into(), ¬ify_data) - } - State::PAUSED | State::WAITING => { - TaskManager::get_instance().front_notify("pause".into(), ¬ify_data) - } - State::REMOVED => { - TaskManager::get_instance().front_notify("remove".into(), ¬ify_data) - } - _ => {} - } - self.background_notify(); - } - - fn record_task_info(&self) { - TaskManager::get_instance().recording_rdb_num.fetch_add(1, Ordering::SeqCst); - let has_record = unsafe { HasRequestTaskRecord(self.task_id) }; - if !has_record { - let task_info = self.show(); - let info_set = task_info.build_info_set(); - let c_task_info = task_info.to_c_struct(&info_set); - let ret = unsafe { RecordRequestTaskInfo(&c_task_info) }; - info!(LOG_LABEL, "insert database ret is {}", @public(ret)); - } else { - let update_info = self.get_update_info(); - let sizes: String = format!("{:?}", update_info.progress.sizes); - let processed: String = format!("{:?}", update_info.progress.processed); - let extras = hashmap_to_string(&update_info.progress.extras); - let each_file_status = update_info.each_file_status.iter().map(|x| x.to_c_struct()).collect(); - let c_update_info = update_info.to_c_struct(&sizes, &processed, &extras, &each_file_status); - let ret = unsafe { UpdateRequestTaskInfo(self.task_id, &c_update_info)}; - info!(LOG_LABEL, "update database ret is {}", @public(ret)); - } - TaskManager::get_instance().recording_rdb_num.fetch_sub(1, Ordering::SeqCst); - } - - fn get_each_file_status(&self) -> Vec { - let mut vec = Vec::new(); - let size = self.conf.file_specs.len(); - let guard = self.code.lock().unwrap(); - for i in 0..size { - vec.push(EachFileStatus { - path: self.conf.file_specs[i].path.clone(), - reason: guard[i], - message: guard[i].to_str().into(), - }); - } - vec - } - - fn get_update_info(&self) -> UpdateInfo{ - let status = self.status.lock().unwrap(); - let progress = self.progress.lock().unwrap(); - UpdateInfo { - mtime: status.mtime, - reason: status.reason as u8, - tries: self.tries.load(Ordering::SeqCst), - progress: progress.clone(), - each_file_status: self.get_each_file_status(), - } - } - - pub fn show(&self) -> TaskInfo { - let status = self.status.lock().unwrap(); - let progress = self.progress.lock().unwrap(); - TaskInfo { - bundle: self.conf.bundle.clone(), - url: self.conf.url.clone(), - data: self.conf.data.clone(), - token: self.conf.token.clone(), - form_items: self.conf.form_items.clone(), - file_specs: self.conf.file_specs.clone(), - title: self.conf.title.clone(), - description: self.conf.description.clone(), - mime_type: { - match self.conf.version { - Version::API10 => match self.conf.common_data.action { - Action::DOWNLOAD => match self.conf.headers.get("Content-Type") { - None => "".into(), - Some(v) => v.clone(), - }, - Action::UPLOAD => "multipart/form-data".into(), - _ => "".into(), - }, - Version::API9 => self.mime_type.lock().unwrap().clone(), - } - }, - progress: progress.clone(), - extras: progress.extras.clone(), - each_file_status: self.get_each_file_status(), - common_data: CommonTaskInfo { - task_id: self.task_id, - uid: self.uid, - action: self.conf.common_data.action as u8, - mode: self.conf.common_data.mode as u8, - ctime: self.ctime, - mtime: status.mtime, - reason: status.reason as u8, - gauge: self.conf.common_data.gauge, - retry: match self.conf.common_data.mode { - Mode::FRONTEND => false, - _ => self.conf.common_data.retry, - }, - tries: self.tries.load(Ordering::SeqCst), - version: self.conf.version as u8, - }, - } - } - - // only use for download task - pub fn query_mime_type(&self) -> String { - self.mime_type.lock().unwrap().clone() - } - - pub fn is_satisfied_configuration(&self) -> bool { - if self.conf.common_data.network == Network::ANY { - return true; - } - unsafe { - let network_info = GetNetworkInfo(); - if (!self.conf.common_data.roaming && (*network_info).isRoaming) { - error!(LOG_LABEL, "not allow roaming"); - return false; - } - if (!self.conf.common_data.metered && (*network_info).isMetered) { - error!(LOG_LABEL, "not allow metered"); - return false; - } - if ((*network_info).networkType != self.conf.common_data.network) { - error!(LOG_LABEL, "dismatch network type"); - return false; - } - }; - true - } - - fn background_notify(&self) { - if self.conf.version == Version::API9 && !self.conf.common_data.background { - return; - } - if self.conf.version == Version::API10 && self.conf.common_data.mode == Mode::FRONTEND { - return; - } - let mut file_total_size = self.file_total_size.load(Ordering::SeqCst); - let total_processed = self.progress.lock().unwrap().common_data.total_processed as u64; - if file_total_size <= 0 || total_processed == 0 { - return; - } - if self.conf.common_data.action == Action::DOWNLOAD { - if self.conf.common_data.ends < 0 { - file_total_size -= self.conf.common_data.begins as i64; - } else { - file_total_size = self.conf.common_data.ends - self.conf.common_data.begins as i64 + 1; - } - } - self.background_notify_time.store(get_current_timestamp(), Ordering::SeqCst); - let index = self.progress.lock().unwrap().common_data.index; - if index >= self.conf.file_specs.len() { - return; - } - let file_path = self.conf.file_specs[index].path.as_ptr() as *const c_char; - let file_path_len = self.conf.file_specs[index].path.as_bytes().len() as i32; - let percent = total_processed * 100 / (file_total_size as u64); - debug!(LOG_LABEL, "background notify"); - let task_msg = RequestTaskMsg { - task_id: self.task_id, - uid: self.uid as i32, - action: self.conf.common_data.action as u8, - }; - unsafe { - RequestBackgroundNotify( - task_msg, - file_path, - file_path_len, - percent as u32, - ); - }; - } - - fn get_upload_info(&self, index: usize) -> (bool, u64) { - let guard = self.progress.lock().unwrap(); - let file_size = guard.sizes[index]; - let mut is_partial_upload = false; - let mut upload_file_length: u64 = file_size as u64 - guard.processed[index] as u64; - if file_size == 0 { - return (is_partial_upload, upload_file_length); - } - if index as u32 != self.conf.common_data.index { - return (is_partial_upload, upload_file_length); - } - let begins = self.conf.common_data.begins; - let mut ends = self.conf.common_data.ends; - if ends < 0 || ends >= file_size { - ends = file_size - 1; - } - if begins >= file_size as u64 || begins > ends as u64 { - return (is_partial_upload, upload_file_length); - } - is_partial_upload = true; - upload_file_length = ends as u64 - begins + 1 - guard.processed[index] as u64; - return (is_partial_upload, upload_file_length); - } - - fn get_upload_file_total_size(&self) -> u64 { - let mut file_total_size = 0; - for i in 0..self.conf.file_specs.len() { - let (_, upload_size) = self.get_upload_info(i); - file_total_size += upload_size; - } - file_total_size - } -} - -pub async fn run(task: Arc) { - info!(LOG_LABEL, "run the task which id is {}", @public(task.task_id)); - if !task.net_work_online() || !task.check_net_work_status() { - return; - } - let action = task.conf.common_data.action; - match action { - Action::DOWNLOAD => loop { - task.reset_code(0); - download(task.clone()).await; - let state = task.status.lock().unwrap().state; - if state != State::RUNNING && state != State::RETRYING { - break; - } - let code = task.code.lock().unwrap()[0]; - if code != Reason::Default { - task.set_status(State::FAILED, code); - break; - } - }, - Action::UPLOAD => { - let state = task.status.lock().unwrap().state; - if state == State::RETRYING { - let index = { - let mut progress_guard = task.progress.lock().unwrap(); - let index = progress_guard.common_data.index; - progress_guard.common_data.total_processed -= progress_guard.processed[index]; - progress_guard.processed[index] = 0; - index - }; - let file = unsafe { &mut *task.files.0.get() }.get_mut(index).unwrap(); - let mut begins = task.conf.common_data.begins; - let (is_partial_upload, _) = task.get_upload_info(index); - if !is_partial_upload { - begins = 0; - } - match file.seek(SeekFrom::Start(begins)).await { - Err(e) => { - task.set_code(index, Reason::IoError); - error!(LOG_LABEL, "seek err is {:?}", e); - } - Ok(_) => {} - } - } - upload(task.clone()).await; - } - _ => {}, - } - info!(LOG_LABEL, "run end"); -} - -async fn download(task: Arc) { - download_inner(task.clone()).await; - - // If `Reason` is not `Default`, records this sys event. - let reason = task.code.lock().unwrap()[0]; - if reason != Reason::Default { - SysEvent::task_fault() - .param(build_str_param!(SysEvent::TASKS_TYPE, "DOWNLOAD")) - .param(build_number_param!(SysEvent::TOTAL_FILE_NUM, 1)) - .param(build_number_param!(SysEvent::FAIL_FILE_NUM, 1)) - .param(build_number_param!(SysEvent::SUCCESS_FILE_NUM, 0)) - .param(build_number_param!(SysEvent::ERROR_INFO, reason as i32)) - .write(); - } -} - -async fn download_inner(task: Arc) { - info!(LOG_LABEL, "begin download"); - - // Ensures `_trace` can only be freed when this function exits. - let _trace = TraceScope::trace("download file"); - - if task.client.is_none() { - return; - } - let request = task.build_download_request().await; - if request.is_none() { - return; - } - let request = request.unwrap(); - - let name = task.conf.file_specs[0].path.as_str(); - let download = task.progress.lock().unwrap().processed[0]; - // Ensures `_trace` can only be freed when this function exits. - let _trace = TraceScope::trace( - &format!("download file name: {name} downloaded size: {download}") - ); - - let response = task.client.as_ref().unwrap().request(request).await; - task.record_response_header(&response); - if !task.handle_response_error(&response).await { - error!(LOG_LABEL, "response error"); - return; - } - let response = response.unwrap(); - if !task.get_file_info(&response) { - return; - } - let mut downloader = build_downloader(task.clone(), response); - let result = downloader.download().await; - if !task.handle_download_error(&result) { - error!(LOG_LABEL, "handle_download_error"); - return; - } - // Makes sure all the data has been written to the target file. - if let Some(file) = task.files.get(0) { - let _ = file.sync_all().await; - } - task.set_status(State::COMPLETED, Reason::Default); -} - -fn build_downloader(task: Arc, response: Response) -> Downloader { - let task_operator = TaskOperator { task }; - let downloader = Downloader::builder() - .body(response) - .operator(task_operator) - .timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK)) - .speed_limit(SpeedLimit::new().min_speed(LOW_SPEED_LIMIT, LOW_SPEED_TIME)) - .build(); - downloader -} - -async fn upload(task: Arc) { - info!(LOG_LABEL, "begin upload"); - - let url = task.conf.url.as_str(); - let num = task.conf.file_specs.len(); - // Ensures `_trace` can only be freed when this function exits. - let _trace = TraceScope::trace(&format!("exec upload task url: {url} file num: {num}")); - - let size = task.conf.file_specs.len(); - if task.client.is_none() { - return; - } - let index = task.progress.lock().unwrap().common_data.index; - info!(LOG_LABEL, "index is {}", @public(index)); - for i in index..size { - task.progress.lock().unwrap().common_data.index = i; - let result: bool; - match task.conf.headers.get("Content-Type") { - None => { - if task.conf.method.to_uppercase().eq("POST") { - result = upload_one_file(task.clone(), i, build_multipart_request).await; - } else { - result = upload_one_file(task.clone(), i, build_stream_request).await; - } - } - Some(v) => { - if v == "multipart/form-data" { - result = upload_one_file(task.clone(), i, build_multipart_request).await; - } else { - result = upload_one_file(task.clone(), i, build_stream_request).await; - } - } - } - if result { - info!(LOG_LABEL, "upload one file success, which index is {}", @public(i)); - task.upload_counts.fetch_add(1, Ordering::SeqCst); - } - let state = task.status.lock().unwrap().state; - if state != State::RUNNING && state != State::RETRYING { - return; - } - } - - let uploaded = task.upload_counts.load(Ordering::SeqCst); - if uploaded == size as u32 { - task.set_status(State::COMPLETED, Reason::Default); - } else { - task.set_status(State::FAILED, Reason::UploadFileError); - - // Records sys event. - SysEvent::task_fault() - .param(build_str_param!(SysEvent::TASKS_TYPE, "UPLOAD")) - .param(build_number_param!(SysEvent::TOTAL_FILE_NUM, size)) - .param(build_number_param!(SysEvent::FAIL_FILE_NUM, size as u32 - uploaded)) - .param(build_number_param!(SysEvent::SUCCESS_FILE_NUM, uploaded)) - .param(build_number_param!(SysEvent::ERROR_INFO, Reason::UploadFileError as i32)) - .write(); - } - - info!(LOG_LABEL, "upload end"); -} - -async fn upload_one_file( - task: Arc, - index: usize, - build_upload_request: F, -) -> bool -where - F: Fn(Arc, usize) -> Option>, - T: Body, -{ - info!(LOG_LABEL, "begin upload one file"); - - let (_, size) = task.get_upload_info(index); - let name = task.conf.file_specs[index].file_name.as_str(); - // Ensures `_trace` can only be freed when this function exits. - let _trace = TraceScope::trace(&format!("upload file name:{name} index:{index} size:{size}")); - - loop { - task.reset_code(index); - let request = build_upload_request(task.clone(), index); - if request.is_none() { - return false; - } - let response = task.client.as_ref().unwrap().request(request.unwrap()).await; - if task.handle_response_error(&response).await { - task.code.lock().unwrap()[index] = Reason::Default; - task.record_upload_response(index, response).await; - return true; - } - task.record_upload_response(index, response).await; - let code = task.code.lock().unwrap()[index]; - if code != Reason::Default { - error!(LOG_LABEL, "upload {} file fail, which reason is {}", @public(index), @public(code as u32)); - return false; - } - let state = task.status.lock().unwrap().state; - if state != State::RUNNING && state != State::RETRYING { - return false; - } - } -} - -fn build_stream_request( - task: Arc, - index: usize, -) -> Option>> { - info!(LOG_LABEL, "build stream request"); - let task_reader = TaskReader { task: task.clone() }; - let task_operator = TaskOperator { task: task.clone() }; - let mut request_builder = task.build_request_builder(); - if task.conf.headers.get("Content-Type").is_none() { - request_builder = request_builder.header("Content-Type", "application/octet-stream"); - } - let (_, upload_length) = task.get_upload_info(index); - info!(LOG_LABEL, "upload length is {}", @public(upload_length)); - request_builder = request_builder.header("Content-Length", upload_length.to_string().as_str()); - let uploader = Uploader::builder() - .reader(task_reader) - .operator(task_operator) - .total_bytes(Some(upload_length as u64)) - .build(); - let request = request_builder.body(uploader); - build_request_common(&task, index, request) -} - -fn build_multipart_request( - task: Arc, - index: usize, -) -> Option>> { - info!(LOG_LABEL, "build multipart request"); - let task_reader = TaskReader { task: task.clone() }; - let task_operator = TaskOperator { task: task.clone() }; - let mut multi_part = MultiPart::new(); - for item in task.conf.form_items.iter() { - let part = Part::new() - .name(item.name.as_str()) - .body(item.value.as_str()); - multi_part = multi_part.part(part); - } - let (_, upload_length) = task.get_upload_info(index); - info!(LOG_LABEL, "upload length is {}", @public(upload_length)); - let part = Part::new() - .name(task.conf.file_specs[index].name.as_str()) - .file_name(task.conf.file_specs[index].file_name.as_str()) - .mime(task.conf.file_specs[index].mime_type.as_str()) - .length(Some(upload_length)) - .stream(task_reader); - - multi_part = multi_part.part(part); - let uploader = Uploader::builder() - .multipart(multi_part) - .operator(task_operator) - .build(); - - let request_builder = task.build_request_builder(); - let request: Result>, HttpClientError> = - request_builder.multipart(uploader); - build_request_common(&task, index, request) -} - -fn build_request_common( - task: &Arc, - index: usize, - request: Result, HttpClientError>, -) -> Option> { - match request { - Ok(value) => { - info!(LOG_LABEL, "build upload request success"); - return Some(value); - } - Err(e) => { - error!(LOG_LABEL, "build upload request error is {:?}", @public(e)); - { - let mut guard = task.code.lock().unwrap(); - for i in index..guard.len() { - guard[i] = Reason::BuildRequestFailed; - } - } - task.set_status(State::FAILED, Reason::BuildRequestFailed); - return None; - } - } -} - -fn get_restore_files(conf: &Arc, uid: u64) -> Vec { - let mut files: Vec = Vec::new(); - for fs in &conf.file_specs { - if conf.common_data.action == Action::UPLOAD { - match RequestAbility::open_file_readonly(uid, &conf.bundle, &fs.path) { - Ok(file) => files.push(file), - Err(e) => { - error!(LOG_LABEL, "open file RO failed, err is {:?}", @public(e)); - }, - } - } else { - match RequestAbility::open_file_readwrite(uid, &conf.bundle, &fs.path) { - Ok(file) => files.push(file), - Err(e) => { - error!(LOG_LABEL, "open file RW failed, err is {:?}", @public(e)); - }, - } - } - } - files -} - -fn get_restore_body_files(conf: &Arc, uid: u64) -> Vec { - let mut body_files: Vec = Vec::new(); - for name in &conf.body_file_names { - match RequestAbility::open_file_readwrite(uid, &conf.bundle, &name) { - Ok(body_file) => body_files.push(body_file), - Err(e) => { - error!(LOG_LABEL, "open body_file failed, err is {:?}", @public(e)); - }, - } - } - body_files -} diff --git a/services/service/rust/src/sys_event.rs b/services/service/rust/src/sys_event.rs deleted file mode 100644 index 7a45ec8e..00000000 --- a/services/service/rust/src/sys_event.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -extern crate hisysevent; - -use hisysevent::{EventType, HiSysEventParam, write}; -pub(crate) use hisysevent::{build_str_param, build_number_param, build_bool_param}; - -/// System events structure which base on `Hisysevent`. -pub(crate) struct SysEvent<'a> { - event_kind: EventKind, - inner_type: EventType, - params: Vec> -} - -impl<'a> SysEvent<'a> { - const DOMAIN: &str = "REQUEST"; - - pub(crate) const ERROR_INFO: &str = "ERROR_INFO"; - pub(crate) const TASKS_TYPE: &str = "TASKS_TYPE"; - pub(crate) const TOTAL_FILE_NUM: &str = "TOTAL_FILE_NUM"; - pub(crate) const FAIL_FILE_NUM: &str = "FAIL_FILE_NUM"; - pub(crate) const SUCCESS_FILE_NUM: &str = "SUCCESS_FILE_NUM"; - - pub(crate) fn task_fault() -> Self { - Self { - event_kind: EventKind::TaskFault, - inner_type: EventType::Fault, - params: Vec::new(), - } - } - - pub(crate) fn task_info_statistics() -> Self { - Self { - event_kind: EventKind::TaskInfoStatistics, - inner_type: EventType::Statistic, - params: Vec::new(), - } - } - - pub(crate) fn param(mut self, param: HiSysEventParam<'a>) -> Self { - self.params.push(param); - self - } - - pub(crate) fn write(self) { - write( - Self::DOMAIN, - self.event_kind.as_str(), - self.inner_type, - self.params.as_slice(), - ); - } -} - -enum EventKind { - TaskFault, - TaskInfoStatistics, -} - -impl EventKind { - const TASK_FAULT: &str = "TASK_FAULT"; - const TASK_INFO_STATISTICS: &str = "TASK_INFO_STATISTICS"; - - fn as_str(&self) -> &str { - match self { - EventKind::TaskFault => Self::TASK_FAULT, - EventKind::TaskInfoStatistics => Self::TASK_INFO_STATISTICS, - } - } -} \ No newline at end of file diff --git a/services/service/rust/src/task_config.rs b/services/service/rust/src/task_config.rs deleted file mode 100644 index baa2174b..00000000 --- a/services/service/rust/src/task_config.rs +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use super::{c_string_wrapper::*, enumration::*, form_item::*, utils::*, request_binding::*}; -use std::collections::HashMap; - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct CommonTaskConfig { - pub task_id: u32, - pub uid: u64, - pub action: Action, - pub mode: Mode, - pub cover: bool, - pub network: Network, - pub metered: bool, - pub roaming: bool, - pub retry: bool, - pub redirect: bool, - pub index: u32, - pub begins: u64, - pub ends: i64, - pub gauge: bool, - pub precise: bool, - pub background: bool, -} - -#[derive(Debug)] -pub struct TaskConfig { - pub bundle: String, - pub url: String, - pub title: String, - pub description: String, - pub method: String, - pub headers: HashMap, - pub data: String, - pub token: String, - pub extras: HashMap, - pub version: Version, - pub form_items: Vec, - pub file_specs: Vec, - pub body_file_names: Vec, - pub common_data: CommonTaskConfig, -} - -#[repr(C)] -pub struct CommonCTaskConfig { - pub task_id: u32, - pub uid: u64, - pub action: u8, - pub mode: u8, - pub cover: bool, - pub network: u8, - pub metered: bool, - pub roaming: bool, - pub retry: bool, - pub redirect: bool, - pub index: u32, - pub begins: u64, - pub ends: i64, - pub gauge: bool, - pub precise: bool, - pub background: bool, -} - -#[repr(C)] -pub struct CTaskConfig { - pub bundle: CStringWrapper, - pub url: CStringWrapper, - pub title: CStringWrapper, - pub description: CStringWrapper, - pub method: CStringWrapper, - pub headers: CStringWrapper, - pub data: CStringWrapper, - pub token: CStringWrapper, - pub extras: CStringWrapper, - pub version: u8, - pub form_items_ptr: *const CFormItem, - pub form_items_len: u32, - pub file_specs_ptr: *const CFileSpec, - pub file_specs_len: u32, - pub body_file_names_ptr: *const CStringWrapper, - pub body_file_names_len: u32, - pub common_data: CommonCTaskConfig, -} - -impl TaskConfig { - fn build_vec(ptr: *const A, len: usize, func: C) -> Vec where C: Fn(&A) -> B, - { - if ptr.is_null() || len == 0 { - return Vec::::new(); - } - let slice = unsafe { std::slice::from_raw_parts(ptr, len) }; - slice.iter().map(|x| func(x)).collect() - } - - pub fn to_c_struct(&self, task_id: u32, uid: u64) -> CTaskConfig { - let form_items: Vec = self.form_items.iter().map(|x| x.to_c_struct()).collect(); - let file_specs: Vec = self.file_specs.iter().map(|x| x.to_c_struct()).collect(); - let body_file_names: Vec = self.body_file_names.iter() - .map(|x| CStringWrapper::from(x)).collect(); - CTaskConfig { - bundle: CStringWrapper::from(&self.bundle), - url: CStringWrapper::from(&self.url), - title: CStringWrapper::from(&self.title), - description: CStringWrapper::from(&self.description), - method: CStringWrapper::from(&self.method), - headers: CStringWrapper::from(&hashmap_to_string(&self.headers)), - data: CStringWrapper::from(&self.data), - token: CStringWrapper::from(&self.token), - extras: CStringWrapper::from(&hashmap_to_string(&self.extras)), - version: self.version as u8, - form_items_ptr: form_items.as_ptr() as *const CFormItem, - form_items_len: form_items.len() as u32, - file_specs_ptr: file_specs.as_ptr() as *const CFileSpec, - file_specs_len: file_specs.len() as u32, - body_file_names_ptr: body_file_names.as_ptr() as *const CStringWrapper, - body_file_names_len: body_file_names.len() as u32, - common_data: CommonCTaskConfig { - task_id, - uid, - action: self.common_data.action as u8, - mode: self.common_data.mode as u8, - cover: self.common_data.cover, - network: self.common_data.network as u8, - metered: self.common_data.metered, - roaming: self.common_data.roaming, - retry: self.common_data.retry, - redirect: self.common_data.redirect, - index: self.common_data.index, - begins: self.common_data.begins, - ends: self.common_data.ends, - gauge: self.common_data.gauge, - precise: self.common_data.precise, - background: self.common_data.background, - }, - } - } - - pub fn from_c_struct(c_struct: &CTaskConfig) -> Self { - let task_config = TaskConfig { - bundle: c_struct.bundle.to_string(), - url: c_struct.url.to_string(), - title: c_struct.title.to_string(), - description: c_struct.description.to_string(), - method: c_struct.method.to_string(), - headers: string_to_hashmap(&mut c_struct.headers.to_string()), - data: c_struct.data.to_string(), - token: c_struct.token.to_string(), - extras: string_to_hashmap(&mut c_struct.extras.to_string()), - version: Version::from(c_struct.version), - form_items: Self::build_vec( - c_struct.form_items_ptr, - c_struct.form_items_len as usize, - FormItem::from_c_struct, - ), - file_specs: Self::build_vec( - c_struct.file_specs_ptr, - c_struct.file_specs_len as usize, - FileSpec::from_c_struct, - ), - body_file_names: Self::build_vec( - c_struct.body_file_names_ptr, - c_struct.body_file_names_len as usize, - CStringWrapper::to_string, - ), - common_data: CommonTaskConfig { - task_id: c_struct.common_data.task_id, - uid: c_struct.common_data.uid, - action: Action::from(c_struct.common_data.action), - mode: Mode::from(c_struct.common_data.mode), - cover: c_struct.common_data.cover, - network: Network::from(c_struct.common_data.network), - metered: c_struct.common_data.metered, - roaming: c_struct.common_data.roaming, - retry: c_struct.common_data.retry, - redirect: c_struct.common_data.redirect, - index: c_struct.common_data.index, - begins: c_struct.common_data.begins, - ends: c_struct.common_data.ends, - gauge: c_struct.common_data.gauge, - precise: c_struct.common_data.precise, - background: c_struct.common_data.background, - }, - }; - unsafe { DeleteCFormItem(c_struct.form_items_ptr) }; - unsafe { DeleteCFileSpec(c_struct.file_specs_ptr) }; - unsafe { DeleteCStringPtr(c_struct.body_file_names_ptr) }; - task_config - } -} \ No newline at end of file diff --git a/services/service/rust/src/task_info.rs b/services/service/rust/src/task_info.rs deleted file mode 100644 index d8e42739..00000000 --- a/services/service/rust/src/task_info.rs +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use std::collections::HashMap; -use super::{c_string_wrapper::*, enumration::*, form_item::*, progress::*, utils::*, request_binding::*}; -#[derive(Debug)] -pub struct TaskInfo { - pub bundle: String, - pub url: String, - pub data: String, - pub token: String, - pub form_items: Vec, - pub file_specs: Vec, - pub title: String, - pub description: String, - pub mime_type: String, - pub progress: Progress, - pub extras: HashMap, - pub each_file_status: Vec, - pub common_data: CommonTaskInfo, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct CommonTaskInfo { - pub task_id: u32, - pub uid: u64, - pub action: u8, - pub mode: u8, - pub ctime: u64, - pub mtime: u64, - pub reason: u8, - pub gauge: bool, - pub retry: bool, - pub tries: u32, - pub version: u8, -} - -#[derive(Debug)] -pub struct EachFileStatus { - pub path: String, - pub reason: Reason, - pub message: String, -} - -#[derive(Debug)] -pub struct NotifyData { - pub progress: Progress, - pub action: Action, - pub version: Version, - pub each_file_status: Vec, - pub task_id: u32, - pub uid: u64, - pub bundle: String, -} - -#[repr(C)] -pub struct CEachFileStatus { - pub path: CStringWrapper, - pub reason: u8, - pub message: CStringWrapper, -} - -impl EachFileStatus { - pub fn to_c_struct(&self) -> CEachFileStatus { - CEachFileStatus { - path: CStringWrapper::from(&self.path), - reason: self.reason as u8, - message: CStringWrapper::from(&self.message), - } - } - - pub fn from_c_struct(c_struct: &CEachFileStatus) -> EachFileStatus { - EachFileStatus { - path: c_struct.path.to_string(), - reason: Reason::from(c_struct.reason), - message: c_struct.message.to_string(), - } - } -} - -#[repr(C)] -pub struct CTaskInfo { - pub bundle: CStringWrapper, - pub url: CStringWrapper, - pub data: CStringWrapper, - pub token: CStringWrapper, - pub form_items_ptr: *const CFormItem, - pub form_items_len: u32, - pub file_specs_ptr: *const CFileSpec, - pub file_specs_len: u32, - pub title: CStringWrapper, - pub description: CStringWrapper, - pub mime_type: CStringWrapper, - pub progress: CProgress, - pub each_file_status_ptr: *const CEachFileStatus, - pub each_file_status_len: u32, - pub common_data: CommonTaskInfo, -} - -pub struct InfoSet { - pub form_items: Vec, - pub file_specs: Vec, - pub sizes: String, - pub processed: String, - pub extras: String, - pub each_file_status: Vec, -} - -impl TaskInfo { - pub fn build_info_set(&self) -> InfoSet { - InfoSet { - form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(), - file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(), - sizes: format!("{:?}", self.progress.sizes), - processed: format!("{:?}", self.progress.processed), - extras: hashmap_to_string(&self.extras), - each_file_status: self - .each_file_status - .iter() - .map(|x| x.to_c_struct()) - .collect(), - } - } - - fn build_vec(ptr: *const A, len: usize, func: C) -> Vec - where - C: Fn(&A) -> B, - { - if ptr.is_null() || len == 0 { - return Vec::::new(); - } - let slice = unsafe { std::slice::from_raw_parts(ptr, len) }; - slice.iter().map(|x| func(x)).collect() - } - - pub fn to_c_struct(&self, info: &InfoSet) -> CTaskInfo { - CTaskInfo { - bundle: CStringWrapper::from(&self.bundle), - url: CStringWrapper::from(&self.url), - data: CStringWrapper::from(&self.data), - token: CStringWrapper::from(&self.token), - form_items_ptr: info.form_items.as_ptr() as *const CFormItem, - form_items_len: info.form_items.len() as u32, - file_specs_ptr: info.file_specs.as_ptr() as *const CFileSpec, - file_specs_len: info.file_specs.len() as u32, - title: CStringWrapper::from(&self.title), - description: CStringWrapper::from(&self.description), - mime_type: CStringWrapper::from(&self.mime_type), - progress: self.progress.to_c_struct(&info.sizes, &info.processed, &info.extras), - each_file_status_ptr: info.each_file_status.as_ptr() as *const CEachFileStatus, - each_file_status_len: info.each_file_status.len() as u32, - common_data: self.common_data, - } - } - - pub fn from_c_struct(c_struct: &CTaskInfo) -> Self { - let progress = Progress::from_c_struct(&c_struct.progress); - let extras = progress.extras.clone(); - let task_info = TaskInfo { - bundle: c_struct.bundle.to_string(), - url: c_struct.url.to_string(), - data: c_struct.data.to_string(), - token: c_struct.token.to_string(), - form_items: Self::build_vec( - c_struct.form_items_ptr, - c_struct.form_items_len as usize, - FormItem::from_c_struct, - ), - file_specs: Self::build_vec( - c_struct.file_specs_ptr, - c_struct.file_specs_len as usize, - FileSpec::from_c_struct, - ), - title: c_struct.title.to_string(), - description: c_struct.description.to_string(), - mime_type: c_struct.mime_type.to_string(), - progress, - extras, - each_file_status: Self::build_vec( - c_struct.each_file_status_ptr, - c_struct.each_file_status_len as usize, - EachFileStatus::from_c_struct), - common_data: c_struct.common_data, - }; - unsafe { DeleteCFormItem(c_struct.form_items_ptr) }; - unsafe { DeleteCFileSpec(c_struct.file_specs_ptr) }; - unsafe { DeleteCEachFileStatus(c_struct.each_file_status_ptr) }; - task_info - } -} - -pub struct UpdateInfo { - pub mtime: u64, - pub reason: u8, - pub tries: u32, - pub progress: Progress, - pub each_file_status: Vec, -} - -#[repr(C)] -pub struct CUpdateInfo { - pub mtime: u64, - pub reason: u8, - pub tries: u32, - pub progress: CProgress, - pub each_file_status_ptr: *const CEachFileStatus, - pub each_file_status_len: u32, -} - -impl UpdateInfo { - pub fn to_c_struct( - &self, - sizes: &String, - processed: &String, - extras: &String, - each_file_status: &Vec, - ) -> CUpdateInfo { - CUpdateInfo { - mtime: self.mtime, - reason: self.reason, - tries: self.tries, - progress: self.progress.to_c_struct(sizes, processed, extras), - each_file_status_ptr: each_file_status.as_ptr() as *const CEachFileStatus, - each_file_status_len: each_file_status.len() as u32, - } - } -} diff --git a/services/service/rust/src/task_manager.rs b/services/service/rust/src/task_manager.rs deleted file mode 100644 index 0503bf02..00000000 --- a/services/service/rust/src/task_manager.rs +++ /dev/null @@ -1,1040 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -extern crate rust_samgr; - -use super::{ - enumration::*, request_task::*, task_config::*, utils::*, task_info::*, progress::*, request_binding::*, - log::LOG_LABEL, c_string_wrapper::*, filter::*, request_service_ability::*, -}; -use hilog_rust::*; -use ylong_runtime::sync; -use std::{collections::HashMap, ffi::CString, ffi::c_char, fs::File, time::Duration}; -use std::sync::atomic::{AtomicU32, AtomicU64, Ordering, AtomicBool}; -use std::sync::{Arc, Mutex, MutexGuard, Once}; -use rust_samgr::get_systemability_manager; -use ylong_runtime::{builder::RuntimeBuilder, executor::Runtime, task::JoinHandle, time::sleep}; - -static MAX_TASK_COUNT: u32 = 300; -static MAX_TASK_COUNT_EACH_APP: u8 = 10; -static MAX_RUNNING_TASK_COUNT_EACH_APP: u32 = 5; // api10 -static MAX_RUNNING_TASK_COUNT_API9: u32 = 4; -static INTERVAL_MILLISECONDS: u64 = 30 * 60 * 1000; -static MILLISECONDS_IN_ONE_DAY: u64 = 24 * 60 * 60 * 1000; -static MILLISECONDS_IN_ONE_MONTH: u64 = 30 * 24 * 60 * 60 * 1000; -static MILLISECONDS_IN_ONE_SECONDS: u64 = 1000; -static MILLISECONDS_IN_ONE_MINUTE: u64 = 60 * 1000; -static REQUEST_SERVICE_ID: i32 = 3706; -static WAITTING_RETRY_INTERVAL: u64 = 10; -static WAITTING_RESTORE_INTERVAL: u64 = 7; -static DUMP_INTERVAL: u64 = 5 * 60; - -type AppTask = HashMap>; - -pub struct TaskManager { - task_map: Arc>>, - event_cb: Option>, - info_cb: Option>, - pub front_app_map: Mutex>>, - pub front_notify_time: u64, - pub unloading: AtomicBool, - pub restoring: AtomicBool, - pub api10_background_task_count: AtomicU32, - pub recording_rdb_num: AtomicU32, - task_handles: Mutex>>, -} - -pub fn monitor_task() { - let task_manager = TaskManager::get_instance(); - ylong_runtime::spawn(async { - let mut remove_task = Vec::>::new(); - loop { - { - let manager = TaskManager::get_instance(); - let task_map_guard = manager.task_map.lock().unwrap(); - for (_, app_task) in task_map_guard.iter() { - for (_, task) in app_task.iter() { - let current_time = get_current_timestamp(); - let (state, time) = { - let guard = task.status.lock().unwrap(); - (guard.state, guard.waitting_network_time.clone()) - }; - if state == State::WAITING { - if let Some(t) = time { - if current_time - t > MILLISECONDS_IN_ONE_DAY { - task.set_status(State::STOPPED, Reason::WaittingNetWorkOneday); - remove_task.push(task.clone()); - } - } - } - if task.conf.version == Version::API9 { - continue; - } - if current_time - task.ctime > MILLISECONDS_IN_ONE_MONTH { - task.set_status(State::STOPPED, Reason::TaskSurvivalOneMonth); - remove_task.push(task.clone()); - continue; - } - } - } - for task in remove_task.iter() { - TaskManager::get_instance().after_task_processed(task); - } - remove_task.clear(); - } - sleep(Duration::from_millis(INTERVAL_MILLISECONDS)).await; - } - }); -} - -impl TaskManager { - fn new() -> Self { - ylong_runtime::builder::RuntimeBuilder::new_multi_thread().worker_num(4).build_global().unwrap(); - TaskManager { - task_map: Arc::new(Mutex::new(HashMap::::new())), - event_cb: None, - info_cb: None, - front_app_map: Mutex::new(HashMap::>::new()), - front_notify_time: get_current_timestamp(), - unloading: AtomicBool::new(false), - restoring: AtomicBool::new(false), - api10_background_task_count: AtomicU32::new(0), - recording_rdb_num: AtomicU32::new(0), - task_handles: Mutex::new(HashMap::>::new()), - } - } - - pub fn get_instance() -> &'static mut Self { - static mut TASK_MANAGER: Option = None; - static ONCE: Once = Once::new(); - ONCE.call_once(|| unsafe { - TASK_MANAGER = Some(Self::new()); - }); - - unsafe { TASK_MANAGER.as_mut().unwrap() } - } - - pub fn dump_all_task_info(&self) { - ylong_runtime::spawn(async { - loop { - let task_manager = TaskManager::get_instance(); - let api10_background_task_count = task_manager.api10_background_task_count.load(Ordering::SeqCst); - let recording_rdb_num = task_manager.recording_rdb_num.load(Ordering::SeqCst); - let unloading = task_manager.unloading.load(Ordering::SeqCst); - info!(LOG_LABEL, "dump all task info, api10_background_task_count:{}, - recording_rdb_num:{}, unloading flag:{}", @public(api10_background_task_count), - @public(recording_rdb_num), - @public(unloading)); - { - let guard = task_manager.task_map.lock().unwrap(); - for (_, app_task) in guard.iter() { - for (task_id, task) in app_task.iter() { - let task_status = task.status.lock().unwrap(); - info!(LOG_LABEL, - "dump task message, task_id:{}, action:{}, mode:{}, bundle name:{}, task_status:{:?}", - @public(task_id), - @public(task.conf.common_data.action as u8), - @public(task.conf.common_data.mode as u8), - @public(task.conf.bundle), @public(*task_status)); - } - } - } - sleep(Duration::from_secs(DUMP_INTERVAL)).await; - } - }); - } - - pub fn clear_all_task(&mut self) { - self.task_map.lock().unwrap().clear(); - self.front_app_map.lock().unwrap().clear(); - self.api10_background_task_count.store(0, Ordering::SeqCst); - } - - pub fn get_total_task_count(&self, guard: &MutexGuard>) -> u32 { - let mut total_task_count: u32 = 0; - for (_, app_task) in guard.iter() { - total_task_count += app_task.len() as u32; - } - total_task_count - } - - pub fn get_api10_background_task_count(&self) -> u32 { - self.api10_background_task_count.load(Ordering::SeqCst) - } - - pub fn has_event_callback(&self) -> bool { - self.event_cb.is_some() - } - - pub fn register_callback( - &mut self, - event_cb: Box, - info_cb: Box, - ) { - self.event_cb = Some(event_cb); - self.info_cb = Some(info_cb); - } - - pub fn front_notify(&mut self, event: String, notify_data: &NotifyData) { - if self.event_cb.is_none() { - return; - } - let total_processed = notify_data.progress.common_data.total_processed; - let file_total_size: i64 = notify_data.progress.sizes.iter().sum(); - if total_processed == 0 && file_total_size < 0 && event.eq("progress") { - return; - } - if !self.is_front_app(notify_data.uid, notify_data.bundle.as_str()) - && (notify_data.version == Version::API10 || event.eq("progress")) - { - return; - } - self.front_notify_time = get_current_timestamp(); - self.event_cb.as_ref().unwrap()(event, notify_data); - } - - fn is_front_app(&self, uid: u64, bundle: &str) -> bool { - let front_app = self.front_app_map.lock().unwrap(); - if front_app.is_empty() { - let top_bundle = unsafe { GetTopBundleName() }; - let top_bundle = top_bundle.to_string(); - debug!(LOG_LABEL, "top_bundle {}", @public(top_bundle)); - if !top_bundle.eq(bundle) { - return false; - } - } else if !front_app.contains_key(&uid) || !front_app.get(&uid).unwrap().is_none() { - return false; - } - debug!(LOG_LABEL, "is front app"); - true - } - - pub fn construct_task( - &mut self, - conf: Arc, - uid: u64, - task_id: &mut u32, - files: Vec, - body_files: Vec, - ) -> ErrorCode { - debug!(LOG_LABEL, "begin construct a task"); - if files.len() == 0 { - return ErrorCode::FileOperationErr; - } - *task_id = generate_task_id(); - let bundle = conf.bundle.clone(); - let task = RequestTask::constructor(conf, uid, *task_id, files, body_files); - let mut task_map_guard = self.task_map.lock().unwrap(); - if self.unloading.load(Ordering::SeqCst) { - return ErrorCode::UnloadingSA; - } - debug!(LOG_LABEL, "uid {} task_id {} version {:?}", @public(uid), @public(task_id), @public(task.conf.version)); - match task.conf.version { - Version::API10 => { - if !self.add_task(uid, *task_id, Arc::new(task), &mut task_map_guard) { - return ErrorCode::TaskEnqueueErr; - } - self.api10_background_task_count - .fetch_add(1, Ordering::SeqCst); - return ErrorCode::ErrOk; - } - Version::API9 => { - self.add_task_api9(uid, *task_id, Arc::new(task), &mut task_map_guard); - return ErrorCode::ErrOk; - } - } - } - - fn add_task_api9( - &self, - uid: u64, - task_id: u32, - task: Arc, - guard: &mut MutexGuard>, - ) { - debug!(LOG_LABEL, "Begin add a v9 task"); - let app_task = guard.get_mut(&uid); - match app_task { - Some(map) => { - task.set_status(State::INITIALIZED, Reason::Default); - map.insert(task_id, task); - debug!(LOG_LABEL, - "add v9 task sccuess, the current number of tasks which belongs to the app is {}", - @public(map.len() as u8) - ); - } - None => { - let mut app_task = AppTask::new(); - task.set_status(State::INITIALIZED, Reason::Default); - app_task.insert(task_id, task); - guard.insert(uid, app_task); - debug!(LOG_LABEL, "add v9 task sccuess, there is one task which belongs to the app"); - } - } - } - - fn add_task( - &self, - uid: u64, - task_id: u32, - task: Arc, - guard: &mut MutexGuard>, - ) -> bool { - debug!(LOG_LABEL, "Begin add a v10 task"); - if self.api10_background_task_count.load(Ordering::SeqCst) >= MAX_TASK_COUNT { - error!(LOG_LABEL, - "add v10 task failed, the number of tasks has reached the limit in the system"); - return false; - } - let app_task = guard.get_mut(&uid); - match app_task { - Some(map) => { - if (map.len() as u8) == MAX_TASK_COUNT_EACH_APP { - error!(LOG_LABEL, - "add v10 task failed, the maximum value for each application processing task has been reached"); - return false; - } - task.set_status(State::INITIALIZED, Reason::Default); - map.insert(task_id, task); - debug!(LOG_LABEL, - "add v10 task sccuess, the current number of tasks which belongs to the app is {}", - @public(map.len() as u8) - ); - return true; - } - None => { - let mut app_task = AppTask::new(); - task.set_status(State::INITIALIZED, Reason::Default); - app_task.insert(task_id, task); - guard.insert(uid, app_task); - debug!(LOG_LABEL, "add v10 task sccuess, there is one task which belongs to the app"); - return true; - } - } - } - - fn get_task( - &self, - uid: u64, - task_id: u32, - guard: &MutexGuard>, - ) -> Option> { - let app_task = guard.get(&uid); - if app_task.is_none() { - error!(LOG_LABEL, "the Application has not any task"); - return None; - } - debug!(LOG_LABEL, "task_id: {}", @public(task_id)); - let task = app_task.unwrap().get(&task_id); - match task { - Some(v) => { - debug!(LOG_LABEL, "get the task by uid and task id success"); - return Some(v.clone()); - } - None => { - error!(LOG_LABEL, "can not found the task which belongs to the application"); - return None; - } - } - } - - fn reach_maximum_running_limit( - &self, - uid: u64, - version: Version, - limit: u32, - guard: &MutexGuard>, - ) -> bool { - let mut count = 0; - for (id, app_task) in guard.iter() { - if version == Version::API10 && uid != *id { - continue; - } - for (_, task) in app_task.iter() { - if task.conf.version != version { - continue; - } - let state = task.status.lock().unwrap().state; - if state == State::RETRYING || state == State::RUNNING { - count += 1; - } - if count >= limit { - return true; - } - } - } - false - } - - fn start_common( - &self, - uid: u64, - task: Arc, - guard: MutexGuard>, - ) { - if !task.net_work_online() || !task.check_net_work_status() { - error!(LOG_LABEL, "check net work failed"); - return; - } - let state = task.status.lock().unwrap().state; - if state != State::INITIALIZED && state != State::WAITING && state != State::PAUSED { - return; - } - let limit = if task.conf.version == Version::API10 { - MAX_RUNNING_TASK_COUNT_EACH_APP - } else { - MAX_RUNNING_TASK_COUNT_API9 - }; - if self.reach_maximum_running_limit(uid, task.conf.version, limit, &guard) { - info!(LOG_LABEL, "too many task in running state"); - task.set_status(State::WAITING, Reason::RunningTaskMeetLimits); - return; - } - let (state, reason) = { - let status = task.status.lock().unwrap(); - (status.state, status.reason.clone()) - }; - if state == State::WAITING - && (reason == Reason::NetWorkOffline || reason == Reason::UnSupportedNetWorkType) - { - task.retry.store(true, Ordering::SeqCst); - task.tries.fetch_add(1, Ordering::SeqCst); - task.set_status(State::RETRYING, Reason::Default); - } else { - task.set_status(State::RUNNING, Reason::Default); - } - let task_id = task.task_id; - let handle = ylong_runtime::spawn(async move { - run(task.clone()).await; - TaskManager::get_instance().after_task_processed(&task); - }); - self.task_handles.lock().unwrap().insert(task_id, handle); - info!(LOG_LABEL, "task {} start success", @public(task_id)); - return; - } - - fn start_inner( - &self, - uid: u64, - task: Arc, - guard: MutexGuard>, - ) { - self.start_common(uid, task.clone(), guard); - Self::get_instance().after_task_processed(&task); - } - - pub fn start(&mut self, uid: u64, task_id: u32) -> ErrorCode { - info!(LOG_LABEL, "start a task, which task id is {}", @public(task_id)); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - if let Some(task) = task { - let task_state = task.status.lock().unwrap().state; - if task_state != State::INITIALIZED { - error!(LOG_LABEL, "can not start a task which state is {}", @public(task_state as u32)); - return ErrorCode::TaskStateErr; - } - self.start_inner(uid, task.clone(), task_map_guard); - return ErrorCode::ErrOk; - } - error!(LOG_LABEL, "task not found"); - ErrorCode::TaskStateErr - } - - fn process_waitting_task(&self, uid: u64, version: Version, guard: &MutexGuard>) { - for (id, app_task) in guard.iter() { - if version == Version::API10 && uid != *id { - continue; - } - for (_, task) in app_task.iter() { - if version != task.conf.version { - continue; - } - let state = task.status.lock().unwrap().state; - if state == State::WAITING { - debug!(LOG_LABEL, "begin process the task which in waitting state"); - let task = task.clone(); - ylong_runtime::spawn(async move { - let manager = TaskManager::get_instance(); - let task_map_guard = manager.task_map.lock().unwrap(); - manager.start_inner(uid, task, task_map_guard); - }); - } - return; - } - } - } - - fn after_task_processed(&mut self, task: &Arc) { - ylong_runtime::spawn(remove_task_from_map(task.clone())); - - } - - pub fn pause_task(&self, task:Arc, reason: Reason) -> ErrorCode { - if !task.set_status(State::PAUSED, reason) { - error!(LOG_LABEL, "can not pause a task which state is not meet the requirements"); - return ErrorCode::TaskStateErr; - } - task.resume.store(false, Ordering::SeqCst); - debug!(LOG_LABEL, "pause the task success"); - return ErrorCode::ErrOk; - } - - pub fn pause(&self, uid: u64, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "pause a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - if let Some(task) = task { - return self.pause_task(task, Reason::UserOperation); - } - error!(LOG_LABEL, "task not found"); - ErrorCode::TaskStateErr - } - - pub fn resume(&self, uid: u64, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "resume a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - if let Some(task) = task { - let state = task.status.lock().unwrap().state; - if state != State::PAUSED { - error!(LOG_LABEL, "can not resume a task which state is not paused"); - return ErrorCode::TaskStateErr; - } - error!(LOG_LABEL, "resume the task success"); - task.resume.store(true, Ordering::SeqCst); - let notify_data = task.build_notify_data(); - TaskManager::get_instance().front_notify("resume".into(), ¬ify_data); - self.start_inner(uid, task.clone(), task_map_guard); - return ErrorCode::ErrOk; - } - error!(LOG_LABEL, "task not found"); - ErrorCode::TaskStateErr - } - - pub fn stop_task(&self, task:Arc, reason: Reason) -> ErrorCode { - if !task.set_status(State::STOPPED, reason) { - error!(LOG_LABEL, "can not stop a task which state is not meet the requirements"); - return ErrorCode::TaskStateErr; - } - Self::get_instance().after_task_processed(&task); - debug!(LOG_LABEL, "Stopped success"); - task.resume.store(false, Ordering::SeqCst); - return ErrorCode::ErrOk; - } - - pub fn stop(&mut self, uid: u64, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "Stop a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - if let Some(task) = task { - return self.stop_task(task, Reason::UserOperation); - } - error!(LOG_LABEL, "Stop failed"); - ErrorCode::TaskStateErr - } - - pub fn remove(&mut self, uid: u64, task_id: u32) -> ErrorCode { - debug!(LOG_LABEL, "Remove a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - if let Some(task) = task { - task.set_status(State::REMOVED, Reason::UserOperation); - Self::get_instance().after_task_processed(&task); - debug!(LOG_LABEL, "remove success"); - return ErrorCode::ErrOk; - } - error!(LOG_LABEL, "Remove failed"); - ErrorCode::TaskNotFound - } - - pub fn show(&self, uid: u64, task_id: u32) -> Option { - debug!(LOG_LABEL, "show a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - match task { - Some(value) => { - debug!(LOG_LABEL, "show task info by memory"); - let task_info = value.show(); - return Some(task_info); - } - None => return None, - } - } - - pub fn touch(&self, uid: u64, task_id: u32, token: String) -> Option { - debug!(LOG_LABEL, "touch a task"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - match task { - Some(value) => { - debug!(LOG_LABEL, "touch task info by memory"); - if value.conf.token.eq(token.as_str()) { - let mut task_info = value.show(); - task_info.bundle = "".to_string(); - return Some(task_info); - } - return None; - } - None => { - debug!(LOG_LABEL, "touch task info by database"); - let c_task_info = unsafe { Touch(task_id, uid, CStringWrapper::from(&token)) }; - if c_task_info.is_null() { - return None; - } - let c_task_info = unsafe { &*c_task_info }; - let task_info = TaskInfo::from_c_struct(c_task_info); - debug!(LOG_LABEL, "touch task info is {:?}", @public(task_info)); - unsafe { DeleteCTaskInfo(c_task_info) }; - return Some(task_info); - } - } - } - - pub fn query(&self, task_id: u32, query_action: Action) -> Option { - debug!(LOG_LABEL, "query a task"); - let task_map_guard = self.task_map.lock().unwrap(); - for (_, app_task) in task_map_guard.iter() { - for (tid, task) in app_task.iter() { - if *tid == task_id { - if (query_action == Action::DOWNLOAD - && task.conf.common_data.action == Action::DOWNLOAD) - || (query_action == Action::UPLOAD - && task.conf.common_data.action == Action::UPLOAD) - || (query_action == Action::ANY) - { - debug!(LOG_LABEL, "query task info by memory"); - let mut task_info = task.show(); - task_info.data = "".to_string(); - task_info.url = "".to_string(); - debug!(LOG_LABEL, "query task info is {:?}", @public(task_info)); - return Some(task_info); - } - } - } - } - debug!(LOG_LABEL, "query task info by database"); - let c_task_info = unsafe { Query(task_id, query_action) }; - if c_task_info.is_null() { - return None; - } - let c_task_info = unsafe { &*c_task_info }; - let task_info = TaskInfo::from_c_struct(c_task_info); - debug!(LOG_LABEL, "query task info is {:?}", @public(task_info)); - unsafe { DeleteCTaskInfo(c_task_info) }; - Some(task_info) - } - - pub fn search(&self, filter: Filter) -> Vec { - let mut vec = Vec::::new(); - let task_map_guard = self.task_map.lock().unwrap(); - let c_vector_wrapper = unsafe { Search(filter.to_c_struct()) }; - if c_vector_wrapper.ptr.is_null() || c_vector_wrapper.len == 0 { - error!(LOG_LABEL, "c_vector_wrapper is null"); - return vec; - } - let slice = unsafe { std::slice::from_raw_parts(c_vector_wrapper.ptr, c_vector_wrapper.len as usize) }; - for item in slice.iter() { - vec.push(*item); - } - debug!(LOG_LABEL, "c_vector_wrapper is not null"); - unsafe { DeleteCVectorWrapper(c_vector_wrapper.ptr) }; - vec - } - - pub fn query_mime_type(&self, uid: u64, task_id: u32) -> String { - debug!(LOG_LABEL, "query a task mime type"); - let task_map_guard = self.task_map.lock().unwrap(); - let task = self.get_task(uid, task_id, &task_map_guard); - match task { - Some(value) => { - debug!(LOG_LABEL, "query task mime type by memory"); - let mimt_type = value.query_mime_type(); - return mimt_type; - } - None => { - return "".into(); - } - } - } - - pub fn query_one_task(&self, task_id: u32) -> Option> { - let guard = self.task_map.lock().unwrap(); - for (_, app_task) in guard.iter() { - for (id, task) in app_task.iter() { - if task_id == *id { - return Some(task.clone()); - } - } - } - None - } - - pub fn query_all_task(&self) -> Vec> { - let mut vec: Vec> = Vec::new(); - let guard = self.task_map.lock().unwrap(); - for (_, app_task) in guard.iter() { - for (_, task) in app_task.iter() { - vec.push(task.clone()); - } - } - vec - } - - pub fn network_check_unload_sa(&self, guard: &MutexGuard>) -> bool { - let mut need_unload = false; - for (_, app_task) in guard.iter() { - for (_, task) in app_task.iter() { - let state = task.status.lock().unwrap().state; - if state == State::COMPLETED || state == State::FAILED - || state == State::REMOVED || state == State::STOPPED { - need_unload = true; - } else if (state == State::WAITING || state == State::PAUSED) - && (!task.is_satisfied_configuration() || unsafe { !IsOnline() }) { - need_unload = true; - } else { - return false; - } - } - } - return need_unload; - } - - pub fn record_all_task_config(&self, guard: &MutexGuard>) { - debug!(LOG_LABEL, "record all task config into database"); - self.recording_rdb_num.fetch_add(1, Ordering::SeqCst); - for (_, app_task) in guard.iter() { - for (_, task) in app_task.iter() { - if unsafe { HasTaskConfigRecord(task.task_id) } { - continue; - } - let state = task.status.lock().unwrap().state; - if state != State::WAITING && state != State::PAUSED { - continue; - } - let task_config = task.conf.as_ref().clone(); - let c_task_config = task_config.to_c_struct(task.task_id, task.uid); - let ret = unsafe { RecordRequestTaskConfig(&c_task_config) }; - info!(LOG_LABEL, "insert taskConfig DB ret is {}", @public(ret)); - } - } - self.recording_rdb_num.fetch_sub(1, Ordering::SeqCst); - } - - pub fn query_all_task_config(&self) -> Option>> { - debug!(LOG_LABEL, "query all task config in database"); - let mut task_config_list: Vec> = Vec::new(); - let c_config_list_len = unsafe { QueryTaskConfigLen() }; - if c_config_list_len <= 0 { - debug!(LOG_LABEL, "no task config in database"); - return None; - } - let c_task_config_list = unsafe { QueryAllTaskConfig() }; - if c_task_config_list.is_null() { - return None; - } - let c_task_config_ptrs = unsafe { std::slice::from_raw_parts(c_task_config_list, c_config_list_len as usize) }; - for c_task_config in c_task_config_ptrs.iter() { - let task_config = TaskConfig::from_c_struct(unsafe { &**c_task_config }); - task_config_list.push(Arc::new(task_config)); - unsafe { DeleteCTaskConfig(*c_task_config) }; - } - unsafe { DeleteCTaskConfigs(c_task_config_list) }; - Some(task_config_list) - } - - pub fn restore_task(&mut self, task: Arc) { - if task.conf.version == Version::API10 { - self.api10_background_task_count.fetch_add(1, Ordering::SeqCst); - } - let mut guard = self.task_map.lock().unwrap(); - let uid = task.uid; - let task_id = task.task_id; - if self.get_task(uid, task_id, &guard).is_some() { - return; - } - let app_task = guard.get_mut(&uid); - match app_task { - Some(map) => { - map.insert(task_id, task); - debug!(LOG_LABEL, "restore task into exist app_map success"); - } - None => { - let mut app_task = AppTask::new(); - app_task.insert(task_id, task); - guard.insert(uid, app_task); - debug!(LOG_LABEL, "restore task into new app_map success"); - } - } - } -} - -pub async fn unload_sa() { - loop { - sleep(Duration::from_secs(60)).await; - let task_manager = TaskManager::get_instance(); - info!(LOG_LABEL, "unload SA end sleep"); - match task_manager.task_map.try_lock() { - Ok(guard) => { - let total_task_count = task_manager.get_total_task_count(&guard); - let recording_rdb_num = task_manager.recording_rdb_num.load(Ordering::SeqCst); - let only_network_unsat = task_manager.network_check_unload_sa(&guard); - if (total_task_count != 0 && !only_network_unsat) || recording_rdb_num != 0 { - info!(LOG_LABEL, "total_task_count is {}, recording_rdb_num is {}", - @public(total_task_count), @public(recording_rdb_num)); - continue; - } - task_manager.unloading.store(true, Ordering::SeqCst); - if total_task_count != 0 { - task_manager.record_all_task_config(&guard); - } - info!(LOG_LABEL, "unload SA"); - let samgr_proxy = get_systemability_manager(); - let res = samgr_proxy.unload_systemability(REQUEST_SERVICE_ID); - match res { - Err(e) => { error!(LOG_LABEL, "unload SA failed, err is {:?}", e); }, - _ => {}, - } - return; - } - Err(_) => continue, - } - } -} - -async fn remove_task_from_map(task: Arc) { - let state = task.status.lock().unwrap().state; - if state != State::COMPLETED && state != State::FAILED - && state != State::REMOVED && state != State::STOPPED { - return; - } - debug!(LOG_LABEL, "remove task from map"); - let task_manager = TaskManager::get_instance(); - { - let _guard = task_manager.task_map.lock().unwrap(); - task_manager.task_handles.lock().unwrap().remove(&task.task_id); - }; - - if task.conf.version == Version::API9 { - let task_info = task.show(); - task_manager.info_cb.as_ref().unwrap()(&task_info); - sleep(Duration::from_millis(MILLISECONDS_IN_ONE_SECONDS)).await; - } - let mut guard = task_manager.task_map.lock().unwrap(); - let app_task = guard.get_mut(&task.uid); - if app_task.is_none() { - return; - } - let app_task = app_task.unwrap(); - let remove_task = app_task.remove(&task.task_id); - if remove_task.is_none() { - return; - } - let remove_task = remove_task.unwrap(); - if remove_task.conf.version == Version::API10 { - task_manager.api10_background_task_count.fetch_sub(1, Ordering::SeqCst); - } - if app_task.len() == 0 { - guard.remove(&remove_task.uid); - task_manager.front_app_map.lock().unwrap().remove(&remove_task.uid); - } - task_manager.process_waitting_task(remove_task.uid, remove_task.conf.version, &guard); -} - -pub async fn restore_all_tasks() { - let task_manager = TaskManager::get_instance(); - if task_manager.restoring.load(Ordering::SeqCst) { - return; - } - task_manager.restoring.store(true, Ordering::SeqCst); - if let Some(config_list) = task_manager.query_all_task_config() { - info!(LOG_LABEL, "RSA query task config list len: {} in database", @public(config_list.len())); - for config in config_list.iter() { - debug!(LOG_LABEL, "RSA query task config is {:?}", @public(config)); - let uid = config.common_data.uid; - let task_id = config.common_data.task_id; - let token = config.token.clone(); - if let Some(task_info) = task_manager.touch(uid, task_id, token) { - let state = State::from(task_info.progress.common_data.state); - if state != State::WAITING && state != State::PAUSED { - continue; - } - let request_task = RequestTask::restore_task(config.clone(), task_info); - let task = Arc::new(request_task); - task_manager.restore_task(task.clone()); - if unsafe { IsOnline() } { - resume_waiting_task(task.clone(), uid, WAITTING_RESTORE_INTERVAL); - } - } - unsafe { CleanTaskConfigTable(task_id, uid) }; - } - } - task_manager.restoring.store(false, Ordering::SeqCst); -} - -pub fn monitor_network() { - debug!(LOG_LABEL, "monitor_network"); - unsafe { - RegisterNetworkCallback(net_work_change_callback); - } -} - -extern "C" fn net_work_change_callback() { - info!(LOG_LABEL, "net work changed"); - let task_manager = TaskManager::get_instance(); - let guard = task_manager.task_map.lock().unwrap(); - for (uid, app_task) in guard.iter() { - let uid = *uid; - for (_, task) in app_task.iter() { - if unsafe { IsOnline() } { - resume_waiting_task(task.clone(), uid, WAITTING_RETRY_INTERVAL); - } - } - } -} - -pub fn resume_waiting_task(task: Arc, uid: u64, interval: u64) { - let state = task.status.lock().unwrap().state; - if state == State::WAITING && task.is_satisfied_configuration() { - info!(LOG_LABEL, "Begin try resume task as network condition resume"); - task.resume.store(true, Ordering::SeqCst); - ylong_runtime::spawn(async move { - sleep(Duration::from_secs(interval)).await; - let manager = TaskManager::get_instance(); - let guard = manager.task_map.lock().unwrap(); - let notify_data = task.build_notify_data(); - TaskManager::get_instance().front_notify("resume".into(), ¬ify_data); - manager.start_inner(uid, task.clone(), guard); - }); - } -} - -pub fn monitor_app_state() { - debug!(LOG_LABEL, "monitor_app_state"); - unsafe { - RegisterAPPStateCallback(update_app_state); - } -} - -extern "C" fn update_app_state(uid: i32, state: i32) { - debug!(LOG_LABEL, "update app state, uid = {}, state = {}", @public(uid), @public(state)); - let task_manager = TaskManager::get_instance(); - let guard = task_manager.task_map.lock().unwrap(); - let app_task = guard.get(&(uid as u64)); - if app_task.is_none() { - return; - } - if is_foreground(state) { - debug!(LOG_LABEL, "app in foreground"); - task_manager.front_app_map.lock().unwrap().insert(uid as u64, None); - update_foreground_app(uid as u64, &app_task.unwrap()); - } else if is_background(state) { - debug!(LOG_LABEL, "app in background"); - task_manager.front_app_map.lock().unwrap().insert(uid as u64, Some(get_current_timestamp())); - update_background_app(uid as u64); - } else if is_terminated(state) { - debug!(LOG_LABEL, "app is terminated"); - task_manager.front_app_map.lock().unwrap().remove(&(uid as u64)); - update_terminated_app(uid as u64, &app_task.unwrap()); - } -} - -fn is_foreground(state: i32) -> bool { - state == ApplicationState::AppStateForeground as i32 -} - -fn is_background(state: i32) -> bool { - state == ApplicationState::AppStateBackground as i32 -} - -fn is_terminated(state: i32) -> bool { - state == ApplicationState::AppStateTerminated as i32 -} - -fn update_foreground_app( - uid: u64, - app_task: &HashMap>, -) { - for (_, task) in app_task.iter() { - if task.conf.common_data.mode != Mode::FRONTEND { - continue; - } - let task = task.clone(); - let state = task.status.lock().unwrap().state; - let reason = task.status.lock().unwrap().reason; - if state == State::PAUSED && reason == Reason::AppBackgroundOrTerminate { - info!(LOG_LABEL, "Begin try resume task as app switch to background"); - task.resume.store(true, Ordering::SeqCst); - ylong_runtime::spawn(async move { - sleep(Duration::from_millis(MILLISECONDS_IN_ONE_SECONDS)).await; - let manager = TaskManager::get_instance(); - let guard = manager.task_map.lock().unwrap(); - let notify_data = task.build_notify_data(); - TaskManager::get_instance().front_notify("resume".into(), ¬ify_data); - manager.start_inner(uid, task.clone(), guard); - }); - } - } -} - -fn update_background_app(uid: u64) { - ylong_runtime::spawn(async move { - loop{ - sleep(Duration::from_millis(MILLISECONDS_IN_ONE_SECONDS)).await; - let task_manager = TaskManager::get_instance(); - { - let guard = task_manager.task_map.lock().unwrap(); - let app_task = guard.get(&uid); - if app_task.is_none() { - break; - } - if let Some(switch_time) = task_manager.front_app_map.lock().unwrap().get(&uid) { - if switch_time.is_none() { - break; - } - let interval_time = get_current_timestamp() - switch_time.unwrap(); - if interval_time < MILLISECONDS_IN_ONE_MINUTE { - debug!(LOG_LABEL, "interval time: {}", @public(interval_time)); - continue; - } - } - for (_, task) in app_task.unwrap().iter() { - if task.conf.common_data.mode != Mode::FRONTEND { - continue; - } - if task.conf.common_data.action == Action::UPLOAD { - task.set_status(State::FAILED, Reason::AppBackgroundOrTerminate); - TaskManager::get_instance().after_task_processed(&task); - } else if task.conf.common_data.action == Action::DOWNLOAD { - task_manager.pause_task(task.clone(), Reason::AppBackgroundOrTerminate); - } - } - break; - } - } - }); -} - -fn update_terminated_app( - uid: u64, - app_task: &HashMap>, -) { - for (_, task) in app_task.iter() { - if task.conf.common_data.mode != Mode::FRONTEND { - continue; - } - task.set_status(State::FAILED, Reason::AppBackgroundOrTerminate); - TaskManager::get_instance().after_task_processed(&task); - } -} diff --git a/services/service/rust/src/trace.rs b/services/service/rust/src/trace.rs deleted file mode 100644 index 49bb4b67..00000000 --- a/services/service/rust/src/trace.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -extern crate hitrace_meter_rust; - -use hitrace_meter_rust::{start_trace, finish_trace}; - -/// Hitrace adapter which provides timing capability. -/// -/// The timing will end automatically when the structure drops. Users should -/// take care that the lifetime of this structure. -pub(crate) struct TraceScope { - label: u64, -} - -impl TraceScope { - // Copies from `Hitrace`. - const HITRACE_TAG_MISC: u64 = 1u64 << 41; - - /// Starts tracing. - pub(crate) fn trace(value: &str) -> Self { - let trace = Self { label: Self::HITRACE_TAG_MISC }; - start_trace(trace.label, value); - trace - } -} - -impl Drop for TraceScope { - // The timing will end automatically when the structure drops. - fn drop(&mut self) { - finish_trace(self.label); - } -} \ No newline at end of file diff --git a/services/service/rust/src/utils.rs b/services/service/rust/src/utils.rs deleted file mode 100644 index 97e36346..00000000 --- a/services/service/rust/src/utils.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -use std::{io::Write, collections::HashMap}; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::ffi::{ c_char, CStr }; - -pub fn get_current_timestamp() -> u64 { - match SystemTime::now().duration_since(UNIX_EPOCH) { - Ok(n) => n.as_millis() as u64, - Err(_) => panic!("SystemTime before UNIX EPOCH!"), - } -} - -pub fn convert_to_string(ptr: *const c_char) -> String { - let c_str: &CStr = unsafe { CStr::from_ptr(ptr) }; - let str_slice: &str = c_str.to_str().unwrap(); - str_slice.to_owned() -} - -pub fn generate_task_id() -> u32 { - SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos() -} - -pub fn hashmap_to_string(map: &HashMap) -> String { - let mut res = Vec::new(); - for (n, (k, v)) in map.iter().enumerate() { - if n != 0 { - let _ = write!(res, "\r\n"); - } - let _ = write!(res, "{k}\t{v}"); - } - unsafe { String::from_utf8_unchecked(res) } -} - -pub fn string_to_hashmap(str: &mut String) -> HashMap { - let mut map = HashMap::::new(); - if str.is_empty() { - return map; - } - for item in str.split("\r\n") { - let (k, v) = item.split_once('\t').unwrap(); - map.insert(k.into(), v.into()); - } - map -} - -pub fn split_string(str: &mut String) -> std::str::Split<'_, &str> { - let pat: &[_] = &['[', ']']; - str.trim_matches(pat).split(", ") -} diff --git a/test/unittest/rust/src/common.rs b/test/unittest/rust/src/common.rs index 21a2b131..6af51b87 100644 --- a/test/unittest/rust/src/common.rs +++ b/test/unittest/rust/src/common.rs @@ -1,23 +1,27 @@ -/* - * Copyright (C) 2023 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. - */ +// Copyright (C) 2023 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. #![allow(unused_must_use)] extern crate request; -use request::{enumration::*, form_item::*, task_config::*, task_manager::*}; -use std::{collections::HashMap, fs::File, sync::Arc}; +use std::collections::HashMap; +use std::fs::File; +use std::sync::Arc; + +use request::enumration::*; +use request::form_item::*; +use request::task_config::*; +use request::task_manager::*; pub fn construct_download_task( task_id: &mut u32, diff --git a/test/unittest/rust/src/main.rs b/test/unittest/rust/src/main.rs index 7c64a4a7..4753bf56 100644 --- a/test/unittest/rust/src/main.rs +++ b/test/unittest/rust/src/main.rs @@ -1,273 +1,257 @@ -/* - * Copyright (C) 2023 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. - */ - -#![allow(unused_assignments)] -mod common; -extern crate request; -use request::{enumration::*, task_manager::*}; - -static MAX_TASK_COUNT_API10: u32 = 300; -static MAX_TASK_COUNT_EACH_APP_API10: u32 = 10; - -#[test] -fn create_test1() { - let task_manager = TaskManager::get_instance(); - let mut task_id: u32 = 0; - let uid: u64 = 10; - let mut paths: Vec = Vec::new(); - for i in 0..10 { - let file_name = format!("create_test1{}", i); - let code = common::construct_download_task( - &mut task_id, - uid, - file_name.as_str(), - Mode::BACKGROUND, - Version::API10, - ); - paths.push(file_name); - assert_eq!(code, ErrorCode::ErrOk); - } - let code = common::construct_download_task( - &mut task_id, - uid, - "test", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::TaskEnqueueErr); - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_EACH_APP_API10 - ); - common::remove_files(paths); -} - -#[test] -fn create_test2() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - assert_eq!(task_manager.get_api10_background_task_count(), 0); - let mut task_id: u32 = 0; - let mut paths: Vec = Vec::new(); - for i in 0..300 { - let file_name = format!("create_test2{}", i); - let code = common::construct_download_task( - &mut task_id, - i, - file_name.as_str(), - Mode::BACKGROUND, - Version::API10, - ); - paths.push(file_name); - assert_eq!(code, ErrorCode::ErrOk); - } - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_API10 - ); - let code = common::construct_download_task( - &mut task_id, - 11, - "test", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::TaskEnqueueErr); - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_API10 - ); - common::remove_files(paths); -} - -#[test] -fn create_test3() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id: u32 = 0; - let uid: u64 = 10; - let mut paths: Vec = Vec::new(); - for i in 0..10 { - let file_name = format!("create_test3{}", i); - let code = common::construct_download_task( - &mut task_id, - uid, - file_name.as_str(), - Mode::BACKGROUND, - Version::API10, - ); - paths.push(file_name); - assert_eq!(code, ErrorCode::ErrOk); - } - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_EACH_APP_API10 - ); - let mut code = common::construct_download_task( - &mut task_id, - uid, - "test", - Mode::FRONTEND, - Version::API10, - ); - assert_eq!(code, ErrorCode::TaskModeErr); - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_EACH_APP_API10 - ); - code = common::construct_download_task( - &mut task_id, - uid, - "test", - Mode::BACKGROUND, - Version::API9, - ); - assert_eq!(code, ErrorCode::ErrOk); - assert_eq!( - task_manager.get_api10_background_task_count(), - MAX_TASK_COUNT_EACH_APP_API10 - ); - common::remove_files(paths); -} - -#[test] -fn start_test1() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id: u32 = 0; - let uid: u64 = 1; - let mut code = common::construct_download_task( - &mut task_id, - uid, - "test_file", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::ErrOk); - assert_eq!(task_manager.get_api10_background_task_count(), 1); - code = task_manager.start(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.start(uid, task_id); - assert_eq!(code, ErrorCode::TaskStateErr); - code = task_manager.start(uid, task_id + 1); - assert_eq!(code, ErrorCode::TaskStateErr); -} - -#[test] -fn pause_test() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id: u32 = 0; - let uid: u64 = 1; - let mut code = common::construct_download_task( - &mut task_id, - uid, - "test_file", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.pause(uid, task_id); - assert_eq!(code, ErrorCode::TaskStateErr); - code = task_manager.start(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.pause(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); -} - -#[test] -fn resume_test() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id: u32 = 0; - let uid: u64 = 1; - let mut code = common::construct_download_task( - &mut task_id, - uid, - "test_file", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.resume(uid, task_id); - assert_eq!(code, ErrorCode::TaskStateErr); - task_manager.start(uid, task_id); - code = task_manager.pause(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.resume(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); -} - -#[test] -fn stop_test() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id: u32 = 0; - let uid: u64 = 1; - let mut code = common::construct_download_task( - &mut task_id, - uid, - "test_file", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.stop(uid, task_id); - assert_eq!(code, ErrorCode::TaskStateErr); - task_manager.start(uid, task_id); - code = task_manager.stop(uid, task_id); - assert_eq!(code, ErrorCode::ErrOk); - code = common::construct_download_task( - &mut task_id, - uid, - "test_file", - Mode::BACKGROUND, - Version::API9, - ); - assert_eq!(code, ErrorCode::ErrOk); - task_manager.start(uid, task_id); - code = task_manager.stop(uid, task_id); - assert_eq!(code, ErrorCode::TaskStateErr); -} - -#[test] -fn remove_test() { - let task_manager = TaskManager::get_instance(); - task_manager.clear_all_task(); - let mut task_id1: u32 = 0; - let mut task_id2: u32 = 0; - let uid: u64 = 1; - let mut code = common::construct_download_task( - &mut task_id1, - uid, - "test_file1", - Mode::BACKGROUND, - Version::API10, - ); - assert_eq!(code, ErrorCode::ErrOk); - code = common::construct_download_task( - &mut task_id2, - uid, - "test_file2", - Mode::BACKGROUND, - Version::API9, - ); - assert_eq!(code, ErrorCode::ErrOk); - assert_eq!(task_manager.get_api10_background_task_count(), 1); - code = task_manager.remove(uid, task_id1); - assert_eq!(code, ErrorCode::ErrOk); - code = task_manager.remove(uid, task_id2); - assert_eq!(code, ErrorCode::ErrOk); -} +// Copyright (C) 2023 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. + +#![allow(unused_assignments)] +mod common; +extern crate request; +use request::enumration::*; +use request::task_manager::*; + +const MAX_TASK_COUNT_API10: u32 = 300; +const MAX_TASK_COUNT_EACH_APP_API10: u32 = 10; + +#[test] +fn create_test1() { + let task_manager = TaskManager::get_instance(); + let mut task_id: u32 = 0; + let uid: u64 = 10; + let mut paths: Vec = Vec::new(); + for i in 0..10 { + let file_name = format!("create_test1{}", i); + let code = common::construct_download_task( + &mut task_id, + uid, + file_name.as_str(), + Mode::BACKGROUND, + Version::API10, + ); + paths.push(file_name); + assert_eq!(code, ErrorCode::ErrOk); + } + let code = common::construct_download_task( + &mut task_id, + uid, + "test", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::TaskEnqueueErr); + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_EACH_APP_API10 + ); + common::remove_files(paths); +} + +#[test] +fn create_test2() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + assert_eq!(task_manager.get_api10_background_task_count(), 0); + let mut task_id: u32 = 0; + let mut paths: Vec = Vec::new(); + for i in 0..300 { + let file_name = format!("create_test2{}", i); + let code = common::construct_download_task( + &mut task_id, + i, + file_name.as_str(), + Mode::BACKGROUND, + Version::API10, + ); + paths.push(file_name); + assert_eq!(code, ErrorCode::ErrOk); + } + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_API10 + ); + let code = + common::construct_download_task(&mut task_id, 11, "test", Mode::BACKGROUND, Version::API10); + assert_eq!(code, ErrorCode::TaskEnqueueErr); + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_API10 + ); + common::remove_files(paths); +} + +#[test] +fn create_test3() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id: u32 = 0; + let uid: u64 = 10; + let mut paths: Vec = Vec::new(); + for i in 0..10 { + let file_name = format!("create_test3{}", i); + let code = common::construct_download_task( + &mut task_id, + uid, + file_name.as_str(), + Mode::BACKGROUND, + Version::API10, + ); + paths.push(file_name); + assert_eq!(code, ErrorCode::ErrOk); + } + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_EACH_APP_API10 + ); + let mut code = + common::construct_download_task(&mut task_id, uid, "test", Mode::FRONTEND, Version::API10); + assert_eq!(code, ErrorCode::TaskModeErr); + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_EACH_APP_API10 + ); + code = + common::construct_download_task(&mut task_id, uid, "test", Mode::BACKGROUND, Version::API9); + assert_eq!(code, ErrorCode::ErrOk); + assert_eq!( + task_manager.get_api10_background_task_count(), + MAX_TASK_COUNT_EACH_APP_API10 + ); + common::remove_files(paths); +} + +#[test] +fn start_test1() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id: u32 = 0; + let uid: u64 = 1; + let mut code = common::construct_download_task( + &mut task_id, + uid, + "test_file", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::ErrOk); + assert_eq!(task_manager.get_api10_background_task_count(), 1); + code = task_manager.start(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.start(uid, task_id); + assert_eq!(code, ErrorCode::TaskStateErr); + code = task_manager.start(uid, task_id + 1); + assert_eq!(code, ErrorCode::TaskStateErr); +} + +#[test] +fn pause_test() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id: u32 = 0; + let uid: u64 = 1; + let mut code = common::construct_download_task( + &mut task_id, + uid, + "test_file", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.pause(uid, task_id); + assert_eq!(code, ErrorCode::TaskStateErr); + code = task_manager.start(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.pause(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); +} + +#[test] +fn resume_test() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id: u32 = 0; + let uid: u64 = 1; + let mut code = common::construct_download_task( + &mut task_id, + uid, + "test_file", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.resume(uid, task_id); + assert_eq!(code, ErrorCode::TaskStateErr); + task_manager.start(uid, task_id); + code = task_manager.pause(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.resume(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); +} + +#[test] +fn stop_test() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id: u32 = 0; + let uid: u64 = 1; + let mut code = common::construct_download_task( + &mut task_id, + uid, + "test_file", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.stop(uid, task_id); + assert_eq!(code, ErrorCode::TaskStateErr); + task_manager.start(uid, task_id); + code = task_manager.stop(uid, task_id); + assert_eq!(code, ErrorCode::ErrOk); + code = common::construct_download_task( + &mut task_id, + uid, + "test_file", + Mode::BACKGROUND, + Version::API9, + ); + assert_eq!(code, ErrorCode::ErrOk); + task_manager.start(uid, task_id); + code = task_manager.stop(uid, task_id); + assert_eq!(code, ErrorCode::TaskStateErr); +} + +#[test] +fn remove_test() { + let task_manager = TaskManager::get_instance(); + task_manager.clear_all_task(); + let mut task_id1: u32 = 0; + let mut task_id2: u32 = 0; + let uid: u64 = 1; + let mut code = common::construct_download_task( + &mut task_id1, + uid, + "test_file1", + Mode::BACKGROUND, + Version::API10, + ); + assert_eq!(code, ErrorCode::ErrOk); + code = common::construct_download_task( + &mut task_id2, + uid, + "test_file2", + Mode::BACKGROUND, + Version::API9, + ); + assert_eq!(code, ErrorCode::ErrOk); + assert_eq!(task_manager.get_api10_background_task_count(), 1); + code = task_manager.remove(uid, task_id1); + assert_eq!(code, ErrorCode::ErrOk); + code = task_manager.remove(uid, task_id2); + assert_eq!(code, ErrorCode::ErrOk); +}