diff --git a/bundle.json b/bundle.json index 6496979c596c2b325520e7bb46d167f1d8b7762b..2a9baa4d20a9399c6d15f35b81ee8006ae9656c9 100644 --- a/bundle.json +++ b/bundle.json @@ -88,7 +88,8 @@ "drivers_peripheral_display", "selinux_adapter", "runtime_core", - "jsoncpp" + "jsoncpp", + "os_account" ] }, "build": { @@ -97,14 +98,17 @@ "//base/web/webview/interfaces/kits/cj:cj_webview_ffi", "//base/web/webview/interfaces/kits/napi:webview_napi", "//base/web/webview/interfaces/kits/napi:neterrorlist_napi", + "//base/web/webview/interfaces/kits/napi:webnativemessagingextensionmanager_napi", "//base/web/webview/interfaces/native:webview_ndk", + "//base/web/webview/interfaces/native/web_native_messaging_extension:web_native_messaging_extension", "//base/web/webview/ohos_glue:ohos_base_glue_source", "//base/web/webview/ohos_nweb:chrome_crashpad_handler", "//base/web/webview/ohos_nweb:libnweb", "//base/web/webview/ohos_nweb:nweb_hap", "//base/web/webview/ohos_adapter:nweb_ohos_adapter", "//base/web/webview/ohos_wrapper:nweb_ohos_wrapper", - "//base/web/webview/sa:app_fwk_update_service", + "//base/web/webview/sa/app_fwk_update:app_fwk_update_service", + "//base/web/webview/sa/web_native_messaging:web_native_messaging", "//base/web/webview/arkweb_utils:libarkweb_utils" ], "inner_kits": [ @@ -142,9 +146,18 @@ "header_files": [ "app_fwk_update_client.h" ], - "header_base": "//base/web/webview/sa/include" + "header_base": "//base/web/webview/sa/app_fwk_update/include" + }, + "name": "//base/web/webview/sa/app_fwk_update:app_fwk_update_service" + }, + { + "header": { + "header_files": [ + "web_native_messaging_client.h" + ], + "header_base": "//base/web/webview/sa/web_native_messaging/client" }, - "name": "//base/web/webview/sa:app_fwk_update_service" + "name": "//base/web/webview/sa/web_native_messaging:web_native_messaging" }, { "header": { diff --git a/interfaces/kits/napi/BUILD.gn b/interfaces/kits/napi/BUILD.gn index bf882224fc6f69de71b0b40ec705f3494c9d25ee..a52b52c38b165bbedfe2f60b089dda34f4a5ac07 100644 --- a/interfaces/kits/napi/BUILD.gn +++ b/interfaces/kits/napi/BUILD.gn @@ -26,10 +26,13 @@ config("web_public_config") { "webfunction", "webstorage", "webviewcontroller", + "web_native_messaging_extension_manager", "$target_gen_dir/protos", "../../native", "../../../ohos_adapter/ohos_resource_adapter/include", "../../../ohos_adapter/system_properties_adapter/include", + "../../../sa/web_native_messaging/client", + "../../../sa/web_native_messaging/common" ] } @@ -155,3 +158,47 @@ ohos_shared_library("neterrorlist_napi") { part_name = "webview" subsystem_name = "web" } + +ohos_shared_library("webnativemessagingextensionmanager_napi") { + if (target_cpu == "arm64") { + branch_protector_ret = "pac_ret" + } + + public_configs = [ ":web_public_config" ] + + sources = [ + "common/business_error.cpp", + "common/napi_parse_utils.cpp", + "common/napi_extension_manager_init.cpp", + "web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.cpp", + "../../../sa/web_native_messaging/common/web_native_messaging_common.cpp" + ] + + use_exceptions = true + + external_deps = [ + "ability_runtime:abilitykit_native", + "ability_runtime:ability_context_native", + "ability_runtime:app_context", + "ability_runtime:napi_base_context", + "ability_runtime:napi_common", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + ] + + deps = [ + "../nativecommon:webview_common", + "../../../arkweb_utils:libarkweb_utils", + "../../../ohos_adapter:nweb_ohos_adapter", + "../../../ohos_nweb:libnweb", + "../../../sa/web_native_messaging:web_native_messaging_kit", + "../../native:ohweb", + ] + + relative_install_dir = "module/web" + + part_name = "webview" + subsystem_name = "web" +} diff --git a/interfaces/kits/napi/common/business_error.cpp b/interfaces/kits/napi/common/business_error.cpp index 04aae14e9f63d3da0273ec4d5181afa164e9dae6..f4bffe9e501b81d9a0d82f8bd46cb63452f30411 100644 --- a/interfaces/kits/napi/common/business_error.cpp +++ b/interfaces/kits/napi/common/business_error.cpp @@ -20,13 +20,18 @@ namespace OHOS { namespace NWebError { napi_value BusinessError::CreateError(napi_env env, int32_t err) +{ + return CreateError(env, err, GetErrMsgByErrCode(err)); +} + +napi_value BusinessError::CreateError(napi_env env, int32_t err, const std::string& errorMsg) { napi_value businessError = nullptr; NAPI_CALL(env, napi_create_object(env, &businessError)); napi_value errorCode = nullptr; NAPI_CALL(env, napi_create_int32(env, err, &errorCode)); napi_value errorMessage = nullptr; - NAPI_CALL(env, napi_create_string_utf8(env, GetErrMsgByErrCode(err).c_str(), NAPI_AUTO_LENGTH, &errorMessage)); + NAPI_CALL(env, napi_create_string_utf8(env, errorMsg.c_str(), NAPI_AUTO_LENGTH, &errorMessage)); NAPI_CALL(env, napi_set_named_property(env, businessError, "code", errorCode)); NAPI_CALL(env, napi_set_named_property(env, businessError, "message", errorMessage)); return businessError; @@ -42,4 +47,4 @@ void BusinessError::ThrowErrorByErrcode(napi_env env, int32_t errCode, const std napi_throw_error(env, std::to_string(errCode).c_str(), errorMsg.c_str()); } } -} \ No newline at end of file +} diff --git a/interfaces/kits/napi/common/business_error.h b/interfaces/kits/napi/common/business_error.h index db81f85436d996ed7b742876a73fa55b939d4150..4c39956f84377b6b52896652f2e3f7f5ae8d6dee 100644 --- a/interfaces/kits/napi/common/business_error.h +++ b/interfaces/kits/napi/common/business_error.h @@ -30,10 +30,12 @@ static inline void ThrowError(napi_env env, int32_t err, const std::string& msg) static napi_value CreateError(napi_env env, int32_t err); +static napi_value CreateError(napi_env env, int32_t err, const std::string& errorMsg); + static void ThrowErrorByErrcode(napi_env env, int32_t errCode); static void ThrowErrorByErrcode(napi_env env, int32_t errCode, const std::string& errorMsg); }; } } -#endif \ No newline at end of file +#endif diff --git a/interfaces/kits/napi/common/napi_extension_manager_init.cpp b/interfaces/kits/napi/common/napi_extension_manager_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a4e7ac2dcfdb1a3dc867dfb9af801702b24050e --- /dev/null +++ b/interfaces/kits/napi/common/napi_extension_manager_init.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "napi_web_native_messaging_extension_manager.h" + +namespace OHOS { +namespace NWeb { +namespace { +napi_value NapiExtensionManagerInit(napi_env env, napi_value exports) +{ + napi_property_descriptor resultFuncs[] = { + DECLARE_NAPI_FUNCTION("connectNative", NapiWebNativeMessagingExtensionManager::ConnectNative), + DECLARE_NAPI_FUNCTION("disconnectNative", NapiWebNativeMessagingExtensionManager::DisconnectNative), + }; + napi_define_properties(env, exports, sizeof(resultFuncs) / sizeof(resultFuncs[0]), resultFuncs); + NapiWebNativeMessagingExtensionManager::Init(); + return exports; +} + +napi_module *GetNapiExtensionManagerModule(void) +{ + static ::napi_module napiModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = NapiExtensionManagerInit, + .nm_modname = "web.webNativeMessagingExtensionManager", + .nm_priv = ((void *)0), + .reserved = {0} + }; + return &napiModule; +} +} // namespace + +/* + * module register + */ +extern "C" __attribute__((constructor)) void Register(void) +{ + napi_module_register(GetNapiExtensionManagerModule()); +} +} // namespace NWeb +} // namesapce OHOS diff --git a/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.cpp b/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c7e062195531d2ea41d301ba7ef48e07ea603b8 --- /dev/null +++ b/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.cpp @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "napi_web_native_messaging_extension_manager.h" + +#include +#include +#include +#include + +#include "ability.h" +#include "business_error.h" +#include "context.h" +#include "napi_base_context.h" +#include "napi_common_want.h" +#include "nweb_napi_scope.h" +#include "napi_parse_utils.h" +#include "securec.h" +#include "system_properties_adapter_impl.h" +#include "web_extension_connection_callback.h" +#include "web_native_messaging_client.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { + +namespace { +std::recursive_mutex gConnectsLock_; +int32_t g_serialNumber = 1; +static std::map> g_connects; +} + +static void RemoveConnection(int32_t connectId) +{ + std::lock_guard lock(gConnectsLock_); + auto item = g_connects.find(connectId); + if (item != g_connects.end()) { + WNMLOG_D("remove connection %{public}d ok", connectId); + g_connects.erase(item); + } else { + WNMLOG_I("remove connection %{public}d not exist", connectId); + } +} + +static int32_t InsertConnection(sptr connection) +{ + std::lock_guard lock(gConnectsLock_); + if (connection == nullptr) { + WNMLOG_E("insert null connection"); + return -1; + } + int32_t connectId = g_serialNumber; + g_connects.emplace(connectId, connection); + if (g_serialNumber < INT32_MAX) { + g_serialNumber++; + } else { + g_serialNumber = 1; + } + connection->connectionId_ = connectId; + WNMLOG_D("insert connection %{public}d", connectId); + return connectId; +} + +CommonAsyncContext::CommonAsyncContext(napi_env napiEnv) +{ + env = napiEnv; +} + +CommonAsyncContext::~CommonAsyncContext() +{ + if (callbackRef) { + WNMLOG_D("~CommonAsyncContext delete callbackRef"); + napi_delete_reference(env, callbackRef); + callbackRef = nullptr; + } + if (work) { + WNMLOG_D("~CommonAsyncContext delete work"); + napi_delete_async_work(env, work); + work = nullptr; + } +} + +static napi_value FillJsConnectionNativeInfo(napi_env env, const ConnectionNativeInfo& info) +{ + napi_value jsInfoObj = nullptr; + napi_create_object(env, &jsInfoObj); + + napi_value jsConnectId = {0}; + napi_create_int32(env, info.connectionId, &jsConnectId); + + napi_value jsBundleName = {0}; + napi_create_string_utf8(env, info.bundleName.c_str(), info.bundleName.length(), &jsBundleName); + + napi_value jsExtensionOrigin = {0}; + napi_create_string_utf8(env, info.extensionOrigin.c_str(), info.extensionOrigin.length(), &jsExtensionOrigin); + + napi_value jsExtensionPid = {0}; + napi_create_int32(env, info.extensionPid, &jsExtensionPid); + + napi_set_named_property(env, jsInfoObj, "connectionId", jsConnectId); + napi_set_named_property(env, jsInfoObj, "bundleName", jsBundleName); + napi_set_named_property(env, jsInfoObj, "extensionOrigin", jsExtensionOrigin); + napi_set_named_property(env, jsInfoObj, "extensionPid", jsExtensionPid); + return jsInfoObj; +} + +static bool InvokeConnectNativeCallback(NapiExtensionConnectionCallbackParam* param) +{ + NApiScope scope(param->env_); + if (!scope.IsVaild()) { + WNMLOG_E("scope is null"); + return false; + } + if (!param) { + WNMLOG_E("param is invalid"); + return false; + } + + napi_value result[INTEGER_ONE] = {0}; + napi_value onCallbackFunc = nullptr; + switch (param->type_) { + case ConnectCallbackType::ON_CONNECT_TYPE: + result[INTEGER_ZERO] = FillJsConnectionNativeInfo(param->env_, param->result_); + napi_get_reference_value(param->env_, param->callback_.onConnectMethodRef, &onCallbackFunc); + break; + case ConnectCallbackType::ON_DISCONNECT_TYPE: + RemoveConnection(param->result_.connectionId); + result[INTEGER_ZERO] = FillJsConnectionNativeInfo(param->env_, param->result_); + napi_get_reference_value(param->env_, param->callback_.onDisconnectMethodRef, &onCallbackFunc); + break; + case ConnectCallbackType::ON_FAILED_TYPE: + RemoveConnection(param->result_.connectionId); + napi_create_int32(param->env_, param->errorNum_, &result[INTEGER_ZERO]); + napi_get_reference_value(param->env_, param->callback_.onFailedMethodRef, &onCallbackFunc); + break; + default: + WNMLOG_E("type %{public}d is invalid", param->type_); + return false; + } + napi_value placeHodler = nullptr; + napi_call_function(param->env_, nullptr, onCallbackFunc, INTEGER_ONE, &result[INTEGER_ZERO], &placeHodler); + return true; +} + +static void UvInvokeConnectNativeCallback(uv_work_t* work, int status) +{ + if (work == nullptr) { + WNMLOG_E("uv work is null"); + return; + } + auto* data = reinterpret_cast(work->data); + if (data == nullptr) { + WNMLOG_E("extension connection callback param is null"); + delete work; + return; + } + InvokeConnectNativeCallback(data); + delete data; + delete work; +} + +void NapiExtensionConnectionCallback::DoJsExtensionConnectCallback( + ConnectionNativeInfo& info, int32_t errorNum, ConnectCallbackType type) +{ + uv_loop_s* loop = nullptr; + napi_get_uv_event_loop(env_, &loop); + auto engine = reinterpret_cast(env_); + if (loop == nullptr) { + WNMLOG_E("get uv event loop failed"); + return; + } + + auto param = std::make_unique(); + if (param == nullptr) { + WNMLOG_E("new NapiExtensionConnectionCallbackParam failed"); + return; + } + + param->type_ = type; + param->env_ = env_; + param->callback_ = jsCallback_; + param->result_ = info; + param->errorNum_ = errorNum; + param->caller = shared_from_this(); + if (pthread_self() == engine->GetTid()) { + InvokeConnectNativeCallback(param.get()); + } else { + std::unique_ptr work = std::make_unique(); + if (work == nullptr) { + WNMLOG_E("new uv work failed"); + return; + } + + work->data = reinterpret_cast(param.get()); + int ret = uv_queue_work_with_qos( + loop, work.get(), [](uv_work_t* work) {}, UvInvokeConnectNativeCallback, uv_qos_user_initiated); + if (ret == 0) { + work.release(); + param.release(); + } + } +} + +static void DoDeleteJsCall(DeleteJsCallParam* param) +{ + if (!param) { + return; + } + auto env = param->env_; + auto jsCallback = param->jsCallback_; + if (jsCallback.onConnectMethodRef) { + napi_delete_reference(env, jsCallback.onConnectMethodRef); + } + if (jsCallback.onDisconnectMethodRef) { + napi_delete_reference(env, jsCallback.onDisconnectMethodRef); + } + if (jsCallback.onFailedMethodRef) { + napi_delete_reference(env, jsCallback.onFailedMethodRef); + } +} + +static void UvInvokeDeleteJsCall(uv_work_t* work, int status) +{ + if (work == nullptr) { + WNMLOG_E("uv work is null"); + return; + } + auto* data = reinterpret_cast(work->data); + if (data == nullptr) { + WNMLOG_E("delete connection callback param is null"); + delete work; + return; + } + DoDeleteJsCall(data); + delete data; + delete work; +} + +void NapiExtensionConnectionCallback::DeleteJsCallInJsThread() +{ + uv_loop_s* loop = nullptr; + napi_get_uv_event_loop(env_, &loop); + auto engine = reinterpret_cast(env_); + if (loop == nullptr) { + WNMLOG_E("get uv event loop failed"); + return; + } + + auto param = std::make_unique(); + if (param == nullptr) { + WNMLOG_E("new NapiExtensionConnectionCallbackParam failed"); + return; + } + + param->env_ = env_; + param->jsCallback_ = jsCallback_; + if (pthread_self() == engine->GetTid()) { + DoDeleteJsCall(param.get()); + } else { + std::unique_ptr work = std::make_unique(); + if (work == nullptr) { + WNMLOG_E("new uv work failed"); + return; + } + + work->data = reinterpret_cast(param.get()); + int ret = uv_queue_work_with_qos( + loop, work.get(), [](uv_work_t* work) {}, UvInvokeDeleteJsCall, uv_qos_user_initiated); + if (ret == 0) { + work.release(); + param.release(); + } + } +} + +void NapiExtensionConnectionCallback::OnExtensionConnect(ConnectionNativeInfo& info) +{ + DoJsExtensionConnectCallback(info, 0, ConnectCallbackType::ON_CONNECT_TYPE); +} + +void NapiExtensionConnectionCallback::OnExtensionDisconnect(ConnectionNativeInfo& info) +{ + DoJsExtensionConnectCallback(info, 0, ConnectCallbackType::ON_DISCONNECT_TYPE); +} + +void NapiExtensionConnectionCallback::OnExtensionFailed(int32_t connectionId, int32_t errorNum) +{ + ConnectionNativeInfo info; + info.connectionId = connectionId; + DoJsExtensionConnectCallback(info, errorNum, ConnectCallbackType::ON_FAILED_TYPE); +} + +static bool ParseConnectionCallbackFunction(napi_env env, napi_value preArgs, + const std::string& funcName, napi_ref& methodRef) +{ + napi_value methodVal = nullptr; + napi_get_named_property(env, preArgs, funcName.c_str(), &methodVal); + + if (!methodVal) { + return false; + } + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, methodVal, &valueType); + if (valueType != napi_function) { + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + NWebError::FormatString(ParamCheckErrorMsgTemplate::TYPE_ERROR, "callback", "function")); + return false; + } + + napi_create_reference(env, methodVal, INTEGER_ONE, &methodRef); + return true; +} + +static sptr ParseNapiExtensionConnectionCallback(napi_env env, napi_value preArgs) +{ + JsConnectCallback jsCallback; + if (!ParseConnectionCallbackFunction(env, preArgs, "onConnect", jsCallback.onConnectMethodRef)) { + return nullptr; + } + if (!ParseConnectionCallbackFunction(env, preArgs, "onDisconnect", jsCallback.onDisconnectMethodRef)) { + napi_delete_reference(env, jsCallback.onConnectMethodRef); + return nullptr; + } + if (!ParseConnectionCallbackFunction(env, preArgs, "onFailed", jsCallback.onFailedMethodRef)) { + napi_delete_reference(env, jsCallback.onConnectMethodRef); + napi_delete_reference(env, jsCallback.onDisconnectMethodRef); + return nullptr; + } + + auto callback = std::make_shared(env, jsCallback); + if (!callback) { + napi_delete_reference(env, jsCallback.onConnectMethodRef); + napi_delete_reference(env, jsCallback.onDisconnectMethodRef); + napi_delete_reference(env, jsCallback.onFailedMethodRef); + return nullptr; + } + auto callbackRemote = new (std::nothrow) WebExtensionConnectionCallback(callback); + if (!callbackRemote) { + WNMLOG_E("new WebExtensionConnectionCallback failed"); + return nullptr; + } + return callbackRemote; +} + +void NapiWebNativeMessagingExtensionManager::ConnectNativeExcute(napi_env env, void* data) +{ + WNMLOG_D("connectNative excute"); + auto asyncContext = reinterpret_cast(data); + if (asyncContext == nullptr) { + WNMLOG_E("asyncContext is null"); + return; + } + asyncContext->errCode = WebNativeMessagingClient::GetInstance().ConnectWebNativeMessagingExtension( + asyncContext->token, asyncContext->want, asyncContext->connectCallback, + asyncContext->connectId); +} + +void NapiWebNativeMessagingExtensionManager::ConnectNativeComplete(napi_env env, napi_status status, void* data) +{ + WNMLOG_D("connectNative complete"); + auto asyncContext = reinterpret_cast(data); + if (asyncContext == nullptr || !asyncContext->connectCallback) { + WNMLOG_E("asyncContext is invalid"); + return; + } + + std::unique_ptr asyncContextPtr { asyncContext }; + if (asyncContext->errCode != 0) { + asyncContext->connectCallback->OnFailed(asyncContext->errCode); + } +} + +static bool ParseUiAbilityContextToken(napi_env env, napi_value param, sptr& token) +{ + bool stageMode = false; + napi_status status = AbilityRuntime::IsStageContext(env, param, stageMode); + if (status != napi_ok || !stageMode) { + WNMLOG_E("not stage mode"); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "Parse param context failed, must be a context of stageMode."); + return false; + } + auto context = AbilityRuntime::GetStageModeContext(env, param); + if (context == nullptr) { + WNMLOG_E("parse param context is null"); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "Parse param context failed, must not be nullptr."); + return false; + } + auto uiAbilityContext = AbilityRuntime::Context::ConvertTo(context); + if (uiAbilityContext == nullptr) { + WNMLOG_E("parse param context is not abilityContext"); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "Parse param context failed, must be UIAbilityContext."); + return false; + } + + token = uiAbilityContext->GetToken(); + return true; +} + +napi_value NapiWebNativeMessagingExtensionManager::ConnectNative(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + napi_create_int32(env, -1, &result); + if (SystemPropertiesAdapterImpl::GetInstance().GetProductDeviceType() != ProductDeviceType::DEVICE_TYPE_2IN1) { + WNMLOG_E("ConnectNative: Capability not supported."); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::CAPABILITY_NOT_SUPPORTED_ERROR); + return result; + } + size_t argc = INTEGER_THREE; + napi_value argv[INTEGER_THREE] = {0}; + napi_value thisVar = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr); + if (argc != INTEGER_THREE) { + WNMLOG_E("BusinessError: 401. Arg count must be 3."); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, "Arg count must be 3"); + return result; + } + auto* asyncContext = new (std::nothrow) ConnectNativeAsyncContext(env); + if (asyncContext == nullptr) { + WNMLOG_E("insufficient memory for asyncContext."); + return result; + } + std::unique_ptr asyncContextPtr { asyncContext }; + if (!ParseUiAbilityContextToken(env, argv[INTEGER_ZERO], asyncContext->token)) { + return result; + } + if (!AppExecFwk::UnwrapWant(env, argv[INTEGER_ONE], asyncContext->want)) { + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "Parse param want failed, must be a want"); + return result; + } + + asyncContext->connectCallback = ParseNapiExtensionConnectionCallback(env, argv[INTEGER_TWO]); + if (asyncContext->connectCallback == nullptr) { + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "Parse param callback failed."); + return result; + } + + asyncContext->connectId = InsertConnection(asyncContext->connectCallback); + napi_create_int32(env, asyncContext->connectId, &result); + + napi_value resource = nullptr; + NAPI_CALL(env, napi_create_string_utf8(env, "ConnectNative", NAPI_AUTO_LENGTH, &resource)); + NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, ConnectNativeExcute, ConnectNativeComplete, + static_cast(asyncContext), &(asyncContext->work))); + NAPI_CALL(env, napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated)); + asyncContextPtr.release(); + return result; +} + +void NapiWebNativeMessagingExtensionManager::DisconnectNativeExcute(napi_env env, void* data) +{ + WNMLOG_D("disconnect native excute"); + auto asyncContext = reinterpret_cast(data); + if (asyncContext == nullptr) { + WNMLOG_E("asyncContext is nullptr"); + return; + } + asyncContext->errCode = WebNativeMessagingClient::GetInstance().DisconnectWebNativeMessagingExtension( + asyncContext->connectId); +} + +void NapiWebNativeMessagingExtensionManager::DisconnectNativeComplete(napi_env env, napi_status status, void* data) +{ + WNMLOG_D("disconnect native complete"); + auto asyncContext = reinterpret_cast(data); + if (asyncContext == nullptr) { + WNMLOG_E("asyncContext is nullptr"); + return; + } + + std::unique_ptr asyncContextPtr { asyncContext }; + napi_value result; + NAPI_CALL_RETURN_VOID(env, napi_get_null(env, &result)); + if (asyncContext->errCode != 0) { + std::string ErrorMesg = ""; + int32_t jsError = NativeMessageError::NativeCodeToJsCode(asyncContext->errCode, ErrorMesg); + napi_value businessError = NWebError::BusinessError::CreateError(env, jsError, ErrorMesg); + NAPI_CALL_RETURN_VOID(env, napi_reject_deferred(env, asyncContextPtr->deferred, businessError)); + } else { + NAPI_CALL_RETURN_VOID(env, napi_resolve_deferred(env, asyncContextPtr->deferred, result)); + } +} + +napi_value NapiWebNativeMessagingExtensionManager::DisconnectNative(napi_env env, napi_callback_info info) +{ + if (SystemPropertiesAdapterImpl::GetInstance().GetProductDeviceType() != ProductDeviceType::DEVICE_TYPE_2IN1) { + WNMLOG_E("DisconnectNative: Capability not supported."); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::CAPABILITY_NOT_SUPPORTED_ERROR); + return nullptr; + } + + size_t argc = INTEGER_ONE; + napi_value argv[INTEGER_ONE] = {0}; + napi_value thisVar = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr); + + if (argc != INTEGER_ONE) { + WNMLOG_E("BusinessError: 401. Arg count must be 1."); + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, "Arg count must be 1"); + return nullptr; + } + + auto* asyncContext = new (std::nothrow) DisconnectNativeAsyncContext(env); + if (asyncContext == nullptr) { + WNMLOG_E("insufficient memory for asyncContext."); + return nullptr; + } + std::unique_ptr asyncContextPtr { asyncContext }; + + if (!NapiParseUtils::ParseInt32(env, argv[INTEGER_ZERO], asyncContext->connectId) || + (asyncContext->connectId < 0)) { + NWebError::BusinessError::ThrowErrorByErrcode(env, NWebError::PARAM_CHECK_ERROR, + "The type of 'connectionId' must be int and must be >= 0"); + return nullptr; + } + + napi_value result = nullptr; + NAPI_CALL(env, napi_create_promise(env, &asyncContext->deferred, &result)); + napi_value resource = nullptr; + NAPI_CALL(env, napi_create_string_utf8(env, "DisconnectNative", NAPI_AUTO_LENGTH, &resource)); + NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, DisconnectNativeExcute, DisconnectNativeComplete, + static_cast(asyncContext), &(asyncContext->work))); + NAPI_CALL(env, napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated)); + asyncContextPtr.release(); + return result; +} + +void NapiWebNativeMessagingExtensionManager::Init() +{ + WebNativeMessagingClient::GetInstance().SetUserDefineDiedRecipient([](){ + std::lock_guard lock(gConnectsLock_); + for (auto iter = g_connects.begin(); iter != g_connects.end();) { + WNMLOG_I("remove connection %{public}d because of service death", iter->first); + if (iter->second) { + iter->second->OnFailed(ConnectNativeRet::SERVICE_DIED_ERROR); + } + iter = g_connects.erase(iter); + } + }); +} +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.h b/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..5067fd073bcb9ac020ae880e8c5e56f7e03ab280 --- /dev/null +++ b/interfaces/kits/napi/web_native_messaging_extension_manager/napi_web_native_messaging_extension_manager.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef NWEB_NAPI_WEB_NATIVE_MESSAGING_EXTENSION_MANAGER_H +#define NWEB_NAPI_WEB_NATIVE_MESSAGING_EXTENSION_MANAGER_H + +#include + +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" +#include "want.h" +#include "web_extension_connection_callback.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +struct JsConnectCallback { + napi_ref onConnectMethodRef = nullptr; + napi_ref onDisconnectMethodRef = nullptr; + napi_ref onFailedMethodRef = nullptr; +}; + +enum ConnectCallbackType : int32_t { + ON_CONNECT_TYPE = 0, + ON_DISCONNECT_TYPE = 1, + ON_FAILED_TYPE = 2 +}; + +class NapiExtensionConnectionCallback : public IExtensionConnectionCallback, + public std::enable_shared_from_this { +public: + NapiExtensionConnectionCallback(napi_env env, JsConnectCallback jsCallback) : + env_(env), jsCallback_(jsCallback) {}; + virtual ~NapiExtensionConnectionCallback() + { + WNMLOG_D("~NapiExtensionConnectionCallback"); + DeleteJsCallInJsThread(); + } + void OnExtensionConnect(ConnectionNativeInfo& info) override; + void OnExtensionDisconnect(ConnectionNativeInfo& info) override; + void OnExtensionFailed(int32_t connectionId, int32_t errnum) override; + +private: + void DoJsExtensionConnectCallback( + ConnectionNativeInfo& info, int32_t errorNum, ConnectCallbackType type); + void DeleteJsCallInJsThread(); + napi_env env_; + JsConnectCallback jsCallback_; +}; + +struct DeleteJsCallParam { + napi_env env_; + JsConnectCallback jsCallback_; +}; + +struct NapiExtensionConnectionCallbackParam { + napi_env env_; + JsConnectCallback callback_; + ConnectionNativeInfo result_; + int32_t errorNum_; + ConnectCallbackType type_; + std::shared_ptr caller; +}; + +struct CommonAsyncContext { + explicit CommonAsyncContext(napi_env napiEnv); + virtual ~CommonAsyncContext(); + napi_env env = nullptr; + napi_status status = napi_invalid_arg; + int32_t errCode = 0; + napi_deferred deferred = nullptr; // promise handle + napi_ref callbackRef = nullptr; // callback handle + napi_async_work work = nullptr; // work handle +}; + +struct ConnectNativeAsyncContext : public CommonAsyncContext { + explicit ConnectNativeAsyncContext(napi_env env) : CommonAsyncContext(env) {}; + sptr token; + AAFwk::Want want; + sptr connectCallback; + int32_t connectId; +}; + +struct DisconnectNativeAsyncContext : public CommonAsyncContext { + explicit DisconnectNativeAsyncContext(napi_env env) : CommonAsyncContext(env) {}; + int32_t connectId; +}; + +class NapiWebNativeMessagingExtensionManager { +public: + NapiWebNativeMessagingExtensionManager() {} + ~NapiWebNativeMessagingExtensionManager() = default; + + static void Init(); + static napi_value ConnectNative(napi_env env, napi_callback_info info); + static napi_value DisconnectNative(napi_env env, napi_callback_info info); + + static void ConnectNativeExcute(napi_env env, void* data); + static void ConnectNativeComplete(napi_env env, napi_status status, void* data); + static void DisconnectNativeExcute(napi_env env, void* data); + static void DisconnectNativeComplete(napi_env env, napi_status status, void* data); +}; + +} // namespace NWeb +} // namespace OHOS + +#endif // NWEB_NAPI_WEB_NATIVE_MESSAGING_EXTENSION_MANAGER_H diff --git a/interfaces/native/web_native_messaging_extension/BUILD.gn b/interfaces/native/web_native_messaging_extension/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9c1616529e6e95d41ebee6d9452600f33f550978 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright (c) 2025 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. + + group("web_native_messaging_extension") { + deps = [ + "ability:webnativemessagingextensionability_napi", + "context:webnativemessagingextensioncontext_napi", + "extension:web_extension", + "extension:web_native_messaging_extension_module", + "extension_client:web_extension_client", + ] + } diff --git a/interfaces/native/web_native_messaging_extension/ability/BUILD.gn b/interfaces/native/web_native_messaging_extension/ability/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2b0f6d8fbf1affe283a7f2a5647efdd651065f5e --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/ability/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (c) 2025 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") + +es2abc_gen_abc("gen_web_native_messaging_extension_ability_abc") { + src_js = rebase_path("web_native_messaging_extension_ability.js") + dst_file = + rebase_path(target_out_dir + "/web_native_messaging_extension_ability.abc") + in_puts = [ "web_native_messaging_extension_ability.js" ] + out_puts = [ target_out_dir + "/web_native_messaging_extension_ability.abc" ] + extra_args = [ + "--module", + "--source-file=web_native_messaging_extension_ability", + "--module-record-field-name=web_native_messaging_extension_ability" + ] +} + +gen_js_obj("web_native_messaging_extension_ability_js") { + input = "web_native_messaging_extension_ability.js" + output = target_out_dir + "/web_native_messaging_extension_ability.o" +} + +gen_js_obj("web_native_messaging_extension_ability_abc") { + input = + get_label_info(":gen_web_native_messaging_extension_ability_abc", + "target_out_dir") + "/web_native_messaging_extension_ability.abc" + output = target_out_dir + "/web_native_messaging_extension_ability_abc.o" + dep = ":gen_web_native_messaging_extension_ability_abc" +} + +ohos_shared_library("webnativemessagingextensionability_napi") { + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + sources = [ "web_native_messaging_extension_ability_module.cpp" ] + + deps = [ + ":web_native_messaging_extension_ability_abc", + ":web_native_messaging_extension_ability_js", + ] + + external_deps = [ "napi:ace_napi" ] + + cflags = [ + "-fstack-protector-strong", + "-D_FORTIFY_SOURCE=2", + "-O2" + ] + + relative_install_dir = "module/web" + subsystem_name = "web" + part_name = "webview" +} \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability.js b/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability.js new file mode 100644 index 0000000000000000000000000000000000000000..eaee261601a8126cecd36502e838e18e844b62b6 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability.js @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2025 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. +*/ + +let ExtensionAbility = requireNapi('app.ability.ExtensionAbility'); +let hilog = requireNapi('hilog'); + +let domainID = 0xD004500; +let TAG = 'NativeMessaging'; + +class WebNativeMessagingExtensionAbility extends ExtensionAbility { + onConnectNative(info) { + hilog.sLogI(domainID, TAG, 'onCreateNative'); + } + + onDisconnectNative(info) { + hilog.sLogI(domainID, TAG, 'onDisconnectNative'); + } + + onDestroy() { + hilog.sLogI(domainID, TAG, 'onDestroy'); + } +} + +export default WebNativeMessagingExtensionAbility; diff --git a/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability_module.cpp b/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fb5a71ae6d75c473c76d2eff5852efa43f54a7e --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/ability/web_native_messaging_extension_ability_module.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "native_engine/native_engine.h" + +extern const char _binary_web_native_messaging_extension_ability_js_start[]; +extern const char _binary_web_native_messaging_extension_ability_js_end[]; +extern const char _binary_web_native_messaging_extension_ability_abc_start[]; +extern const char _binary_web_native_messaging_extension_ability_abc_end[]; + +static napi_module _module = { + .nm_version = 0, + .nm_filename = "libwebnativemessagingextensionability_napi.z.so/web_native_messaging_extension_ability.js", + .nm_modname = "web.webNativeMessagingExtensionAbility", +}; + +extern "C" __attribute__((constructor)) +void NAPI_WebNativeMessagingExtensionAbility_AutoRegister() +{ + napi_module_register(&_module); +} + +extern "C" __attribute__((visibility("default"))) +void NAPI_web_webNativeMessagingExtensionAbility_GetJSCode(const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_web_native_messaging_extension_ability_js_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_web_native_messaging_extension_ability_js_end - + _binary_web_native_messaging_extension_ability_js_start; + } +} + +extern "C" __attribute__((visibility("default"))) +void NAPI_web_webNativeMessagingExtensionAbility_GetABCCode(const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_web_native_messaging_extension_ability_abc_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_web_native_messaging_extension_ability_abc_end - + _binary_web_native_messaging_extension_ability_abc_start; + } +} diff --git a/interfaces/native/web_native_messaging_extension/context/BUILD.gn b/interfaces/native/web_native_messaging_extension/context/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5fe28a151196e37e23e84a7dcde5c095bbfdfdeb --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/context/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (c) 2025 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") + +es2abc_gen_abc("gen_web_native_messaging_extension_context_abc") { + src_js = rebase_path("web_native_messaging_extension_context.js") + dst_file = + rebase_path(target_out_dir + "/web_native_messaging_extension_context.abc") + in_puts = [ "web_native_messaging_extension_context.js" ] + out_puts = [ target_out_dir + "/web_native_messaging_extension_context.abc" ] + extra_args = [ + "--module", + "--source-file=web_native_messaging_extension_context", + "--module-record-field-name=web_native_messaging_extension_context" + ] +} + +gen_js_obj("web_native_messaging_extension_context_js") { + input = "web_native_messaging_extension_context.js" + output = target_out_dir + "/web_native_messaging_extension_context.o" +} + +gen_js_obj("web_native_messaging_extension_context_abc") { + input = + get_label_info(":gen_web_native_messaging_extension_context_abc", + "target_out_dir") + "/web_native_messaging_extension_context.abc" + output = target_out_dir + "/web_native_messaging_extension_context_abc.o" + dep = ":gen_web_native_messaging_extension_context_abc" +} + +ohos_shared_library("webnativemessagingextensioncontext_napi") { + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + sources = [ "web_native_messaging_extension_context_module.cpp" ] + + deps = [ + ":web_native_messaging_extension_context_abc", + ":web_native_messaging_extension_context_js", + ] + + external_deps = [ "napi:ace_napi" ] + + cflags = [ + "-fstack-protector-strong", + "-D_FORTIFY_SOURCE=2", + "-O2" + ] + + relative_install_dir = "module/web" + subsystem_name = "web" + part_name = "webview" +} \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context.js b/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context.js new file mode 100644 index 0000000000000000000000000000000000000000..fd7299dee3777ffb346636b91016cad047853205 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +let ExtensionContext = requireNapi('application.ExtensionContext'); +let hilog = requireNapi('hilog'); + +let domainID = 0xD004500; +let TAG = 'NativeMessaging'; + +class WebNativeMessagingExtensionContext extends ExtensionContext { + constructor(obj) { + super(obj); + } + + startAbility(want, options) { + hilog.sLogI(domainID, TAG, 'startAbility'); + return this.__context_impl__.startAbility(want, options); + } + + terminateSelf() { + hilog.sLogI(domainID, TAG, 'terminateSelf'); + return this.__context_impl__.terminateSelf(); + } + + stopNativeConnection(connectionId) { + hilog.sLogI(domainID, TAG, 'stopNativeConnection'); + return this.__context_impl__.stopNativeConnection(connectionId); + } +} + +export default WebNativeMessagingExtensionContext; \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context_module.cpp b/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9eaccdc9d9c4260a5b755e58b8e1d905d1a0edb --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/context/web_native_messaging_extension_context_module.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "native_engine/native_engine.h" + +extern const char _binary_web_native_messaging_extension_context_js_start[]; +extern const char _binary_web_native_messaging_extension_context_js_end[]; +extern const char _binary_web_native_messaging_extension_context_abc_start[]; +extern const char _binary_web_native_messaging_extension_context_abc_end[]; + +static napi_module _module = { + .nm_version = 0, + .nm_filename = "libwebnativemessagingextensioncontext_napi.z.so/web_native_messaging_extension_context.js", + .nm_modname = "web.webNativeMessagingExtensionContext", +}; + +extern "C" __attribute__((constructor)) +void NAPI_WebNativeMessagingExtensionContext_AutoRegister() +{ + napi_module_register(&_module); +} + +extern "C" __attribute__((visibility("default"))) +void NAPI_web_webNativeMessagingExtensionContext_GetJSCode(const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_web_native_messaging_extension_context_js_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_web_native_messaging_extension_context_js_end - + _binary_web_native_messaging_extension_context_js_start; + } +} + +extern "C" __attribute__((visibility("default"))) +void NAPI_web_webNativeMessagingExtensionContext_GetABCCode(const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_web_native_messaging_extension_context_abc_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_web_native_messaging_extension_context_abc_end - + _binary_web_native_messaging_extension_context_abc_start; + } +} diff --git a/interfaces/native/web_native_messaging_extension/extension/.BUILD.gn.swp b/interfaces/native/web_native_messaging_extension/extension/.BUILD.gn.swp new file mode 100644 index 0000000000000000000000000000000000000000..3b6e53b26583978f8b7b101a6dee71c2836c0899 Binary files /dev/null and b/interfaces/native/web_native_messaging_extension/extension/.BUILD.gn.swp differ diff --git a/interfaces/native/web_native_messaging_extension/extension/BUILD.gn b/interfaces/native/web_native_messaging_extension/extension/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5e83f220f47c3eeb05ed8cbb58f27e8859d81f81 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/BUILD.gn @@ -0,0 +1,134 @@ +# Copyright (c) 2025 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. + +import("//build/ohos.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("../../../../config.gni") +import("../../../../web_aafwk.gni") + +config("web_ex_cfg") { + include_dirs = [ + "include", + ] +} + +ohos_source_set("connect_info") { + include_dirs = [ + "include", + "../../../../ohos_nweb/include", + "../../../../sa/web_native_messaging/common", + ] + + sources = [ "src/web_native_messaging_extension_connect_info.cpp" ] + + external_deps = [ + "hilog:libhilog", + "ipc:ipc_core", + ] + + part_name = "webview" + subsystem_name = "web" +} + +ohos_shared_library("web_extension") { + include_dirs = [ + "include", + "../../../../ohos_nweb/include", + "../../../../sa/web_native_messaging/common", + ] + + sources = [ + "src/js_web_native_messaging_extension_context.cpp", + "src/js_web_native_messaging_extension.cpp", + "src/web_native_messaging_extension_context.cpp", + "src/web_native_messaging_extension_stub_impl.cpp", + "src/web_native_messaging_extension_stub.cpp", + "src/web_native_messaging_extension.cpp", + "../../../../sa/web_native_messaging/common/web_native_messaging_common.cpp" + ] + + deps = [ + ":connect_info", + "../../../../sa/web_native_messaging:web_native_messaging_kit" + ] + + external_deps = [ + "ability_base:want", + "ability_runtime:app_context", + "ability_runtime:napi_common", + "ability_runtime:ability_start_options", + "ability_runtime:ability_context_native", + "ability_runtime:extensionkit_native", + "ability_runtime:ability_manager", + "ability_runtime:runtime", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_napi", + "ipc:ipc_single", + "napi:ace_napi", + ] + + part_name = "webview" + subsystem_name = "web" +} + +ohos_shared_library("web_native_messaging_extension_module") { + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + include_dirs = [ + "include", + "../../../../sa/web_native_messaging/common", + ] + + sources = [ + "src/web_native_messaging_extension_module_loader.cpp" + ] + + deps = [ + ":web_extension", + ] + + external_deps = [ + "ability_base:base", + "ability_base:want", + "ability_runtime:abilitykit_native", + "ability_runtime:runtime", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_napi", + "ipc:ipc_single", + "napi:ace_napi", + ] + + cflags = [ + "-fstack-protector-strong", + "-D_FORTIFY_SOURCE=2", + "-Os", + "-fvisibility=hidden", + "-fdata-sections", + "-ffunction-sections", + "-flto", + ] + + relative_install_dir = "extensionability/" + + part_name = "webview" + subsystem_name = "web" +} diff --git a/interfaces/native/web_native_messaging_extension/extension/include/iweb_native_messaging_extension.h b/interfaces/native/web_native_messaging_extension/extension/include/iweb_native_messaging_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..af348611d9a169742833ef4d619bdb309a24f6b4 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/iweb_native_messaging_extension.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef I_WEB_NATIVE_MESSAGING_EXTENSION_H +#define I_WEB_NATIVE_MESSAGING_EXTENSION_H + +#include "web_native_messaging_extension_connect_info.h" +#include "iremote_broker.h" + +namespace OHOS { +namespace NWeb { +class IWebNativeMessagingExtension : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.NWeb.IWEBNATIVEMESSAGINGEXTENSION"); + + enum WNMEProxyError { + WNME_SUCCESS = 0, + WNME_IPC_ERROR= -1, + WNME_PARAMS_INVALID = -2, + WNME_INNER_ERROR = -3, + }; + + /* + * Remote method code. + */ + enum { + CODE_CONNECTNATIVE = 0, + CODE_DISCONNECTNATIVE = 1, + }; + + virtual int32_t ConnectNative(WNMEConnectionInfo& connection) = 0; + virtual int32_t DisconnectNative(WNMEConnectionInfo& connection) = 0; +}; +} // namespace NWeb +} // namespace OHOS +#endif // I_WEB_NATIVE_MESSAGING_EXTENSION_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension.h b/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..18b01bf0fd3665b859918359c667734d1ce25fc3 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef JS_WEB_NATIVE_MESSAGING_EXTENSION_H +#define JS_WEB_NATIVE_MESSAGING_EXTENSION_H + +#include "web_native_messaging_extension.h" +#include "js_runtime.h" +#include "native_engine/native_value.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +using namespace OHOS::AbilityRuntime; +class JsWebNativeMessagingExtension : public WebNativeMessagingExtension { +public: + JsWebNativeMessagingExtension(JsRuntime& jsRuntime); + ~JsWebNativeMessagingExtension() override; + + static JsWebNativeMessagingExtension *Create(const std::unique_ptr& runtime); + + void Init(const std::shared_ptr& record, + const std::shared_ptr& application, + std::shared_ptr& handler, const sptr& token) override; + + sptr OnConnect(const AAFwk::Want & want) override; + + void OnStop() override; + + virtual int32_t ConnectNative(WNMEConnectionInfo& connection) override; + + virtual int32_t DisconnectNative(WNMEConnectionInfo& connection) override; + +private: + class ConnectionManager { + public: + ConnectionManager() = default; + ~ConnectionManager() { + for (const auto& [id, conn] : connections_) { + close(conn.fdRead); + close(conn.fdWrite); + } + } + void AddConnection(const WNMEConnectionInfo& conn) { + auto tmp = GetConnection(conn.connectionId); + if (tmp) { + WNMLOG_E("connectionId exists!"); + } + connections_[conn.connectionId] = conn; + } + void RemoveConnection(const WNMEConnectionInfo& conn) { + auto tmp = GetConnection(conn.connectionId); + if (tmp) { + close(tmp->fdRead); + close(tmp->fdWrite); + connections_.erase(conn.connectionId); + return; + } + WNMLOG_E("connectionId not exists!"); + } + + WNMEConnectionInfo* GetConnection(int32_t connectionId) { + auto it = connections_.find(connectionId); + return (it != connections_.end()) ? &it->second : nullptr; + } + private: + std::map connections_; + }; + + void OnDestroy(); + void BindContext(napi_env env, napi_value obj); + void InvokeCallback(const char* methodName, const WNMEConnectionInfo& params); + int32_t InvokeCallbackInMainThread(const std::string& methodName, WNMEConnectionInfo& params); + void GetSrcPath(std::string& srcPath); + + JsRuntime& jsRuntime_; + std::unique_ptr jsObj_ = nullptr; + std::shared_ptr shellContextRef_ = nullptr; + sptr providerRemoteObject_ = nullptr; + ConnectionManager connmgr_; +}; +} // namespace NWeb +} // namespace OHOS +#endif // JS_WEB_NATIVE_MESSAGING_EXTENSION_H diff --git a/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension_context.h b/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension_context.h new file mode 100644 index 0000000000000000000000000000000000000000..266e875296b0433729cf40fb4b0f948c773402b7 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/js_web_native_messaging_extension_context.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef JS_WEB_NATIVE_EXTENSION_CONTEXT_H +#define JS_WEB_NATIVE_EXTENSION_CONTEXT_H + +#include "napi/native_api.h" +#include "web_native_messaging_extension.h" + +namespace OHOS { +namespace NWeb { +napi_value CreateJsWebNativeMessagingExtensionContext( + napi_env env, std::shared_ptr context); +} // namespace NWeb +} // namespace OHOS +#endif // JS_WEB_NATIVE_EXTENSION_CONTEXT_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..6b5e3a62e7ee3f65455aecdab24beb06d05b5109 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_H +#define WEB_NATIVE_MESSAGING_EXTENSION_H + +#include "web_native_messaging_extension_connect_info.h" +#include "extension_base.h" +#include "web_native_messaging_extension_context.h" + +namespace OHOS { +namespace NWeb { +class WebNativeMessagingExtension : public AbilityRuntime::ExtensionBase { +public: + WebNativeMessagingExtension() = default; + virtual ~WebNativeMessagingExtension() = default; + + static WebNativeMessagingExtension* Create(const std::unique_ptr& runtime); + + virtual int32_t ConnectNative(WNMEConnectionInfo& connection); + + virtual int32_t DisconnectNative(WNMEConnectionInfo& connection); +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_connect_info.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_connect_info.h new file mode 100644 index 0000000000000000000000000000000000000000..ce4fe971b531148f07c79ec6cdcc6aab42f06081 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_connect_info.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_CONNECT_INFO_H +#define WEB_NATIVE_MESSAGING_EXTENSION_CONNECT_INFO_H + +#include +#include +#include "message_parcel.h" + +namespace OHOS { +namespace NWeb { +struct WNMEConnectionInfo { + int32_t connectionId = -1; + std::string bundleName = ""; + std::string extensionOrigin = ""; + int32_t fdRead = -1; + int32_t fdWrite = -1; +}; + +struct WNMEConnectionInfoParcel { + bool Marshalling(MessageParcel& parcel); + static bool Unmarshalling(MessageParcel& in, WNMEConnectionInfo& conn_info); + + WNMEConnectionInfo conn_info_; +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_CONNECT_INFO_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_context.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_context.h new file mode 100644 index 0000000000000000000000000000000000000000..5c66ecbbfc2577d23c465b80e7743e6b3fe2e659 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_context.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef OHOS_NWEB_WEB_NATIVE_MESSAGING_EXTENSION_CONTEXT_H +#define OHOS_NWEB_WEB_NATIVE_MESSAGING_EXTENSION_CONTEXT_H + +#include "extension_context.h" +#include "start_options.h" +#include "want.h" + +namespace OHOS { +namespace NWeb { +class WebNativeMessagingExtensionContext : public AbilityRuntime::ExtensionContext { +public: + WebNativeMessagingExtensionContext() = default; + virtual ~WebNativeMessagingExtensionContext() = default; + + ErrCode StartAbility(const OHOS::AAFwk::Want& want, const OHOS::AAFwk::StartOptions startOptions); + + ErrCode TerminateSelf(); + + ErrCode StopNativeConnection(int32_t connectionId); +}; +} // namespace NWeb +} // namespace OHOS +#endif // OHOS_NWEB_WEB_NATIVE_MESSAGING_EXTENSION_CONTEXT_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_module_loader.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_module_loader.h new file mode 100644 index 0000000000000000000000000000000000000000..f23d0da0f8b2991fdb40982d3eb23bfb5ab3fe97 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_module_loader.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_MODULE_LOADER_H +#define WEB_NATIVE_MESSAGING_EXTENSION_MODULE_LOADER_H + +#include "extension_module_loader.h" + +namespace OHOS { +namespace NWeb { +class WebNativeMessagingExtensionModuleLoader : public AbilityRuntime::ExtensionModuleLoader, + public Singleton +{ + DECLARE_SINGLETON(WebNativeMessagingExtensionModuleLoader); + +public: + virtual AbilityRuntime::Extension *Create(const std::unique_ptr& runtime) const override; + + virtual std::map GetParams() override; +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_MODULE_LOADER_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..37f163f862d9c86ea009c9c3532ca10dc0a74001 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_STUB_H +#define WEB_NATIVE_MESSAGING_EXTENSION_STUB_H + +#include "iweb_native_messaging_extension.h" +#include "iremote_stub.h" + +namespace OHOS { +namespace NWeb { +class WebNativeMessagingExtensionStub : public IRemoteStub { +public: + WebNativeMessagingExtensionStub() = default; + virtual ~WebNativeMessagingExtensionStub() = default; + + int32_t OnRemoteRequest(uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) override; +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_STUB_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub_impl.h b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..afd996762d7dc1fd4c95bc672e6e8fc170795f76 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/include/web_native_messaging_extension_stub_impl.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_STUB_IMPL_H +#define WEB_NATIVE_MESSAGING_EXTENSION_STUB_IMPL_H + +#include +#include "web_native_messaging_extension_connect_info.h" +#include "js_web_native_messaging_extension.h" +#include "web_native_messaging_extension_stub.h" + +namespace OHOS { +namespace NWeb { +class WebNativeMessagingExtensionStubImpl : public WebNativeMessagingExtensionStub { +public: + explicit WebNativeMessagingExtensionStubImpl(const std::shared_ptr& extension); + + virtual ~WebNativeMessagingExtensionStubImpl(); + + virtual int32_t ConnectNative(WNMEConnectionInfo& connection) override; + + virtual int32_t DisconnectNative(WNMEConnectionInfo& connection) override; + +private: + std::weak_ptr extension_; +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_STUB_IMPL_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension.cpp b/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81c791e09e94518c7b01c39816ba3a805c6603d3 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_web_native_messaging_extension.h" + +#include "ability_handler.h" +#include "js_runtime_utils.h" +#include "js_web_native_messaging_extension_context.h" +#include "web_native_messaging_extension_stub_impl.h" + +namespace OHOS { +namespace NWeb { +namespace { +constexpr int32_t ARGC_ONE = 1; +constexpr int32_t INDEX_ZERO = 0; +} // namespace + +using namespace OHOS::AppExecFwk; +using namespace OHOS::AbilityRuntime; + +napi_value AttachWebNativeMessagingExtensionContext(napi_env env, void* value, void*) +{ + if (value == nullptr) { + WNMLOG_E("null value"); + return nullptr; + } + auto ptr = reinterpret_cast*>(value)->lock(); + if (ptr == nullptr) { + WNMLOG_E("null ptr"); + return nullptr; + } + napi_value object = CreateJsWebNativeMessagingExtensionContext(env, ptr); + auto sysModule = JsRuntime::LoadSystemModuleByEngine(env, "web.webNativeMessagingExtensionContext", &object, 1); + if (sysModule == nullptr) { + WNMLOG_E("null sysModule"); + return nullptr; + } + auto contextObj = sysModule->GetNapiValue(); + napi_coerce_to_native_binding_object( + env, contextObj, DetachCallbackFunc, AttachWebNativeMessagingExtensionContext, value, nullptr); + auto workContext = new (std::nothrow) std::weak_ptr(ptr); + auto res = napi_wrap( + env, contextObj, workContext, + [](napi_env, void* data, void*) { + WNMLOG_I("Finalizer for weak_ptr app service extension context is called"); + delete static_cast*>(data); + }, + nullptr, nullptr); + if (res != napi_ok && workContext != nullptr) { + WNMLOG_E("napi_wrap failed"); + delete workContext; + return nullptr; + } + return contextObj; +} + +JsWebNativeMessagingExtension* JsWebNativeMessagingExtension::Create(const std::unique_ptr& runtime) +{ + return new JsWebNativeMessagingExtension(static_cast(*runtime)); +} + +JsWebNativeMessagingExtension::JsWebNativeMessagingExtension(JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {} + +JsWebNativeMessagingExtension::~JsWebNativeMessagingExtension() +{ + auto context = GetContext(); + if (context) { + context->Unbind(); + } + + jsRuntime_.FreeNativeReference(std::move(jsObj_)); + jsRuntime_.FreeNativeReference(std::move(shellContextRef_)); +} + +void JsWebNativeMessagingExtension::Init(const std::shared_ptr& record, + const std::shared_ptr& application, std::shared_ptr& handler, + const sptr& token) +{ + WebNativeMessagingExtension::Init(record, application, handler, token); + + std::string srcPath = ""; + GetSrcPath(srcPath); + if (srcPath.empty()) { + WNMLOG_E("get srcPath failed"); + return; + } + + std::string moduleName(Extension::abilityInfo_->moduleName); + moduleName.append("::").append(abilityInfo_->name); + AbilityRuntime::HandleScope handleScope(jsRuntime_); + auto env = jsRuntime_.GetNapiEnv(); + + jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath, + abilityInfo_->compileMode == CompileMode::ES_MODULE, false, abilityInfo_->srcEntrance); + if (jsObj_ == nullptr) { + WNMLOG_E("null jsObj_"); + return; + } + + napi_value obj = jsObj_->GetNapiValue(); + if (!CheckTypeForNapiValue(env, obj, napi_object)) { + WNMLOG_E("get JsWebNativeMessagingExtension obj failed"); + return; + } + + BindContext(env, obj); +} + +void JsWebNativeMessagingExtension::BindContext(napi_env env, napi_value obj) +{ + auto context = GetContext(); + if (context == nullptr) { + WNMLOG_E("null context"); + return; + } + napi_value contextObj = CreateJsWebNativeMessagingExtensionContext(env, context); + shellContextRef_ = + JsRuntime::LoadSystemModuleByEngine(env, "web.webNativeMessagingExtensionContext", &contextObj, ARGC_ONE); + if (shellContextRef_ == nullptr) { + WNMLOG_E("null shellContextRef"); + return; + } + contextObj = shellContextRef_->GetNapiValue(); + if (!CheckTypeForNapiValue(env, contextObj, napi_object)) { + WNMLOG_E("get context native obj failed"); + return; + } + auto workContext = new (std::nothrow) std::weak_ptr(context); + napi_coerce_to_native_binding_object( + env, contextObj, DetachCallbackFunc, AttachWebNativeMessagingExtensionContext, workContext, nullptr); + context->Bind(jsRuntime_, shellContextRef_.get()); + napi_set_named_property(env, obj, "context", contextObj); + + auto res = napi_wrap( + env, contextObj, workContext, + [](napi_env, void* data, void*) { + delete static_cast*>(data); + }, nullptr, nullptr); + if (res != napi_ok && workContext != nullptr) { + WNMLOG_E("napi_wrap failed:%{public}d", res); + delete workContext; + return; + } + WNMLOG_I("end BindContext"); +} + +sptr JsWebNativeMessagingExtension::OnConnect(const AAFwk::Want& want) +{ + Extension::OnConnect(want); + if (providerRemoteObject_ == nullptr) { + sptr remoteObject = new (std::nothrow) + WebNativeMessagingExtensionStubImpl(std::static_pointer_cast( + shared_from_this())); + if (remoteObject != nullptr) { + providerRemoteObject_ = remoteObject->AsObject(); + } + } + return providerRemoteObject_; +} + +napi_value ConnInfoToJs(napi_env env, const WNMEConnectionInfo& params) +{ + napi_value connInfoJs = nullptr; + NAPI_CALL(env, napi_create_object(env, &connInfoJs)); + + napi_value connectionIdJs; + napi_create_int32(env, params.connectionId, &connectionIdJs); + NAPI_CALL(env, napi_set_named_property(env, connInfoJs, "connectionId", connectionIdJs)); + + napi_value bundleNameJs; + NAPI_CALL(env, napi_create_string_utf8(env, params.bundleName.c_str(), NAPI_AUTO_LENGTH, &bundleNameJs)); + NAPI_CALL(env, napi_set_named_property(env, connInfoJs, "bundleName", bundleNameJs)); + + napi_value extensionOriginJs; + NAPI_CALL(env, napi_create_string_utf8(env, params.extensionOrigin.c_str(), NAPI_AUTO_LENGTH, &extensionOriginJs)); + NAPI_CALL(env, napi_set_named_property(env, connInfoJs, "extensionOrigin", extensionOriginJs)); + + napi_value fdReadJs; + napi_create_int32(env, params.fdRead, &fdReadJs); + NAPI_CALL(env, napi_set_named_property(env, connInfoJs, "fdRead", fdReadJs)); + + napi_value fdWriteJs; + napi_create_int32(env, params.fdWrite, &fdWriteJs); + NAPI_CALL(env, napi_set_named_property(env, connInfoJs, "fdWrite", fdWriteJs)); + return connInfoJs; +} + +bool CheckValueType(const napi_env& env, const napi_value& value, const napi_valuetype type) +{ + if (value == nullptr) { + return false; + } + napi_valuetype valuetype; + NAPI_CALL_BASE(env, napi_typeof(env, value, &valuetype), false); + return valuetype == type; +} + +void JsWebNativeMessagingExtension::InvokeCallback(const char* methodName, const WNMEConnectionInfo& params) +{ + AbilityRuntime::HandleScope handleScope(jsRuntime_); + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value abilityObj = jsObj_->GetNapiValue(); + napi_value method; + napi_get_named_property(env, abilityObj, methodName, &method); + if (method== nullptr) { + WNMLOG_E("no find %{public}s", methodName); + return; + } + if (!CheckValueType(env, method, napi_valuetype::napi_function)) { + WNMLOG_E("wrong type %{public}s", methodName); + return; + } + napi_value arg[ARGC_ONE]; + arg[INDEX_ZERO] = ConnInfoToJs(env, params); + + napi_status status = napi_call_function(env, abilityObj, method, ARGC_ONE, arg, nullptr); + if (status != napi_ok) { + WNMLOG_E("call js func failed: %{public}d", status); + } + + if (std::string(methodName) == std::string("onConnectNative")) { + connmgr_.AddConnection(params); + } else { + connmgr_.RemoveConnection(params); + } + return; +} + +int32_t JsWebNativeMessagingExtension::InvokeCallbackInMainThread( + const std::string& methodName, WNMEConnectionInfo& params) +{ + if (handler_ == nullptr) { + WNMLOG_E("handler_ is nullptr"); + return -1; + } + auto jsServiceExtension = std::static_pointer_cast(shared_from_this()); + auto task = [jsServiceExtension, methodName, data = params]() { + if (jsServiceExtension) { + jsServiceExtension->InvokeCallback(methodName.c_str(), data); + } + }; + handler_->PostTask(task, "JsWebNativeMessagingExtension::" + methodName); + return 0; +} + +int32_t JsWebNativeMessagingExtension::ConnectNative(WNMEConnectionInfo& connection) +{ + return InvokeCallbackInMainThread("onConnectNative", connection); +} + +int32_t JsWebNativeMessagingExtension::DisconnectNative(WNMEConnectionInfo& connection) +{ + return InvokeCallbackInMainThread("onDisconnectNative", connection); +} + +void JsWebNativeMessagingExtension::OnDestroy() +{ + AbilityRuntime::HandleScope handleScope(jsRuntime_); + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value abilityObj = jsObj_->GetNapiValue(); + napi_value method; + const std::string methodName = "onDestroy"; + napi_get_named_property(env, abilityObj, methodName.c_str(), &method); + if (method== nullptr) { + WNMLOG_E("no find %{public}s", methodName.c_str()); + return; + } + if (!CheckValueType(env, method, napi_valuetype::napi_function)) { + WNMLOG_E("wrong type %{public}s", methodName.c_str()); + return; + } + + napi_status status = napi_call_function(env, abilityObj, method, 0, nullptr, nullptr); + if (status != napi_ok) { + WNMLOG_E("call js func failed: %{public}d", status); + } + return; +} + +void JsWebNativeMessagingExtension::OnStop() +{ + OnDestroy(); +} + +void JsWebNativeMessagingExtension::GetSrcPath(std::string& srcPath) +{ + if (!Extension::abilityInfo_->srcEntrance.empty()) { + srcPath.append(Extension::abilityInfo_->moduleName + "/"); + srcPath.append(Extension::abilityInfo_->srcEntrance); + srcPath.erase(srcPath.rfind('.')); + srcPath.append(".abc"); + } +} + +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension_context.cpp b/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a311ce367451f49df5bb1352d2eae9449497f883 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/js_web_native_messaging_extension_context.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_web_native_messaging_extension_context.h" + +#include "js_extension_context.h" +#include "js_error_utils.h" +#include "napi_common_ability.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +using namespace AbilityRuntime; +namespace { +constexpr int32_t ARGC_ZERO = 0; +constexpr int32_t ARGC_ONE = 1; +constexpr int32_t INDEX_ZERO = 0; +constexpr int32_t INDEX_ONE = 1; + +napi_value CreateNMJsError(napi_env env, int32_t err) +{ + std::string ErrorMesg = ""; + int32_t jsError = NativeMessageError::NativeCodeToJsCode(err, ErrorMesg); + return AbilityRuntime::CreateJsError(env, jsError, ErrorMesg.c_str()); +} + +class JsWebNativeMessagingExtensionContext final { +public: + explicit JsWebNativeMessagingExtensionContext(const std::shared_ptr& context) + : context_(context) + {} + ~JsWebNativeMessagingExtensionContext() = default; + + static void Finalizer(napi_env env, void* data, void* hint) + { + JsWebNativeMessagingExtensionContext* context = static_cast(data); + if (context == nullptr) { + WNMLOG_E("context is null"); + return; + } + std::unique_ptr ptr(context); + } + + static napi_value StartAbility(napi_env env, napi_callback_info info) + { + GET_NAPI_INFO_AND_CALL(env, info, JsWebNativeMessagingExtensionContext, OnStartAbility); + } + + static napi_value TerminateSelf(napi_env env, napi_callback_info info) + { + GET_NAPI_INFO_AND_CALL(env, info, JsWebNativeMessagingExtensionContext, OnTerminateSelf); + } + + static napi_value StopNativeConnection(napi_env env, napi_callback_info info) + { + GET_NAPI_INFO_AND_CALL(env, info, JsWebNativeMessagingExtensionContext, OnStopNativeConnection); + } + +private: + std::weak_ptr context_; + + bool CheckType(const napi_env& env, const napi_value& value, const napi_valuetype& type) + { + napi_valuetype valuetype = napi_undefined; + napi_typeof(env, value, &valuetype); + if (valuetype != type) { + WNMLOG_E("value type dismatch, [%{public}d]->[%{public}d]", valuetype, type); + return false; + } + return true; + } + + bool ParseInt32(const napi_env& env, const napi_value& value, int32_t& result) + { + if (!CheckType(env, value, napi_number)) { + return false; + } + if (napi_get_value_int32(env, value, &result) != napi_ok) { + WNMLOG_E("Cannot get value int32"); + return false; + } + return true; + } + + napi_value OnStopNativeConnection(napi_env env, NapiCallbackInfo& info) + { + WNMLOG_I("OnStopNativeConnection"); + if (info.argc == ARGC_ZERO) { + WNMLOG_E("invalid arg"); + ThrowTooFewParametersError(env); + return CreateJsUndefined(env); + } + + int32_t connectionId; + if (!ParseInt32(env, info.argv[0], connectionId)) { + ThrowInvalidParamError(env, "Parse param connectionId failed, type must be number"); + return CreateJsUndefined(env); + } + + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [connectionId, weak = context_, innerErrCode]() { + auto context = weak.lock(); + if (!context) { + WNMLOG_E("context is null"); + *innerErrCode = static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + return; + } + *innerErrCode = context->StopNativeConnection(connectionId); + }; + NapiAsyncTask::CompleteCallback complete = + [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + if (*innerErrCode == ERR_OK) { + task.Resolve(env, CreateJsUndefined(env)); + } else if (NativeMessageError::IsNativeMessagingErr(*innerErrCode)) { + task.Reject(env, CreateNMJsError(env, *innerErrCode)); + } else { + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + } + }; + + napi_value lastParam = nullptr; + napi_value result = nullptr; + NapiAsyncTask::ScheduleHighQos("JsWebNativeMessagingExtensionContext::OnStopNativeConnection", + env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result)); + return result; + } + + napi_value OnTerminateSelf(napi_env env, NapiCallbackInfo& info) + { + WNMLOG_I("OnTerminateSelf"); + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrCode]() { + auto context = weak.lock(); + if (!context) { + WNMLOG_E("context is null"); + *innerErrCode = static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + return; + } + *innerErrCode = context->TerminateSelf(); + }; + NapiAsyncTask::CompleteCallback complete = + [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + if (*innerErrCode == ERR_OK) { + task.Resolve(env, CreateJsUndefined(env)); + } else { + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + } + }; + + napi_value lastParam = nullptr; + napi_value result = nullptr; + NapiAsyncTask::ScheduleHighQos("JsWebNativeMessagingExtensionContext::TerminateSelf", + env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result)); + return result; + } + + bool CheckStartAbilityInputParam(napi_env env, NapiCallbackInfo& info, + AAFwk::Want& want, AAFwk::StartOptions& startOptions, size_t& unwrapArgc) const + { + if (info.argc < ARGC_ONE) { + return false; + } + unwrapArgc = ARGC_ZERO; + if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) { + return false; + } + ++unwrapArgc; + if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + AppExecFwk::UnwrapStartOptions(env, info.argv[INDEX_ONE], startOptions); + unwrapArgc++; + } + return true; + } + + napi_value OnStartAbility(napi_env env, NapiCallbackInfo& info) + { + WNMLOG_I("OnStartAbility"); + size_t unwrapArgc = 0; + AAFwk::Want want; + AAFwk::StartOptions startOptions; + if (info.argc == ARGC_ZERO) { + WNMLOG_E("invalid arg"); + ThrowTooFewParametersError(env); + return CreateJsUndefined(env); + } + if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) { + WNMLOG_E("Failed, input param type invalid"); + ThrowInvalidParamError(env, "Parse param want failed, type must be Want"); + return CreateJsUndefined(env); + } + + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = + [weak = context_, want, startOptions, unwrapArgc, innerErrCode]() { + auto context = weak.lock(); + if (!context) { + WNMLOG_E("context is null"); + *innerErrCode = static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + return; + } + *innerErrCode = context->StartAbility(want, startOptions); + }; + NapiAsyncTask::CompleteCallback complete = + [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + if (*innerErrCode == ERR_OK) { + task.ResolveWithNoError(env, CreateJsUndefined(env)); + } else if (NativeMessageError::IsNativeMessagingErr(*innerErrCode)) { + task.Reject(env, CreateNMJsError(env, *innerErrCode)); + } else { + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + } + }; + + napi_value lastParam = nullptr; + napi_value result = nullptr; + NapiAsyncTask::ScheduleHighQos("JsWebNativeMessagingExtensionContext::OnStartAbility", + env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result)); + return result; + } +}; +} // namespace + +napi_value CreateJsWebNativeMessagingExtensionContext(napi_env env, + std::shared_ptr context) +{ + napi_value objValue = AbilityRuntime::CreateJsExtensionContext(env, context); + std::unique_ptr jsContext = + std::make_unique(context); + napi_status status = + napi_wrap(env, objValue, jsContext.release(), + JsWebNativeMessagingExtensionContext::Finalizer, nullptr, nullptr); + if (status != napi_ok) { + WNMLOG_E("napi_wrap failed"); + return nullptr; + } + + const char *moduleName = "JsWebNativeMessagingExtensionContext"; + BindNativeFunction( + env, objValue, "startAbility", moduleName, JsWebNativeMessagingExtensionContext::StartAbility); + BindNativeFunction( + env, objValue, "terminateSelf", moduleName, JsWebNativeMessagingExtensionContext::TerminateSelf); + BindNativeFunction( + env, objValue, "stopNativeConnection", + moduleName, JsWebNativeMessagingExtensionContext::StopNativeConnection); + return objValue; +} +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6424b6361faddae4221bd3af51481856d3a40e3a --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension.h" + +#include "js_web_native_messaging_extension.h" +#include "runtime.h" +#include "web_native_messaging_extension_context.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { + +WebNativeMessagingExtension* WebNativeMessagingExtension::Create( + const std::unique_ptr& runtime) +{ + if (!runtime) { + return new WebNativeMessagingExtension(); + } + + switch (runtime->GetLanguage()) { + case AbilityRuntime::Runtime::Language::JS: + return JsWebNativeMessagingExtension::Create(runtime); + + default: + return new WebNativeMessagingExtension(); + } +} + +int32_t WebNativeMessagingExtension::ConnectNative(WNMEConnectionInfo& connection) +{ + return 0; +} + +int32_t WebNativeMessagingExtension::DisconnectNative(WNMEConnectionInfo& connection) +{ + return 0; +} + +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_connect_info.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_connect_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e92b8022a96fc6a8b32d041276e4a7f693e9199d --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_connect_info.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_connect_info.h" + +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +namespace{ +const int IGNORE_FD = 0; +const int FORCE_FD = 1; +} + +bool WNMEConnectionInfoParcel::Marshalling(MessageParcel &out) +{ + if (!(out.WriteInt32(conn_info_.connectionId))) { + WNMLOG_E("fail write"); + return false; + } + if (!(out.WriteString(conn_info_.bundleName))) { + WNMLOG_E("fail write"); + return false; + } + if (!(out.WriteString(conn_info_.extensionOrigin))) { + WNMLOG_E("fail write"); + return false; + } + if ((conn_info_.fdRead == -1) || (conn_info_.fdWrite == -1)) { + out.WriteInt32(IGNORE_FD); + return true; + } else { + out.WriteInt32(FORCE_FD); + } + if (!(out.WriteFileDescriptor(conn_info_.fdRead))) { + WNMLOG_E("fail write"); + return false; + } + if (!(out.WriteFileDescriptor(conn_info_.fdWrite))) { + WNMLOG_E("fail write"); + return false; + } + return true; +} + +bool WNMEConnectionInfoParcel::Unmarshalling(MessageParcel& in, WNMEConnectionInfo& conn_info) +{ + if (!(in.ReadInt32(conn_info.connectionId))) { + WNMLOG_E("fail read"); + return false; + } + if (!(in.ReadString(conn_info.bundleName))) { + WNMLOG_E("fail read"); + return false; + } + if (!(in.ReadString(conn_info.extensionOrigin))) { + WNMLOG_E("fail read"); + return false; + } + int ignore = FORCE_FD; + if (!in.ReadInt32(ignore)) { + WNMLOG_E("fail read"); + return false; + } + if (ignore == IGNORE_FD) { + conn_info.fdRead = -1; + conn_info.fdWrite = -1; + return true; + } + + conn_info.fdRead = in.ReadFileDescriptor(); + if (conn_info.fdRead < 0) { + WNMLOG_E("fail read"); + return false; + } + conn_info.fdWrite = in.ReadFileDescriptor(); + if (conn_info.fdWrite < 0) { + WNMLOG_E("fail read"); + return false; + } + return true; +} +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_context.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ebf4b18733db0e49b72544f30f110d80e667bcf --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_context.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_context.h" + +#include "ability_manager_client.h" +#include "web_native_messaging_client.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +ErrCode WebNativeMessagingExtensionContext::StartAbility(const OHOS::AAFwk::Want& want, + const OHOS::AAFwk::StartOptions startOptions) +{ + ErrCode err = WebNativeMessagingClient::GetInstance().StartAbility(token_, want, startOptions); + if (err != ERR_OK) { + WNMLOG_E("failed %{public}d", err); + } + return err; +} + +ErrCode WebNativeMessagingExtensionContext::TerminateSelf() +{ + ErrCode err = AAFwk::AbilityManagerClient::GetInstance()->TerminateAbility(token_, -1, nullptr); + if (err != ERR_OK) { + WNMLOG_E("failed %{public}d", err); + } + return err; +} + +ErrCode WebNativeMessagingExtensionContext::StopNativeConnection(int32_t connectionId) +{ + ErrCode err = WebNativeMessagingClient::GetInstance().StopNativeConnectionFromExtension(connectionId); + if (err != ERR_OK) { + WNMLOG_E("failed %{public}d", err); + } + return err; +} + +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_module_loader.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_module_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5667851a5cc08e71b45abdc2370a9192907cbc5b --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_module_loader.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_module_loader.h" + +#include "web_native_messaging_extension.h" + +namespace OHOS { +namespace NWeb { +namespace { +const std::map EXTENSION_PARAMS = { + {"type", "32"}, + {"name", "WebNativeMessagingExtension"} +}; +} + +WebNativeMessagingExtensionModuleLoader::WebNativeMessagingExtensionModuleLoader() = default; +WebNativeMessagingExtensionModuleLoader::~WebNativeMessagingExtensionModuleLoader() = default; + +AbilityRuntime::Extension *WebNativeMessagingExtensionModuleLoader::Create( + const std::unique_ptr& runtime) const +{ + return WebNativeMessagingExtension::Create(runtime); +} + +std::map WebNativeMessagingExtensionModuleLoader::GetParams() +{ + return EXTENSION_PARAMS; +} + +extern "C" __attribute__((visibility("default"))) void *OHOS_EXTENSION_GetExtensionModule() +{ + return &WebNativeMessagingExtensionModuleLoader::GetInstance(); +} + +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..341a7656fc8d00b1a402a9e73233f1383d30b513 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_stub.h" + +#include +#include +#include "ipc_types.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { +int32_t WebNativeMessagingExtensionStub::OnRemoteRequest(uint32_t code, MessageParcel& data, + MessageParcel& reply, MessageOption& option) +{ + if (data.ReadInterfaceToken() != GetDescriptor()) { + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + + switch (code) { + case IWebNativeMessagingExtension::CODE_CONNECTNATIVE: { + WNMEConnectionInfo connInfo; + WNMEConnectionInfoParcel::Unmarshalling(data, connInfo); + int32_t errorCode = ConnectNative(connInfo); + if (errorCode == WNME_SUCCESS) { + errorCode = getpid(); + } + reply.WriteInt32(errorCode); + return ERR_NONE; + } + + case IWebNativeMessagingExtension::CODE_DISCONNECTNATIVE: { + WNMEConnectionInfo connInfo; + WNMEConnectionInfoParcel::Unmarshalling(data, connInfo); + int32_t errorCode = DisconnectNative(connInfo); + reply.WriteInt32(errorCode); + return ERR_NONE; + } + + default: + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + + return ERR_TRANSACTION_FAILED; +} +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub_impl.cpp b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0aa75efcc57d6ef28b02703394fe5d1ff84787b --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_stub_impl.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_stub_impl.h" + +namespace OHOS { +namespace NWeb { +WebNativeMessagingExtensionStubImpl::WebNativeMessagingExtensionStubImpl( + const std::shared_ptr& extension) : extension_(extension) +{} + +WebNativeMessagingExtensionStubImpl::~WebNativeMessagingExtensionStubImpl() {} + +int32_t WebNativeMessagingExtensionStubImpl::ConnectNative(WNMEConnectionInfo& connection) +{ + auto extension = extension_.lock(); + if (extension == nullptr) { + return WNMEProxyError::WNME_PARAMS_INVALID; + } + return extension->ConnectNative(connection); +} + +int32_t WebNativeMessagingExtensionStubImpl::DisconnectNative(WNMEConnectionInfo& connection) +{ + auto extension = extension_.lock(); + if (extension == nullptr) { + return WNMEProxyError::WNME_PARAMS_INVALID; + } + return extension->DisconnectNative(connection); +} + +} // namespace NWeb +} // namespace OHOS diff --git a/interfaces/native/web_native_messaging_extension/extension_client/BUILD.gn b/interfaces/native/web_native_messaging_extension/extension_client/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0bd1e8c69b2d2b2c69ae8c33b0021acdb3ba4003 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension_client/BUILD.gn @@ -0,0 +1,49 @@ +# Copyright (c) 2025 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. + +import("//build/ohos.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("../../../../config.gni") +import("../../../../web_aafwk.gni") + +ohos_shared_library("web_extension_client") { + include_dirs = [ + "include", + "../../../../ohos_nweb/include", + "../../../../sa/web_native_messaging/common", + ] + + sources = [ + "src/web_native_messaging_extension_proxy.cpp", + ] + + configs = [ + "../extension:web_ex_cfg", + ] + + deps = [ + "../extension:connect_info", + ] + + external_deps = [ + "ability_runtime:ability_connect_callback_stub", + "ability_runtime:extension_manager", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + ] + + part_name = "webview" + subsystem_name = "web" +} diff --git a/interfaces/native/web_native_messaging_extension/extension_client/include/web_native_messaging_extension_proxy.h b/interfaces/native/web_native_messaging_extension/extension_client/include/web_native_messaging_extension_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..b49eb0a663fa0d80a4a56c7176e1718d15dcf1f5 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension_client/include/web_native_messaging_extension_proxy.h @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2025 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. +*/ + +#ifndef WEB_NATIVE_MESSAGING_EXTENSION_PROXY_H +#define WEB_NATIVE_MESSAGING_EXTENSION_PROXY_H + +#include +#include "iweb_native_messaging_extension.h" +#include "iremote_proxy.h" +#include "web_native_messaging_extension_connect_info.h" + +namespace OHOS { +namespace NWeb { + +class WebNativeMessagingExtensionProxy : public IRemoteProxy { +public: + explicit WebNativeMessagingExtensionProxy(const sptr &remote) + : IRemoteProxy(remote) {} + + virtual ~WebNativeMessagingExtensionProxy() {} + + // return stub process pid when ret > 0. + int32_t ConnectNative(WNMEConnectionInfo& connection); + + int32_t DisconnectNative(WNMEConnectionInfo& connection); + +private: + static inline BrokerDelegator delegator; + int32_t SendRequest(const int32_t code, WNMEConnectionInfo& connection); +}; +} // namespace NWeb +} // namespace OHOS +#endif // WEB_NATIVE_MESSAGING_EXTENSION_PROXY_H \ No newline at end of file diff --git a/interfaces/native/web_native_messaging_extension/extension_client/src/web_native_messaging_extension_proxy.cpp b/interfaces/native/web_native_messaging_extension/extension_client/src/web_native_messaging_extension_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4b91ca9df45fbe72be300c55a8dac03b7fc7195 --- /dev/null +++ b/interfaces/native/web_native_messaging_extension/extension_client/src/web_native_messaging_extension_proxy.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_extension_proxy.h" + +#include "ipc_types.h" +#include "message_parcel.h" +#include "web_native_messaging_log.h" + +namespace OHOS { +namespace NWeb { + +int32_t WebNativeMessagingExtensionProxy::SendRequest(const int32_t code, WNMEConnectionInfo& connection) +{ + WNMLOG_I("WebNativeMessagingExtensionProxy::SendRequest %{public}d", code); + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_SYNC); + + if (!data.WriteInterfaceToken(GetDescriptor())) { + WNMLOG_E("WriteInterfaceToken fail"); + return WNME_IPC_ERROR; + } + + WNMEConnectionInfoParcel sending; + sending.conn_info_ = connection; + + if (!sending.Marshalling(data)) { + WNMLOG_E("Failed to WriteParcelable"); + return WNME_IPC_ERROR; + } + + int32_t sendResult = Remote()->SendRequest(code, data, reply, option); + if (sendResult != ERR_NONE) { + WNMLOG_E("SendRequest failed %{public}d.", sendResult); + return WNME_IPC_ERROR; + } + + int32_t errorCode; + if (!reply.ReadInt32(errorCode)) { + WNMLOG_E("ReadInt32 failed"); + return WNME_IPC_ERROR; + } + + return errorCode; +} + +int32_t WebNativeMessagingExtensionProxy::ConnectNative(WNMEConnectionInfo& connection) +{ + return SendRequest(IWebNativeMessagingExtension::CODE_CONNECTNATIVE, connection); +} + +int32_t WebNativeMessagingExtensionProxy::DisconnectNative(WNMEConnectionInfo& connection) +{ + return SendRequest(IWebNativeMessagingExtension::CODE_DISCONNECTNATIVE, connection); +} +} // namespace NWeb +} // namespace OHOS diff --git a/ohos_nweb/BUILD.gn b/ohos_nweb/BUILD.gn index 9f1dc9a5855eaa864ec3b24509667f5ea1b065c3..70892fa4aba9256faacd48e2a4f7d3b3102a2736 100644 --- a/ohos_nweb/BUILD.gn +++ b/ohos_nweb/BUILD.gn @@ -124,7 +124,7 @@ ohos_shared_library("libnweb") { "${webview_path}/ohos_glue:ohos_adapter_glue_source", "${webview_path}/ohos_glue:ohos_base_glue_source", "${webview_path}/ohos_glue:ohos_nweb_glue_source", - "${webview_path}/sa:app_fwk_update", + "${webview_path}/sa/app_fwk_update:app_fwk_update", "${webview_path}/arkweb_utils:libarkweb_utils", ] diff --git a/sa/8350.json b/sa/app_fwk_update/8350.json similarity index 100% rename from sa/8350.json rename to sa/app_fwk_update/8350.json diff --git a/sa/BUILD.gn b/sa/app_fwk_update/BUILD.gn similarity index 88% rename from sa/BUILD.gn rename to sa/app_fwk_update/BUILD.gn index 457d4da1f24c1cc1944616b4228435f33966f693..8b3522ebd7540b4b007b8a5d9a4144e23770df51 100644 --- a/sa/BUILD.gn +++ b/sa/app_fwk_update/BUILD.gn @@ -56,18 +56,18 @@ ohos_shared_library("app_fwk_update_service") { version_script = "libapp_fwk_update_service.map" output_values = get_target_outputs(":app_fwk_update_service_interface") sources = [ - "../sa/include/app_fwk_update_service.h", - "../sa/src/app_fwk_update_client.cpp", - "../sa/src/app_fwk_update_load_callback.cpp", - "../sa/src/app_fwk_update_service.cpp", + "../app_fwk_update/include/app_fwk_update_service.h", + "../app_fwk_update/src/app_fwk_update_client.cpp", + "../app_fwk_update/src/app_fwk_update_load_callback.cpp", + "../app_fwk_update/src/app_fwk_update_service.cpp", ] sources += filter_include(output_values, [ "*.cpp" ]) deps = [ ":app_fwk_update_service_interface" ] public_configs = [ ":web_sa_interface" ] include_dirs = [ - "../sa/include", - "${target_gen_dir}/../ohos_nweb/include", - "../ohos_nweb/include", + "../app_fwk_update/include", + "${target_gen_dir}/../../ohos_nweb/include", + "../../ohos_nweb/include", "${target_gen_dir}", ] diff --git a/sa/IAppFwkUpdateService.idl b/sa/app_fwk_update/IAppFwkUpdateService.idl similarity index 100% rename from sa/IAppFwkUpdateService.idl rename to sa/app_fwk_update/IAppFwkUpdateService.idl diff --git a/sa/app_fwk_update_service.cfg b/sa/app_fwk_update/app_fwk_update_service.cfg similarity index 100% rename from sa/app_fwk_update_service.cfg rename to sa/app_fwk_update/app_fwk_update_service.cfg diff --git a/sa/include/app_fwk_update_client.h b/sa/app_fwk_update/include/app_fwk_update_client.h similarity index 100% rename from sa/include/app_fwk_update_client.h rename to sa/app_fwk_update/include/app_fwk_update_client.h diff --git a/sa/include/app_fwk_update_load_callback.h b/sa/app_fwk_update/include/app_fwk_update_load_callback.h similarity index 100% rename from sa/include/app_fwk_update_load_callback.h rename to sa/app_fwk_update/include/app_fwk_update_load_callback.h diff --git a/sa/include/app_fwk_update_service.h b/sa/app_fwk_update/include/app_fwk_update_service.h similarity index 100% rename from sa/include/app_fwk_update_service.h rename to sa/app_fwk_update/include/app_fwk_update_service.h diff --git a/sa/libapp_fwk_update_service.map b/sa/app_fwk_update/libapp_fwk_update_service.map similarity index 100% rename from sa/libapp_fwk_update_service.map rename to sa/app_fwk_update/libapp_fwk_update_service.map diff --git a/sa/src/app_fwk_update_client.cpp b/sa/app_fwk_update/src/app_fwk_update_client.cpp similarity index 98% rename from sa/src/app_fwk_update_client.cpp rename to sa/app_fwk_update/src/app_fwk_update_client.cpp index 4d30d41a16e5250375c89199a844bb0e9b1f5335..414b0fad6844eb3d596adcdd815920fe0900ccb0 100644 --- a/sa/src/app_fwk_update_client.cpp +++ b/sa/app_fwk_update/src/app_fwk_update_client.cpp @@ -30,8 +30,8 @@ #include "nweb_log.h" #include "system_ability_definition.h" -#include "base/web/webview/sa/app_fwk_update_service_proxy.h" -#include "base/web/webview/sa/iapp_fwk_update_service.h" +#include "base/web/webview/sa/app_fwk_update/app_fwk_update_service_proxy.h" +#include "base/web/webview/sa/app_fwk_update/iapp_fwk_update_service.h" namespace OHOS::NWeb { namespace { diff --git a/sa/src/app_fwk_update_load_callback.cpp b/sa/app_fwk_update/src/app_fwk_update_load_callback.cpp similarity index 100% rename from sa/src/app_fwk_update_load_callback.cpp rename to sa/app_fwk_update/src/app_fwk_update_load_callback.cpp diff --git a/sa/src/app_fwk_update_service.cpp b/sa/app_fwk_update/src/app_fwk_update_service.cpp similarity index 100% rename from sa/src/app_fwk_update_service.cpp rename to sa/app_fwk_update/src/app_fwk_update_service.cpp diff --git a/sa/web_native_messaging/8610.json b/sa/web_native_messaging/8610.json new file mode 100644 index 0000000000000000000000000000000000000000..a9bc9917621f7753c3147aa9f531d07d3c225fd4 --- /dev/null +++ b/sa/web_native_messaging/8610.json @@ -0,0 +1,12 @@ +{ + "process": "web_native_messaging_service", + "systemability": [ + { + "name": 8610, + "libpath": "libweb_native_messaging_service.z.so", + "run-on-create": false, + "distributed": false, + "dump-level": 1 + } + ] +} diff --git a/sa/web_native_messaging/BUILD.gn b/sa/web_native_messaging/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..43c15a993791e987e7b75eb0e517cd75d41908b8 --- /dev/null +++ b/sa/web_native_messaging/BUILD.gn @@ -0,0 +1,156 @@ +# Copyright (c) 2025 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. +import("//build/config/components/idl_tool/idl.gni") +import("//build/ohos.gni") + +group("web_native_messaging") { + deps = [ + ":web_native_messaging_kit", + ":web_native_messaging_service", + ":web_native_messaging_service_cfg", + ":web_native_messaging_service_profile", + ] +} + +config("web_native_messaging_interface") { + include_dirs = [ + "client/", + "common/", + "${target_gen_dir}", + ] +} + +idl_interface_sources = [ + "${target_gen_dir}/web_native_messaging_service_proxy.cpp", + "${target_gen_dir}/web_native_messaging_service_stub.cpp", +] + +idl_gen_interface("web_native_messaging_service_interface") { + src_idl = rebase_path("IWebNativeMessagingService.idl") + dst_file = string_join(",", idl_interface_sources) +} + +ohos_sa_profile("web_native_messaging_service_profile") { + sources = [ "8610.json" ] + part_name = "webview" +} + +ohos_prebuilt_etc("web_native_messaging_service_cfg") { + source = "web_native_messaging_service.cfg" + relative_install_dir = "init" + subsystem_name = "web" + part_name = "webview" +} + +ohos_shared_library("web_native_messaging_service") { + defines = [ "HILOG_TAG=\"web_native_messaging_service\"" ] + shlib_type = "sa" + version_script = "libweb_native_messaging_service.map" + output_values = get_target_outputs(":web_native_messaging_service_interface") + sources = [ + "../web_native_messaging/common/connection_native_info_parcel.cpp", + "../web_native_messaging/service/connect_native_request.cpp", + "../web_native_messaging/service/extension_ipc_connection.cpp", + "../web_native_messaging/service/service_delay_exit_task.cpp", + "../web_native_messaging/service/service_event_handler.cpp", + "../web_native_messaging/service/web_extension_connection_callback_proxy.cpp", + "../web_native_messaging/service/web_native_messaging_manager.cpp", + "../web_native_messaging/service/web_native_messaging_service.cpp", + "../../interfaces/native/web_native_messaging_extension/extension_client/src/web_native_messaging_extension_proxy.cpp", + "../../interfaces/native/web_native_messaging_extension/extension/src/web_native_messaging_extension_connect_info.cpp" + ] + sources += filter_include(output_values, [ "*.cpp" ]) + deps = [ ":web_native_messaging_service_interface" ] + include_dirs = [ + "./common", + "./service", + "${target_gen_dir}", + "${target_gen_dir}/../../ohos_nweb/include", + "../../interfaces/native/web_native_messaging_extension/extension/include/", + "../../interfaces/native/web_native_messaging_extension/extension_client/include/", + "../../ohos_nweb/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-g3", + ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_manager", + "ability_runtime:ability_context_native", + "ability_runtime:ability_start_options", + "ability_runtime:napi_common", + "access_token:libaccesstoken_sdk", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] + + #innerapi_tags = [ "platformsdk" ] + subsystem_name = "web" + part_name = "webview" +} + +ohos_shared_library("web_native_messaging_kit") { + output_values = get_target_outputs(":web_native_messaging_service_interface") + sources = [ + "../web_native_messaging/client/web_native_messaging_client.cpp", + "../web_native_messaging/client/web_native_messaging_load_callback.cpp", + "../web_native_messaging/client/web_extension_connection_callback.cpp", + "../web_native_messaging/client/web_extension_connection_callback_stub.cpp", + "../web_native_messaging/common/connection_native_info_parcel.cpp", + ] + sources += filter_include(output_values, [ "*.cpp" ]) + deps = [ ":web_native_messaging_service_interface" ] + public_configs = [ ":web_native_messaging_interface" ] + include_dirs = [ + "./client", + "./common", + "${target_gen_dir}", + "${target_gen_dir}/../../ohos_nweb/include", + "../../ohos_nweb/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-g3", + ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_manager", + "ability_runtime:ability_start_options", + "ability_runtime:napi_common", + "c_utils:utils", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + "samgr:samgr_proxy", + ] + + innerapi_tags = [ "platformsdk" ] + subsystem_name = "web" + part_name = "webview" +} diff --git a/sa/web_native_messaging/IWebNativeMessagingService.idl b/sa/web_native_messaging/IWebNativeMessagingService.idl new file mode 100644 index 0000000000000000000000000000000000000000..8b1ea39c26809b68253935e177b412329e27fe6b --- /dev/null +++ b/sa/web_native_messaging/IWebNativeMessagingService.idl @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ +sequenceable OHOS.IRemoteObject; +sequenceable OHOS.AAFwk.Want; +sequenceable OHOS.AAFwk.StartOptions; + +interface OHOS.NWeb.IWebNativeMessagingService { + void ConnectWebNativeMessagingExtension([in] IRemoteObject token, [in] Want want, + [in] IRemoteObject connectionCallback, [in] int connectionId, [out] int errorNum); + void DisconnectWebNativeMessagingExtension([in] int connectionId, [out] int errorNum); + void StartAbility([in] IRemoteObject token, [in] Want want, [in] StartOptions options, [out] int errorNum); + void StopNativeConnectionFromExtension([in] int connectionId, [out] int errorNum); +} diff --git a/sa/web_native_messaging/client/web_extension_connection_callback.cpp b/sa/web_native_messaging/client/web_extension_connection_callback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d025d0918dda26ecdcfb6e6c5188903899fc3820 --- /dev/null +++ b/sa/web_native_messaging/client/web_extension_connection_callback.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_extension_connection_callback.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +void WebExtensionConnectionCallback::OnConnect(ConnectionNativeInfo& info) +{ + if (callback_) { + callback_->OnExtensionConnect(info); + } +} + +void WebExtensionConnectionCallback::OnDisconnect(ConnectionNativeInfo& info) +{ + if (callback_) { + callback_->OnExtensionDisconnect(info); + } +} + +void WebExtensionConnectionCallback::OnFailed(int32_t errorNum) +{ + if (callback_) { + callback_->OnExtensionFailed(connectionId_, errorNum); + } +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/client/web_extension_connection_callback.h b/sa/web_native_messaging/client/web_extension_connection_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..977ad6e7d5c5b9fae52b4843535d6b73f4703e42 --- /dev/null +++ b/sa/web_native_messaging/client/web_extension_connection_callback.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef WEB_EXTENSION_CONNECTION_CALLBACK_H +#define WEB_EXTENSION_CONNECTION_CALLBACK_H + +#include "iremote_stub.h" +#include "nocopyable.h" +#include "web_extension_connection_callback_stub.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +class IExtensionConnectionCallback { +public: + IExtensionConnectionCallback() {} + virtual ~IExtensionConnectionCallback() = default; + + virtual void OnExtensionConnect(ConnectionNativeInfo& info) {} + virtual void OnExtensionDisconnect(ConnectionNativeInfo& info) {} + virtual void OnExtensionFailed(int32_t connectionId, int32_t errorNum) {} +}; + +class WebExtensionConnectionCallback : public WebExtensionConnectionCallbackStub { +public: + explicit WebExtensionConnectionCallback(std::shared_ptr jsCallback) + { + callback_ = jsCallback; + } + + ~WebExtensionConnectionCallback() override + { + WNMLOG_D("~WebExtensionConnectionCallback"); + } + + void OnConnect(ConnectionNativeInfo& info) override; + void OnDisconnect(ConnectionNativeInfo& info) override; + void OnFailed(int32_t errorNum) override; + + int32_t connectionId_; +private: + std::shared_ptr callback_; +}; +} // namespace OHOS::NWeb +#endif //WEB_EXTENSION_CONNECTION_CALLBACK_H diff --git a/sa/web_native_messaging/client/web_extension_connection_callback_stub.cpp b/sa/web_native_messaging/client/web_extension_connection_callback_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15d52d0381b02537fb6d2ead681b3ebdf21850d1 --- /dev/null +++ b/sa/web_native_messaging/client/web_extension_connection_callback_stub.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_extension_connection_callback_stub.h" + +#include "connection_native_info_parcel.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +int32_t WebExtensionConnectionCallbackStub::OnRemoteRequest( + uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) +{ + std::u16string descriptor = data.ReadInterfaceToken(); + if (descriptor != IWebExtensionConnectionCallback::GetDescriptor()) { + WNMLOG_E("Get unexpect descriptor"); + return -1; + } + int32_t msgCode = static_cast(code); + switch (msgCode) { + case IWebExtensionConnectionCallback::ON_CONNECT_CODE: { + sptr infoParcel = data.ReadParcelable(); + if (infoParcel == nullptr) { + WNMLOG_E("read info parcel failed"); + return -1; + } + OnConnect(infoParcel->connectionNativeInfo_); + break; + } + case IWebExtensionConnectionCallback::ON_DISCONNECT_CODE: { + sptr infoParcel = data.ReadParcelable(); + if (infoParcel == nullptr) { + WNMLOG_E("read info parcel failed"); + return -1; + } + OnDisconnect(infoParcel->connectionNativeInfo_); + break; + } + case IWebExtensionConnectionCallback::ON_FAILED_CODE: { + int32_t errorNum; + if (!data.ReadInt32(errorNum)) { + WNMLOG_E("read errorNum failed"); + return -1; + } + OnFailed(errorNum); + break; + } + default: + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + return 0; +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/client/web_extension_connection_callback_stub.h b/sa/web_native_messaging/client/web_extension_connection_callback_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..dada9fcc4bcf6de16a00ee3eec54f1b4d9ada66d --- /dev/null +++ b/sa/web_native_messaging/client/web_extension_connection_callback_stub.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef WEB_EXTENSION_CONNECTION_CALLBACK_STUB_H +#define WEB_EXTENSION_CONNECTION_CALLBACK_STUB_H + +#include "i_web_extension_connection_callback.h" + +#include "iremote_stub.h" +#include "nocopyable.h" + +namespace OHOS::NWeb { +class WebExtensionConnectionCallbackStub : public IRemoteStub { +public: + WebExtensionConnectionCallbackStub() = default; + virtual ~WebExtensionConnectionCallbackStub() = default; + + int32_t OnRemoteRequest(uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) override; +}; +} // namespace OHOS::NWeb +#endif // WEB_EXTENSION_CONNECTION_CALLBACK_STUB_H diff --git a/sa/web_native_messaging/client/web_native_messaging_client.cpp b/sa/web_native_messaging/client/web_native_messaging_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..337591693daaab42e2ea3c10d5cad21ba052b625 --- /dev/null +++ b/sa/web_native_messaging/client/web_native_messaging_client.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "web_native_messaging_client.h" + +#include "if_system_ability_manager.h" +#include "ipc_skeleton.h" +#include "iservice_registry.h" +#include "isystem_ability_load_callback.h" +#include "web_native_messaging_log.h" +#include "system_ability_definition.h" +#include "web_native_messaging_load_callback.h" + +#include "base/web/webview/sa/web_native_messaging/iweb_native_messaging_service.h" +#include "base/web/webview/sa/web_native_messaging/web_native_messaging_service_proxy.h" + +namespace OHOS::NWeb { +namespace { +const int LOAD_SA_TIMEOUT_MS = 2 * 1000; +} // namespace + +WebNativeMessagingClient::WebNativeMessagingClient() +{ + webNativeMessagingDiedRecipient_ = new (std::nothrow) WebNativeMessagingDiedRecipient(); + if (webNativeMessagingDiedRecipient_ == nullptr) { + WNMLOG_E("create web native messaging service died recipient failed"); + } +} +WebNativeMessagingClient& WebNativeMessagingClient::GetInstance() +{ + static WebNativeMessagingClient client; + return client; +} + +void WebNativeMessagingClient::SetWebNativeMessagingProxy(const sptr& remoteObject) +{ + std::lock_guard lock(mutex_); + webNativeMessagingProxy_ = iface_cast(remoteObject); + if (webNativeMessagingProxy_) { + WNMLOG_I("SetWebNativeMessagingProxy is not null"); + } +} + +sptr WebNativeMessagingClient::GetWebNativeMessaging() +{ + std::lock_guard lock(mutex_); + return webNativeMessagingProxy_; +} + +sptr WebNativeMessagingClient::GetWebNativeMessagingProxy() +{ + auto webNativeMessagingProxy = GetWebNativeMessaging(); + if (webNativeMessagingProxy != nullptr) { + return webNativeMessagingProxy; + } + auto sam = OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (sam == nullptr) { + WNMLOG_E("load web native messaging service sam is null"); + return nullptr; + } + + auto remoteObj = sam->CheckSystemAbility(SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID); + if (remoteObj != nullptr) { + webNativeMessagingProxy = iface_cast(remoteObj); + return webNativeMessagingProxy; + } + if (!LoadNativeMessagingService()) { + WNMLOG_I("get web native messaging service is null"); + return nullptr; + } + webNativeMessagingProxy = GetWebNativeMessaging(); + if (webNativeMessagingProxy == nullptr) { + WNMLOG_I("get web native messaging service proxy is null"); + return nullptr; + } + WNMLOG_I("load web native messaging service sa finished"); + return webNativeMessagingProxy; +} + +int WebNativeMessagingClient::ConnectWebNativeMessagingExtension( + const sptr& token, const AAFwk::Want& want, + const sptr& connectCallback, int32_t connectionId) +{ + if (token == nullptr) { + WNMLOG_E("token is invalid"); + return ConnectNativeRet::CONTEXT_ERROR; + } + if (connectCallback == nullptr) { + WNMLOG_E("connectCallback is invalid"); + return ConnectNativeRet::CALLBACK_ERROR; + } + auto proxy = GetWebNativeMessagingProxy(); + if (proxy == nullptr) { + WNMLOG_E("connect web native messaging failed, proxy is null"); + return ConnectNativeRet::IPC_ERROR; + } + + int32_t errorNum = 0; + int32_t ret = + proxy->ConnectWebNativeMessagingExtension(token, want, connectCallback, connectionId, errorNum); + if (ret != 0) { + WNMLOG_E("connect web extension result: %{public}d", ret); + return ConnectNativeRet::IPC_ERROR; + } + return errorNum; +} + +int WebNativeMessagingClient::DisconnectWebNativeMessagingExtension(int32_t connectionId) +{ + auto proxy = GetWebNativeMessagingProxy(); + if (proxy == nullptr) { + WNMLOG_E("disconnect web native messaging failed, proxy is null"); + return ConnectNativeRet::IPC_ERROR; + } + + int32_t errorNum = 0; + int32_t ret = + proxy->DisconnectWebNativeMessagingExtension(connectionId, errorNum); + if (ret != 0) { + WNMLOG_E("disconnect web extension result: %{public}d", ret); + return ConnectNativeRet::IPC_ERROR; + } + return errorNum; +} + +int WebNativeMessagingClient::StartAbility(const sptr& token, + const AAFwk::Want& want, const AAFwk::StartOptions& options) +{ + auto proxy = GetWebNativeMessagingProxy(); + if (proxy == nullptr) { + WNMLOG_E("start ability failed, proxy is null"); + return ConnectNativeRet::IPC_ERROR; + } + + int32_t errorNum = 0; + int32_t ret = + proxy->StartAbility(token, want, options, errorNum); + if (ret != 0) { + WNMLOG_E("Start ability result: %{public}d", ret); + return ConnectNativeRet::IPC_ERROR; + } + return errorNum; +} + +int WebNativeMessagingClient::StopNativeConnectionFromExtension(int32_t connectionId) +{ + auto proxy = GetWebNativeMessagingProxy(); + if (proxy == nullptr) { + WNMLOG_E("stop native connection failed, proxy is null"); + return ConnectNativeRet::IPC_ERROR; + } + + int32_t errorNum = 0; + int32_t ret = + proxy->StopNativeConnectionFromExtension(connectionId, errorNum); + if (ret != 0) { + WNMLOG_E("Stop native connection from extension result: %{public}d", ret); + return ConnectNativeRet::IPC_ERROR; + } + return errorNum; +} + +bool WebNativeMessagingClient::LoadNativeMessagingService() +{ + { + std::unique_lock lock(loadSaMutex_); + loadSaFinished_ = false; + } + auto systemAbilityMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityMgr == nullptr) { + WNMLOG_I("failed to get system ability manager"); + return false; + } + sptr loadCallback = new (std::nothrow) WebNativeMessagingLoadCallback(); + if (loadCallback == nullptr) { + WNMLOG_I("failed to create load callback"); + return false; + } + auto ret = systemAbilityMgr->LoadSystemAbility(SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID, loadCallback); + if (ret != 0) { + WNMLOG_W("load native messaging service failed."); + return false; + } + { + std::unique_lock lock(loadSaMutex_); + auto waitStatus = loadSaCondition_.wait_for( + lock, std::chrono::milliseconds(LOAD_SA_TIMEOUT_MS), [this]() { return loadSaFinished_; }); + if (!waitStatus) { + auto remoteObj = systemAbilityMgr->CheckSystemAbility(SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID); + if (remoteObj != nullptr) { + SetWebNativeMessagingProxy(remoteObj); + return true; + } + WNMLOG_I("load web native messaging service timeout."); + return false; + } + WNMLOG_I("load web native messaging service success."); + return true; + } +} + +void WebNativeMessagingClient::OnLoadSystemAbilitySuccess(const sptr& remoteObject) +{ + WNMLOG_I("on load systemAbility success"); + if (webNativeMessagingDiedRecipient_ == nullptr) { + WNMLOG_E("register web native messaging service died recipient failed"); + return; + } + if (!remoteObject->AddDeathRecipient(webNativeMessagingDiedRecipient_)) { + WNMLOG_E("add web native messaging service died recipient failed"); + return; + } + SetWebNativeMessagingProxy(remoteObject); + std::unique_lock lock(loadSaMutex_); + loadSaFinished_ = true; + loadSaCondition_.notify_one(); +} + +void WebNativeMessagingClient::OnLoadSystemAbilityFail() +{ + SetWebNativeMessagingProxy(nullptr); + std::unique_lock lock(loadSaMutex_); + loadSaFinished_ = true; + loadSaCondition_.notify_one(); +} + +void WebNativeMessagingClient::WebNativeMessagingDiedRecipient::OnRemoteDied(const wptr& remoteObject) +{ + if (remoteObject == nullptr) { + WNMLOG_E("remote object of web native messaging service died recipient is nullptr"); + return; + } + WebNativeMessagingClient::GetInstance().WebNativeMessagingOnRemoteDied(remoteObject); +} + +void WebNativeMessagingClient::WebNativeMessagingOnRemoteDied(const wptr& remoteObject) +{ + WNMLOG_I("remote object of web native messaging service died recipient is died"); + auto webNativeMessagingProxy = GetWebNativeMessaging(); + if (webNativeMessagingProxy == nullptr) { + WNMLOG_E("web native messaging proxy is nullptr"); + return; + } + sptr remotePromote = remoteObject.promote(); + if (remotePromote == nullptr) { + WNMLOG_E("remote object of web native messaging service promote fail"); + return; + } + if (webNativeMessagingProxy->AsObject() != remotePromote) { + WNMLOG_E("web native messaging died recipient not find remote object"); + return; + } + remotePromote->RemoveDeathRecipient(webNativeMessagingDiedRecipient_); + SetWebNativeMessagingProxy(nullptr); + if (deathCallback_) { + deathCallback_(); + } +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/client/web_native_messaging_client.h b/sa/web_native_messaging/client/web_native_messaging_client.h new file mode 100644 index 0000000000000000000000000000000000000000..99b7517ffda1cac75a94ed88bc0c1a0d722661dc --- /dev/null +++ b/sa/web_native_messaging/client/web_native_messaging_client.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef OHOS_NWEB_WEB_NATIVE_MESSAGING_CLIENT_H +#define OHOS_NWEB_WEB_NATIVE_MESSAGING_CLIENT_H + +#include +#include +#include +#include + +#include "ability_connect_callback.h" +#include "iweb_native_messaging_service.h" +#include "singleton.h" +#include "start_options.h" +#include "want.h" +#include "web_native_messaging_common.h" + +namespace OHOS::NWeb { +class WebNativeMessagingClient { +public: + WebNativeMessagingClient(); + virtual ~WebNativeMessagingClient() = default; + static WebNativeMessagingClient& GetInstance(); + + void OnLoadSystemAbilitySuccess(const sptr& object); + void OnLoadSystemAbilityFail(); + void WebNativeMessagingOnRemoteDied(const wptr& remoteObject); + int ConnectWebNativeMessagingExtension(const sptr& token, const AAFwk::Want& want, + const sptr& connectCallback, int32_t connectionId); + int DisconnectWebNativeMessagingExtension(int32_t connectionId); + int StartAbility(const sptr& token, const AAFwk::Want& want, const AAFwk::StartOptions& options); + int StopNativeConnectionFromExtension(int32_t connectionId); + void SetUserDefineDiedRecipient(std::function deathCallback) + { + deathCallback_ = deathCallback; + } + +private: + bool LoadNativeMessagingService(); + void SetWebNativeMessagingProxy(const sptr& remoteObject); + sptr GetWebNativeMessaging(); + sptr GetWebNativeMessagingProxy(); + class WebNativeMessagingDiedRecipient : public IRemoteObject::DeathRecipient { + public: + void OnRemoteDied(const wptr& remote) override; + }; + +private: + std::function deathCallback_; + std::condition_variable loadSaCondition_; + std::mutex loadSaMutex_; + bool loadSaFinished_ { false }; + std::mutex mutex_; + sptr webNativeMessagingProxy_ = nullptr; + sptr webNativeMessagingDiedRecipient_ = nullptr; + DISALLOW_COPY_AND_MOVE(WebNativeMessagingClient); +}; +} // namespace OHOS::NWeb +#endif // OHOS_NWEB_WEB_NATIVE_MESSAGING_CLIENT_H diff --git a/sa/web_native_messaging/client/web_native_messaging_load_callback.cpp b/sa/web_native_messaging/client/web_native_messaging_load_callback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec5cb17420c3f7e1d4763c41d227cf5e20d51921 --- /dev/null +++ b/sa/web_native_messaging/client/web_native_messaging_load_callback.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "web_native_messaging_load_callback.h" + +#include "system_ability_definition.h" +#include "web_native_messaging_client.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +void WebNativeMessagingLoadCallback::OnLoadSystemAbilitySuccess( + int32_t systemAbilityId, const sptr& remoteObject) +{ + if (systemAbilityId != SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID) { + WNMLOG_E("system ability id mismatch, sa id: %{public}d", systemAbilityId); + return; + } + + if (remoteObject == nullptr) { + WNMLOG_E("remoteObject is nullptr"); + return; + } + WNMLOG_D("load system ability success %{public}d", systemAbilityId); + WebNativeMessagingClient::GetInstance().OnLoadSystemAbilitySuccess(remoteObject); +} + +void WebNativeMessagingLoadCallback::OnLoadSystemAbilityFail(int32_t systemAbilityId) +{ + if (systemAbilityId != SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID) { + WNMLOG_E("system ability id mismatch, sa id: %{public}d", systemAbilityId); + return; + } + + WNMLOG_D("load system ability failed %{public}d", systemAbilityId); + WebNativeMessagingClient::GetInstance().OnLoadSystemAbilityFail(); +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/client/web_native_messaging_load_callback.h b/sa/web_native_messaging/client/web_native_messaging_load_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..67db89f685f4448732efc09973ea376e45ff530b --- /dev/null +++ b/sa/web_native_messaging/client/web_native_messaging_load_callback.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef OHOS_NWEB_WEB_NATIVE_MESSAGING_CALLBACK_H +#define OHOS_NWEB_WEB_NATIVE_MESSAGING_CALLBACK_H + +#include "iremote_object.h" +#include "system_ability_load_callback_stub.h" + +namespace OHOS::NWeb { +class WebNativeMessagingLoadCallback : public SystemAbilityLoadCallbackStub { +public: + WebNativeMessagingLoadCallback() = default; + virtual ~WebNativeMessagingLoadCallback() = default; + + void OnLoadSystemAbilitySuccess(int32_t systemAbilityId, const sptr& remoteObject) override; + void OnLoadSystemAbilityFail(int32_t systemAbilityId) override; +}; +} // namespace OHOS::NWeb +#endif // OHOS_NWEB_WEB_NATIVE_MESSAGING_CALLBACK_H diff --git a/sa/web_native_messaging/common/connection_native_info_parcel.cpp b/sa/web_native_messaging/common/connection_native_info_parcel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5dd94df359187fd1125d6e7f361991a0ebda2b86 --- /dev/null +++ b/sa/web_native_messaging/common/connection_native_info_parcel.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "connection_native_info_parcel.h" + +#include "securec.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +bool ConnectionNativeInfoParcel::Marshalling(Parcel& out) const +{ + if (!(out.WriteInt32(connectionNativeInfo_.connectionId)) || + !(out.WriteString(connectionNativeInfo_.bundleName)) || + !(out.WriteString(connectionNativeInfo_.extensionOrigin)) || + !(out.WriteInt32(connectionNativeInfo_.extensionPid))) { + WNMLOG_E("Write connect native info fail"); + return false; + } + return true; +} + +ConnectionNativeInfoParcel* ConnectionNativeInfoParcel::Unmarshalling(Parcel& in) +{ + ConnectionNativeInfoParcel* infoParcel = new (std::nothrow) ConnectionNativeInfoParcel(); + if (infoParcel == nullptr) { + return nullptr; + } + + ConnectionNativeInfo info = {}; + int32_t connectionId; + if (!in.ReadInt32(connectionId)) { + WNMLOG_E("connectionId is invalid"); + delete infoParcel; + return nullptr; + } + info.connectionId = connectionId; + + std::string bundleName = in.ReadString(); + if (bundleName.empty()) { + WNMLOG_E("bundleName is invalid"); + delete infoParcel; + return nullptr; + } + info.bundleName = bundleName; + + std::string extensionOrigin = in.ReadString(); + if (extensionOrigin.empty()) { + WNMLOG_E("extensionOrigin is invalid"); + delete infoParcel; + return nullptr; + } + info.extensionOrigin = extensionOrigin; + + int32_t extensionPid; + if (!in.ReadInt32(extensionPid)) { + WNMLOG_E("extensionPid is invalid"); + delete infoParcel; + return nullptr; + } + info.extensionPid = extensionPid; + infoParcel->connectionNativeInfo_ = info; + return infoParcel; +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/common/connection_native_info_parcel.h b/sa/web_native_messaging/common/connection_native_info_parcel.h new file mode 100644 index 0000000000000000000000000000000000000000..16f34f1e65501fb713c9a68473087aac5efa81e1 --- /dev/null +++ b/sa/web_native_messaging/common/connection_native_info_parcel.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_CONNECTION_NATIVE_INFO_PARCEL_H +#define NWEB_CONNECTION_NATIVE_INFO_PARCEL_H + +#include "parcel.h" +#include "web_native_messaging_common.h" + +namespace OHOS::NWeb { +struct ConnectionNativeInfoParcel final : public Parcelable { + ConnectionNativeInfoParcel() = default; + + ~ConnectionNativeInfoParcel() override = default; + + bool Marshalling(Parcel& out) const override; + + static ConnectionNativeInfoParcel* Unmarshalling(Parcel& in); + + ConnectionNativeInfo connectionNativeInfo_ = {}; +}; +} // namespace OHOS::NWeb + +#endif // NWEB_CONNECTION_NATIVE_INFO_PARCEL_H diff --git a/sa/web_native_messaging/common/i_web_extension_connection_callback.h b/sa/web_native_messaging/common/i_web_extension_connection_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..27614c3c1a3891f8ccec0ade9126e3087abfcdb3 --- /dev/null +++ b/sa/web_native_messaging/common/i_web_extension_connection_callback.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef I_WEB_EXTENSION_CONNECTION_CALLBACK_H +#define I_WEB_EXTENSION_CONNECTION_CALLBACK_H + +#include "iremote_broker.h" +#include "web_native_messaging_common.h" + +namespace OHOS::NWeb { +class IWebExtensionConnectionCallback : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"ohos.nweb.IWebExtensionConnectionCallback"); + virtual void OnConnect(ConnectionNativeInfo& info) = 0; + virtual void OnDisconnect(ConnectionNativeInfo& info) = 0; + virtual void OnFailed(int32_t errorNum) = 0; + + enum InterfaceCode { + ON_CONNECT_CODE = 0, + ON_DISCONNECT_CODE = 1, + ON_FAILED_CODE = 2, + }; +}; +} // namespace OHOS::NWeb +#endif // I_WEB_EXTENSION_CONNECTION_CALLBACK_H diff --git a/sa/web_native_messaging/common/web_native_messaging_common.cpp b/sa/web_native_messaging/common/web_native_messaging_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec54c596fc4cbc67168b468a0d02b7e07d9c5bbb --- /dev/null +++ b/sa/web_native_messaging/common/web_native_messaging_common.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + #include "web_native_messaging_common.h" + +namespace OHOS::NWeb::NativeMessageError { +namespace { +constexpr int32_t JS_PERMISSION_ERROR = 201; +constexpr int32_t JS_CONTEXT_NOT_EXIST = 16000011; +constexpr int32_t JS_INTERNAL_ERROR = 16000050; +const std::string JS_PERMISSION_ERROR_MSG = "Permission denied."; +const std::string JS_CONTEXT_NOT_EXIST_MSG = "This connection is not exist."; +const std::string JS_INTERNAL_ERROR_MSG = "Internal error, code is "; +} + +bool IsNativeMessagingErr(int32_t errCode) +{ + return (errCode > MIN_CN_ERROR && errCode <= 0); +} + +int32_t NativeCodeToJsCode(int32_t ret, std::string& errorMsg) +{ + int32_t errorCode; + switch (ret) { + case ConnectNativeRet::PERMISSION_CHECK_ERROR: + errorCode = JS_PERMISSION_ERROR; + errorMsg = JS_PERMISSION_ERROR_MSG; + break; + case ConnectNativeRet::CONNECTION_NOT_EXIST: + errorCode = JS_CONTEXT_NOT_EXIST; + errorMsg = JS_CONTEXT_NOT_EXIST_MSG; + break; + default: + errorCode = JS_INTERNAL_ERROR; + errorMsg = JS_INTERNAL_ERROR_MSG + std::to_string(ret); + break; + } + return errorCode; +} +} // namespace OHOS::NWeb::NativeMessageError diff --git a/sa/web_native_messaging/common/web_native_messaging_common.h b/sa/web_native_messaging/common/web_native_messaging_common.h new file mode 100644 index 0000000000000000000000000000000000000000..d2293c069ac885ccccf88188055ec12aec1efcb2 --- /dev/null +++ b/sa/web_native_messaging/common/web_native_messaging_common.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef OHOS_NWEB_WEB_NATIVE_MESSAGING_COMMON_H +#define OHOS_NWEB_WEB_NATIVE_MESSAGING_COMMON_H + +#include + +namespace OHOS::NWeb { +enum ConnectNativeRet : int32_t { + SUCCESS = 0, + PERMISSION_CHECK_ERROR = -1, + CONTEXT_ERROR = -2, + WANT_FORMAT_ERROR = -3, + CONNECTION_NOT_EXIST = -4, + MEMORY_ERROR = -5, + CALLBACK_ERROR = -6, + IPC_ERROR = -7, + SERVICE_INIT_ERROR = -8, + CONNECTION_ID_EXIST = -9, + REQUEST_SIZE_TOO_LARGE = -10, + CONNECT_STATUS_ERROR = -11, + ABILITY_CONNECTION_ERROR = -12, + SERVICE_DIED_ERROR = -13, + MIN_CN_ERROR = -1000, +}; + +struct ConnectionNativeInfo { + int32_t connectionId; + std::string bundleName; + std::string extensionOrigin; + int32_t extensionPid; +}; + +namespace NativeMessageError { +bool IsNativeMessagingErr(int32_t errCode); +int32_t NativeCodeToJsCode(int32_t ret, std::string& errorMsg); +} + +// move it to samgr later +#define SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID 8610 +} // namespace OHOS::NWeb +#endif // OHOS_NWEB_WEB_NATIVE_MESSAGING_COMMON_H diff --git a/sa/web_native_messaging/common/web_native_messaging_log.h b/sa/web_native_messaging/common/web_native_messaging_log.h new file mode 100644 index 0000000000000000000000000000000000000000..4e9ba6af873b825e39adc39ef734e52b444bd488 --- /dev/null +++ b/sa/web_native_messaging/common/web_native_messaging_log.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef WEB_NATIVE_MESSAGING_LOG_H +#define WEB_NATIVE_MESSAGING_LOG_H + +#include +#include +#include + +#define LOG_WEB_NATIVE_MESSAGING_DOMAIN 0xD004500 +#define WNM_HILOG_TAG "webNativeMessaging" +#define WNM_FILE_NAME (__builtin_strrchr("/" __FILE__, '/') + 1) +#define WNM_FUNC_LINE_FMT "[%{public}s:%{public}d] " + +#define WNMLOG_D(fmt, ...) do { \ + uint32_t domain = LOG_WEB_NATIVE_MESSAGING_DOMAIN; \ + HILOG_IMPL(LOG_CORE, LOG_DEBUG, domain, WNM_HILOG_TAG, WNM_FUNC_LINE_FMT fmt, \ + WNM_FILE_NAME, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#define WNMLOG_I(fmt, ...) do { \ + uint32_t domain = LOG_WEB_NATIVE_MESSAGING_DOMAIN; \ + HILOG_IMPL(LOG_CORE, LOG_INFO, domain, WNM_HILOG_TAG, WNM_FUNC_LINE_FMT fmt, \ + WNM_FILE_NAME, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#define WNMLOG_W(fmt, ...) do { \ + uint32_t domain = LOG_WEB_NATIVE_MESSAGING_DOMAIN; \ + HILOG_IMPL(LOG_CORE, LOG_WARN, domain, WNM_HILOG_TAG, WNM_FUNC_LINE_FMT fmt, \ + WNM_FILE_NAME, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#define WNMLOG_E(fmt, ...) do { \ + uint32_t domain = LOG_WEB_NATIVE_MESSAGING_DOMAIN; \ + HILOG_IMPL(LOG_CORE, LOG_ERROR, domain, WNM_HILOG_TAG, WNM_FUNC_LINE_FMT fmt, \ + WNM_FILE_NAME, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#define WNMLOG_F(fmt, ...) do { \ + uint32_t domain = LOG_WEB_NATIVE_MESSAGING_DOMAIN; \ + HILOG_IMPL(LOG_CORE, LOG_FATAL, domain, WNM_HILOG_TAG, WNM_FUNC_LINE_FMT fmt, \ + WNM_FILE_NAME, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#endif // WEB_NATIVE_MESSAGING_LOG_H + diff --git a/sa/web_native_messaging/libweb_native_messaging_service.map b/sa/web_native_messaging/libweb_native_messaging_service.map new file mode 100644 index 0000000000000000000000000000000000000000..d7889fd9b4286b9c0980b7964c48c14d6aeec3b3 --- /dev/null +++ b/sa/web_native_messaging/libweb_native_messaging_service.map @@ -0,0 +1,8 @@ +{ + global: + extern "C++" { + OHOS::NWeb::WebNativeMessagingClient::*; + }; + local: + *; +}; diff --git a/sa/web_native_messaging/service/.extension_ipc_connection.cpp.swp b/sa/web_native_messaging/service/.extension_ipc_connection.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sa/web_native_messaging/service/connect_native_request.cpp b/sa/web_native_messaging/service/connect_native_request.cpp new file mode 100644 index 0000000000000000000000000000000000000000..426cdcb100bb0d8ca3d23fd29894244e6b3d4461 --- /dev/null +++ b/sa/web_native_messaging/service/connect_native_request.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "connect_native_request.h" +#include "i_web_extension_connection_callback.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +namespace { +const std::string WANT_EXTENSION_ORIGIN_PARAM_KEY = "ohos.arkweb.extensionOrigin"; +const std::string WANT_READ_PIPE_PARAM_KEY = "ohos.arkweb.messageReadPipe"; +const std::string WANT_WRITE_PIPE_PARAM_KEY = "ohos.arkweb.messageWritePipe"; +} // namespace + +std::mutex ConnectionNativeRequest::connectIdMutex_; +std::unordered_map> ConnectionNativeRequest::connectIdMap_; +std::mutex ConnectionNativeRequest::InnerIdMapMutex_; +InnerConnectionIdMapType ConnectionNativeRequest::InnerIdMap_; + +ConnectionNativeRequest::~ConnectionNativeRequest() +{ + ResetAllFileDescriptors(); + ConnectionNativeRequest::RemoveRequestMap(this->innerConnectionId_); + ConnectionNativeRequest::RemoveInnerConnectionIdMap(this->callerTokenId_, + this->callerPid_, this->callerConnectionId_); + WNMLOG_D("~ConnectionNativeRequest"); +} + +void ConnectionNativeRequest::InsertRequestMap(std::shared_ptr request) +{ + if (!request) { + WNMLOG_E("InsertRequestMap request is null"); + return; + } + std::lock_guard lock(connectIdMutex_); + std::weak_ptr weak = request; + connectIdMap_[request->GetInnerConnectionId()] = weak; + + InsertInnerConnectionIdMap(request->GetCallerTokenId(), request->GetCallerPid(), + request->GetCallerConnectionId(), request->GetInnerConnectionId()); +} + +void ConnectionNativeRequest::RemoveRequestMap(int32_t id) +{ + std::lock_guard lock(connectIdMutex_); + connectIdMap_.erase(id); +} + +int32_t ConnectionNativeRequest::GetInnerConnectionIdFromMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId) +{ + std::lock_guard lock(InnerIdMapMutex_); + InnerConnectIdMapKey key(tokenId, pid, connectionId); + auto iter = InnerIdMap_.find(key); + return iter == InnerIdMap_.end() ? -1 : iter->second; +} + +void ConnectionNativeRequest::InsertInnerConnectionIdMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId, int32_t innerId) +{ + std::lock_guard lock(InnerIdMapMutex_); + InnerConnectIdMapKey key(tokenId, pid, connectionId); + InnerIdMap_[key] = innerId; +} + +void ConnectionNativeRequest::RemoveInnerConnectionIdMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId) +{ + std::lock_guard lock(InnerIdMapMutex_); + InnerConnectIdMapKey key(tokenId, pid, connectionId); + auto iter = InnerIdMap_.find(key); + if (iter != InnerIdMap_.end()) { + InnerIdMap_.erase(iter); + } +} + +int32_t ConnectionNativeRequest::GetAliveSize() +{ + std::lock_guard lock(connectIdMutex_); + return connectIdMap_.size(); +} + +std::shared_ptr ConnectionNativeRequest::GetExistConnectId(int32_t id) +{ + std::lock_guard lock(connectIdMutex_); + auto iter = connectIdMap_.find(id); + if (iter != connectIdMap_.end()) { + std::shared_ptr request = iter->second.lock(); + return request; + } + return nullptr; +} + +void ConnectionNativeRequest::DumpAllRequest(int fd) +{ + std::lock_guard lock(connectIdMutex_); + for (auto iter = connectIdMap_.begin(); iter != connectIdMap_.end(); iter++) { + auto request = iter->second.lock(); + if (request) { + dprintf(fd, " Request:%s\n", request->ToString().c_str()); + } + } +} + +void ConnectionNativeRequest::DumpInnerIdMap(int fd) +{ + std::lock_guard lock(InnerIdMapMutex_); + for (auto iter = InnerIdMap_.begin(); iter != InnerIdMap_.end(); iter++) { + auto key = iter->first; + auto innerId = iter->second; + dprintf(fd, " Entry:callerTokenId:%d;callerPid:%d;callerConnectId:%d;innerConnectionId:%d\n", + key.callerTokenId, key.callerPid, key.callerConnectId, innerId); + } +} + +ConnectNativeRet ConnectionNativeRequest::FillRequestWithWant(const AAFwk::Want& want) +{ + std::string bundleName = want.GetBundle(); + if (bundleName.empty()) { + WNMLOG_E("want bundle name is invalid"); + return ConnectNativeRet::WANT_FORMAT_ERROR; + } + targetBundleName_ = bundleName; + + std::string abilityName = want.GetElement().GetAbilityName(); + if (abilityName.empty()) { + WNMLOG_E("want ability name is invalid"); + return ConnectNativeRet::WANT_FORMAT_ERROR; + } + targetAbilityName_ = abilityName; + + std::string extensionOrigin = want.GetParams().GetStringParam(WANT_EXTENSION_ORIGIN_PARAM_KEY); + if (extensionOrigin.empty()) { + WNMLOG_E("want extensionOrigin is invalid"); + return ConnectNativeRet::WANT_FORMAT_ERROR; + } + callerExtensionOrigin_ = extensionOrigin; + + int32_t readFd = -1; + AAFwk::WantParams readPipeParams = want.GetParams().GetWantParams(WANT_READ_PIPE_PARAM_KEY); + if (readPipeParams.IsEmpty() || readPipeParams.GetStringParam("type") != "FD" || + (readFd = readPipeParams.GetIntParam("value", -1)) == -1) { + WNMLOG_E("want readFd is invalid"); + return ConnectNativeRet::WANT_FORMAT_ERROR; + } + fdRead_ = readFd; + + int32_t writeFd = -1; + AAFwk::WantParams writePipeParams = want.GetParams().GetWantParams(WANT_WRITE_PIPE_PARAM_KEY); + if (writePipeParams.IsEmpty() || writePipeParams.GetStringParam("type") != "FD" || + (writeFd = writePipeParams.GetIntParam("value", -1)) == -1) { + WNMLOG_E("want writeFd is invalid"); + return ConnectNativeRet::WANT_FORMAT_ERROR; + } + fdWrite_ = writeFd; + return ConnectNativeRet::SUCCESS; +} + +void ConnectionNativeRequest::OnConnect() +{ + if (isConnected_) { + return; + } + isConnected_ = true; + if (appConnectCallback_ == nullptr) { + WNMLOG_E("app connect callback is null"); + return; + } + auto callback = iface_cast(appConnectCallback_); + if (!callback) { + WNMLOG_E("cast interface error"); + return; + } + ConnectionNativeInfo info; + info.connectionId = callerConnectionId_; + info.bundleName = targetBundleName_; + info.extensionOrigin = callerExtensionOrigin_; + info.extensionPid = targetExtensionPid_; + callback->OnConnect(info); +} + +void ConnectionNativeRequest::OnDisconnect() +{ + if (!isConnected_) { + return; + } + isConnected_ = false; + if (appConnectCallback_ == nullptr) { + WNMLOG_E("app connect callback is null"); + return; + } + auto callback = iface_cast(appConnectCallback_); + if (!callback) { + WNMLOG_E("cast interface error"); + return; + } + + ConnectionNativeInfo info; + info.connectionId = callerConnectionId_; + info.bundleName = targetBundleName_; + info.extensionOrigin = callerExtensionOrigin_; + info.extensionPid = targetExtensionPid_; + callback->OnDisconnect(info); +} + +void ConnectionNativeRequest::OnFailed(int32_t errorNum) +{ + if (appConnectCallback_ == nullptr) { + WNMLOG_E("app connect callback is null"); + return; + } + auto callback = iface_cast(appConnectCallback_); + if (!callback) { + WNMLOG_E("cast interface error"); + return; + } + callback->OnFailed(errorNum); +} + +std::string ConnectionNativeRequest::ToString() +{ + return "callerConnectionId:" + std::to_string(callerConnectionId_) + + ";innerConnectionId:" + std::to_string(innerConnectionId_) + + ";callerPid:" + std::to_string(callerPid_) + + ";callerTokenId:" + std::to_string(callerTokenId_) + + ";callerBundleName:" + callerBundleName_ + + ";callerExtensionOrigin:" + callerExtensionOrigin_ + + ";targetBundleName:" + targetBundleName_ + + ";targetAbilityName:" + targetAbilityName_ + + ";targetExtensionPid:" + std::to_string(targetExtensionPid_) + + ";fdRead:" + std::to_string(fdRead_) + + ";fdWrite:" + std::to_string(fdWrite_) + + ";isConnected:" + std::to_string(isConnected_); +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/connect_native_request.h b/sa/web_native_messaging/service/connect_native_request.h new file mode 100644 index 0000000000000000000000000000000000000000..7a5565f33373e69d2c272200913f14c27b9a624b --- /dev/null +++ b/sa/web_native_messaging/service/connect_native_request.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef WEB_NWEB_CONNECT_NATIVE_REQUEST_H +#define WEB_NWEB_CONNECT_NATIVE_REQUEST_H + +#include +#include + +#include "accesstoken_kit.h" +#include "refbase.h" +#include "want.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +class InnerConnectIdMapKey { +public: + InnerConnectIdMapKey(Security::AccessToken::AccessTokenID id, int32_t pid, int32_t connectId) : + callerTokenId(id), callerPid(pid), callerConnectId(connectId) {} + bool operator<(const InnerConnectIdMapKey& other) const { + if (this->callerTokenId != other.callerTokenId) { + return this->callerTokenId < other.callerTokenId; + } + if (this->callerPid != other.callerPid) { + return this->callerPid < other.callerPid; + } + return this->callerConnectId < other.callerConnectId; + } + + Security::AccessToken::AccessTokenID callerTokenId; + int32_t callerPid; + int32_t callerConnectId; +}; +using InnerConnectionIdMapType = std::map; + +class ConnectionNativeRequest { +public: + ConnectionNativeRequest() {} + ~ConnectionNativeRequest(); + + static void InsertRequestMap(std::shared_ptr request); + static void RemoveRequestMap(int32_t id); + static std::shared_ptr GetExistConnectId(int32_t id); + static int32_t GetAliveSize(); + + static int32_t GetInnerConnectionIdFromMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId); + static void InsertInnerConnectionIdMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId, int32_t innerId); + static void RemoveInnerConnectionIdMap(Security::AccessToken::AccessTokenID tokenId, + int32_t pid, int32_t connectionId); + static void DumpAllRequest(int fd); + static void DumpInnerIdMap(int fd); + + ConnectNativeRet FillRequestWithWant(const AAFwk::Want& want); + + void OnConnect(); + void OnDisconnect(); + void OnFailed(int32_t errorNum); + + std::string ToString(); + + void SetCallerConnectionId(int32_t connectionId) + { + callerConnectionId_ = connectionId; + } + + int32_t GetCallerConnectionId() + { + return callerConnectionId_; + } + + void SetInnerConnectionId(int32_t connectionId) + { + innerConnectionId_ = connectionId; + } + + int32_t GetInnerConnectionId() + { + return innerConnectionId_; + } + + class RequestClientDeathRecipient : public IRemoteObject::DeathRecipient { + public: + using RemoteDiedHandler = std::function; + explicit RequestClientDeathRecipient(RemoteDiedHandler handler) : handler_(handler) {} + ~RequestClientDeathRecipient() {} + void OnRemoteDied(const wptr& remote) override + { + if (handler_) { + handler_(); + } + } + private: + RemoteDiedHandler handler_; + }; + + void SetAppConnectCallback(sptr callback) + { + appConnectCallback_ = callback; + } + + void AddDeathRecipientCallback(RequestClientDeathRecipient::RemoteDiedHandler handler) + { + if (!appConnectCallback_) { + WNMLOG_W("appConnectCallback_ is null, set death recipient failed."); + return; + } + deathRecipient_ = new (std::nothrow) RequestClientDeathRecipient(handler); + if (deathRecipient_) { + appConnectCallback_->AddDeathRecipient(deathRecipient_); + } + } + + void SetCallerTokenId(Security::AccessToken::AccessTokenID tokenId) + { + callerTokenId_ = tokenId; + } + + Security::AccessToken::AccessTokenID GetCallerTokenId() + { + return callerTokenId_; + } + + void SetCallerBundleName(std::string bundleName) + { + callerBundleName_ = bundleName; + } + + std::string& GetCallerBundleName() + { + return callerBundleName_; + } + + void SetCallerPid(int32_t pid) + { + callerPid_ = pid; + } + + int32_t GetCallerPid() + { + return callerPid_; + } + + void SetExtensionPid(int32_t pid) + { + targetExtensionPid_ = pid; + } + + int32_t GetExtensionPid() + { + return targetExtensionPid_; + } + + std::string& GetTargetBundleName() + { + return targetBundleName_; + } + + std::string& GetTargetAbilityName() + { + return targetAbilityName_; + } + + std::string& GetCallerExtensionOrigin() + { + return callerExtensionOrigin_; + } + + int32_t GetFdRead() + { + return fdRead_; + } + + int32_t GetFdWrite() + { + return fdWrite_; + } + + void ResetAllFileDescriptors() + { + if (fdRead_ >= 0) { + close(fdRead_); + fdRead_ = -1; + } + + if (fdWrite_ >= 0) { + close(fdWrite_); + fdWrite_ = -1; + } + } + + bool IsConnected() + { + return isConnected_; + } +private: + int32_t callerConnectionId_; + int32_t innerConnectionId_; + int32_t callerPid_; + Security::AccessToken::AccessTokenID callerTokenId_; + std::string callerBundleName_; + std::string callerExtensionOrigin_; + std::string targetBundleName_; + std::string targetAbilityName_; + int32_t targetExtensionPid_ = -1; + int32_t fdRead_; + int32_t fdWrite_; + sptr appConnectCallback_; + bool isConnected_ = false; + sptr deathRecipient_; + + static std::mutex connectIdMutex_; + static std::unordered_map> connectIdMap_; + static std::mutex InnerIdMapMutex_; + static InnerConnectionIdMapType InnerIdMap_; +}; +} // namespace OHOS::NWeb +#endif // WEB_NWEB_CONNECT_NATIVE_REQUEST_H diff --git a/sa/web_native_messaging/service/extension_ipc_connection.cpp b/sa/web_native_messaging/service/extension_ipc_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e0d2b5a6edb6d951e40f389ca2d1a287137800f --- /dev/null +++ b/sa/web_native_messaging/service/extension_ipc_connection.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "extension_ipc_connection.h" + +#include "ability_manager_client.h" +#include "connect_native_request.h" +#include "i_web_extension_connection_callback.h" +#include "iweb_native_messaging_extension.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +namespace { +static const std::string REQUEST_ON_CONNECT = "RequestOnConnect"; +static const std::string REQUEST_ON_DISCONNECT = "RequestOnDisconnect"; +static const std::string REQUEST_ON_FAILED = "RequestOnFailed"; +} + +void ExtensionIpcConnection::PostRequestOnConnect(std::shared_ptr request) +{ + if (!eventHandler_) { + WNMLOG_W("event handler is null"); + return; + } + eventHandler_->ProxyPostTask([requestTmp = request] () { + if (requestTmp) { + requestTmp->OnConnect(); + } + }, REQUEST_ON_CONNECT, 0); +} + +void ExtensionIpcConnection::PostRequestOnDisconnect(std::shared_ptr request) +{ + if (!eventHandler_) { + WNMLOG_W("event handler is null"); + return; + } + eventHandler_->ProxyPostTask([requestTmp = request] () { + if (requestTmp) { + requestTmp->OnDisconnect(); + } + }, REQUEST_ON_DISCONNECT, 0); +} + +void ExtensionIpcConnection::PostRequestOnFailed(std::shared_ptr request, int32_t errorNum) +{ + if (!eventHandler_) { + WNMLOG_W("event handler is null"); + return; + } + eventHandler_->ProxyPostTask([requestTmp = request, errorNum] () { + if (requestTmp) { + requestTmp->OnFailed(errorNum); + } + }, REQUEST_ON_FAILED, 0); +} + +static void FillWNMEConnectionInfo(WNMEConnectionInfo& info, ConnectionNativeRequest& request) +{ + info.connectionId = request.GetInnerConnectionId(); + info.bundleName = request.GetCallerBundleName(); + info.extensionOrigin = request.GetCallerExtensionOrigin(); + info.fdRead = request.GetFdRead(); + info.fdWrite = request.GetFdWrite(); +} + +int32_t ExtensionIpcConnection::DoConnectNative(std::shared_ptr request) +{ + if (!request) { + WNMLOG_E("DoConnectNative request is null"); + return -1; + } + + auto connectNativeExtension = iface_cast(extensionIpcRemote_); + if (!connectNativeExtension) { + WNMLOG_E("DoConnectNative cast interface error"); + return -1; + } + + WNMEConnectionInfo info; + FillWNMEConnectionInfo(info, *request); + + // send request to extension side + int32_t pid = connectNativeExtension->ConnectNative(info); + if (pid < 0) { + WNMLOG_E("call extension onConnectNative failed"); + return -1; + } + targetExtensionPid_ = pid; + request->SetExtensionPid(targetExtensionPid_); + + // close fd in service + request->ResetAllFileDescriptors(); + + // notify caller callback + PostRequestOnConnect(request); + return 0; +} + +int32_t ExtensionIpcConnection::DoDisconnectNative(std::shared_ptr request) +{ + if (!request) { + WNMLOG_E("DoDisconnectNative request is null"); + return -1; + } + auto connectNativeExtension = iface_cast(extensionIpcRemote_); + if (!connectNativeExtension) { + WNMLOG_E("DoDisconnectNative cast interface error"); + return -1; + } + WNMEConnectionInfo info; + FillWNMEConnectionInfo(info, *request); + connectNativeExtension->DisconnectNative(info); + PostRequestOnDisconnect(request); + return 0; +} + +void ExtensionIpcConnection::HandleCallerDeath() +{ + WNMLOG_I("handle caller death"); + std::vector> popDeleteList; + { + std::lock_guard lock(mutex_); + if (status_ == IpcConnectStatus::CONNECTED) { + popDeleteList.swap(connectedRequests_); + } + status_ = IpcConnectStatus::DISCONNECTED; + } + for (auto& request : popDeleteList) { + DoDisconnectNative(request); + } + + auto sp = wpThis_.promote(); + if (sp) { + ErrCode err = AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(sp); + if (err != ERR_OK) { + WNMLOG_E("call ability disconnect ability failed, err %{public}d", err); + } + } + + auto spManager = wpManager_.lock(); + if (spManager != nullptr) { + spManager->CleanAbilityConnection(callerTokenId_, targetBundleName_); + } +} + +void ExtensionIpcConnection::OnAbilityConnectDone(const AppExecFwk::ElementName& element, + const sptr& remoteObject, int resultCode) +{ + WNMLOG_I("ability connect done"); + std::lock_guard lock(mutex_); + if (status_ != IpcConnectStatus::CONNECTING) { + WNMLOG_W("connection is not connecting"); + return; + } + status_ = IpcConnectStatus::CONNECTED; + extensionIpcRemote_ = remoteObject; + + // send pending request + auto iter = pendingRequests_.begin(); + while (iter != pendingRequests_.end()) { + std::shared_ptr request = *iter; + if (!request) { + WNMLOG_W("pending request is null"); + iter = pendingRequests_.erase(iter); + continue; + } + + if (resultCode == ERR_OK) { + if (DoConnectNative(request) == 0) { + connectedRequests_.emplace_back(request); + } else { + PostRequestOnFailed(request, ConnectNativeRet::ABILITY_CONNECTION_ERROR); + } + } else { + PostRequestOnFailed(request, resultCode); + } + iter = pendingRequests_.erase(iter); + } +} + +void ExtensionIpcConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName& element, + int resultCode) +{ + WNMLOG_I("ability disconnect done"); + std::vector> popList; + { + std::lock_guard lock(mutex_); + status_ = IpcConnectStatus::DISCONNECTED; + popList.insert(popList.end(), pendingRequests_.begin(), pendingRequests_.end()); + popList.insert(popList.end(), connectedRequests_.begin(), connectedRequests_.end()); + pendingRequests_.clear(); + connectedRequests_.clear(); + } + for (auto& request : popList) { + PostRequestOnDisconnect(request); + } + + auto spManager = wpManager_.lock(); + if (spManager != nullptr) { + spManager->CleanAbilityConnection(callerTokenId_, targetBundleName_); + } +} + +int32_t ExtensionIpcConnection::ConnectNative(std::shared_ptr request) +{ + std::lock_guard lock(mutex_); + if (status_ == IpcConnectStatus::DISCONNECTED) { + WNMLOG_W("ipc connection status is disconnected"); + return ConnectNativeRet::CONNECT_STATUS_ERROR; + } + if (status_ == IpcConnectStatus::INIT) { + status_ = IpcConnectStatus::CONNECTING; + pendingRequests_.emplace_back(request); + AAFwk::Want want; + want.SetElementName(targetBundleName_, targetAbilityName_); + auto spThis = wpThis_.promote(); + auto abilityClient = AAFwk::AbilityManagerClient::GetInstance(); + if (!abilityClient) { + WNMLOG_E("ability manager is null"); + RemovePendingRequestUnlock(request); + return ConnectNativeRet::ABILITY_CONNECTION_ERROR; + } + ErrCode err = abilityClient->ConnectAbilityWithExtensionType(want, spThis, token_, -1, + AppExecFwk::ExtensionAbilityType::WEB_NATIVE_MESSAGING); + if (err != ERR_OK) { + WNMLOG_E("call ability manager connect extension failed, err %{public}d", err); + RemovePendingRequestUnlock(request); + status_ = IpcConnectStatus::INIT; + return err; + } + } else if (status_ == IpcConnectStatus::CONNECTING) { + pendingRequests_.emplace_back(request); + } else if (status_ == IpcConnectStatus::CONNECTED) { + if (DoConnectNative(request) != 0) { + return ConnectNativeRet::ABILITY_CONNECTION_ERROR; + } + connectedRequests_.emplace_back(request); + } + + request->AddDeathRecipientCallback([wp = wpThis_] () { + auto ipcConnect = wp.promote(); + if (ipcConnect) { + ipcConnect->HandleCallerDeath(); + } + }); + return ConnectNativeRet::SUCCESS; +} + +std::shared_ptr ExtensionIpcConnection::LookForAndRemoveRequest(int32_t connectionId) +{ + std::lock_guard lock(mutex_); + std::shared_ptr result = nullptr; + auto iter = std::find_if(pendingRequests_.begin(), pendingRequests_.end(), + [connectionId] (std::shared_ptr request) { + return (request && request->GetInnerConnectionId() == connectionId); + }); + if (iter != pendingRequests_.end()) { + result = *iter; + pendingRequests_.erase(iter); + return result; + } + iter = std::find_if(connectedRequests_.begin(), connectedRequests_.end(), + [connectionId] (std::shared_ptr request) { + return (request && request->GetInnerConnectionId() == connectionId); + }); + if (iter != connectedRequests_.end()) { + result = *iter; + connectedRequests_.erase(iter); + return result; + } + return nullptr; +} + +void ExtensionIpcConnection::RemovePendingRequestUnlock(std::shared_ptr request) +{ + for (auto iter = pendingRequests_.begin(); iter != pendingRequests_.end(); iter++) { + if (*iter == request) { + pendingRequests_.erase(iter); + return; + } + } + return; +} + +bool ExtensionIpcConnection::IsRequestListEmpty() +{ + std::lock_guard lock(mutex_); + return (pendingRequests_.size() == 0) && (connectedRequests_.size() == 0); +} + +int32_t ExtensionIpcConnection::DisconnectNative(int32_t connectionId, bool& resDeleted) +{ + std::shared_ptr deleteReq = LookForAndRemoveRequest(connectionId); + if (!deleteReq) { + WNMLOG_E("can not find disconnect connectId %{public}d", connectionId); + return ConnectNativeRet::CONNECTION_NOT_EXIST; + } + if (deleteReq->IsConnected()) { + if (DoDisconnectNative(deleteReq)) { + WNMLOG_E("connectId %{public}d send extension disconnect failed", connectionId); + } + } + if (IsRequestListEmpty()) { + WNMLOG_I("need disconnect extension ability."); + auto sp = wpThis_.promote(); + if (!sp) { + return ConnectNativeRet::MEMORY_ERROR; + } + auto abilityClient = AAFwk::AbilityManagerClient::GetInstance(); + if (!abilityClient) { + WNMLOG_E("ability manager is null"); + return ConnectNativeRet::ABILITY_CONNECTION_ERROR; + } + ErrCode err = abilityClient->DisconnectAbility(sp); + if (err != ERR_OK) { + WNMLOG_E("call ability manager disconnect ability failed, err %{public}d", err); + return err; + } + resDeleted = true; + } + return ConnectNativeRet::SUCCESS; +} + +void ExtensionIpcConnection::DumpMesg(int fd) +{ + dprintf(fd, " callerTokenId:%d;targetBundleName:%s;targetAbilityName:%s;targetExtensionPid:%d;status:%d\n", + callerTokenId_, targetBundleName_.c_str(), targetAbilityName_.c_str(), targetExtensionPid_, status_); + std::lock_guard lock(mutex_); + dprintf(fd, " pendingList:\n"); + for (auto& request : pendingRequests_) { + if (request) { + dprintf(fd, " request:%s\n", request->ToString().c_str()); + } + } + dprintf(fd, " connectedList:\n"); + for (auto& request : connectedRequests_) { + if (request) { + dprintf(fd, " request:%s\n", request->ToString().c_str()); + } + } +} + +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/extension_ipc_connection.h b/sa/web_native_messaging/service/extension_ipc_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..2b3e7db6093e780c2f41552060de0158dfca8fbc --- /dev/null +++ b/sa/web_native_messaging/service/extension_ipc_connection.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef WEB_NWEB_EXTENSION_IPC_CONNECTION_H +#define WEB_NWEB_EXTENSION_IPC_CONNECTION_H + +#include +#include + +#include "ability_connection.h" +#include "accesstoken_kit.h" +#include "connect_native_request.h" +#include "element_name.h" +#include "i_web_native_messaging_manager.h" +#include "iremote_object.h" +#include "refbase.h" +#include "service_event_handler.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +class ExtensionIpcConnection : public AbilityRuntime::AbilityConnection { +public: + ExtensionIpcConnection(Security::AccessToken::AccessTokenID callerTokenId, + std::string targetBundleName, std::string targetAbilityName, sptr token, + std::shared_ptr eventHandler) : + callerTokenId_(callerTokenId), targetBundleName_(targetBundleName), + targetAbilityName_(targetAbilityName), token_(token), eventHandler_(eventHandler) {} + + ~ExtensionIpcConnection() + { + WNMLOG_D("~ExtensionIpcConnection"); + } + + void OnAbilityConnectDone( + const AppExecFwk::ElementName& element, const sptr& remoteObject, int resultCode) override; + void OnAbilityDisconnectDone(const AppExecFwk::ElementName& element, int resultCode) override; + + int32_t ConnectNative(std::shared_ptr info); + int32_t DisconnectNative(int32_t connectionId, bool& resDeleted); + + void PostRequestOnConnect(std::shared_ptr request); + void PostRequestOnDisconnect(std::shared_ptr request); + void PostRequestOnFailed(std::shared_ptr request, int32_t errorNum); + + void HandleCallerDeath(); + + bool IsRequestListEmpty(); + + void DumpMesg(int fd); + + void SetThisWptr(wptr wp) + { + wpThis_ = wp; + } + + void SetManagerWptr(std::shared_ptr wp) + { + wpManager_ = wp; + } + + std::string& GetTargetBundleName() + { + return targetBundleName_; + } + + std::string& GetTargetAbilityName() + { + return targetAbilityName_; + } + + int32_t GetTargetExtensionPid() + { + return targetExtensionPid_; + } + + Security::AccessToken::AccessTokenID GetCallerTokenId() + { + return callerTokenId_; + } + +private: + int32_t DoConnectNative(std::shared_ptr request); + int32_t DoDisconnectNative(std::shared_ptr request); + + std::shared_ptr LookForAndRemoveRequest(int32_t connectionId); + void RemovePendingRequestUnlock(std::shared_ptr request); + + enum IpcConnectStatus : int32_t { + INIT, + CONNECTING, + CONNECTED, + DISCONNECTED, + }; + + Security::AccessToken::AccessTokenID callerTokenId_; + std::string targetBundleName_; + std::string targetAbilityName_; + int32_t targetExtensionPid_; + sptr extensionIpcRemote_; + sptr token_; + IpcConnectStatus status_ = IpcConnectStatus::INIT; + + std::mutex mutex_; + std::vector> pendingRequests_; + std::vector> connectedRequests_; + + wptr wpThis_; + std::weak_ptr wpManager_; + std::shared_ptr eventHandler_; +}; +} // namespace OHOS::NWeb +#endif // WEB_NWEB_EXTENSION_IPC_CONNECTION_H diff --git a/sa/web_native_messaging/service/i_web_native_messaging_manager.h b/sa/web_native_messaging/service/i_web_native_messaging_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..796e5f8da99da6bb6a198ff81dbb322ff55e83f3 --- /dev/null +++ b/sa/web_native_messaging/service/i_web_native_messaging_manager.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef WEB_NWEB_I_WEB_NATIVE_MESSAGING_MANAGER_H +#define WEB_NWEB_I_WEB_NATIVE_MESSAGING_MANAGER_H + +#include +#include "accesstoken_kit.h" + +namespace OHOS::NWeb { +class IWebNativeMessagingManager { +public: + virtual void CleanAbilityConnection(Security::AccessToken::AccessTokenID tokenId, std::string& bundleName) {} +}; +} // namespace OHOS::NWeb + +#endif // WEB_NWEB_I_WEB_NATIVE_MESSAGING_MANAGER_H diff --git a/sa/web_native_messaging/service/service_delay_exit_task.cpp b/sa/web_native_messaging/service/service_delay_exit_task.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dca84760f3178f9487d79b4480038439fb3ce8e --- /dev/null +++ b/sa/web_native_messaging/service/service_delay_exit_task.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "service_delay_exit_task.h" + +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +namespace { +static const std::string DELAY_EXIT_TASK = "DelayExitTask"; +static const int32_t DELAY_EXIT_MILLISECONDS = 30 * 1000; // 30s +} + +ServiceDelayExitTask::ServiceDelayExitTask(const std::shared_ptr& handler, + std::function exitTask) +{ + serviceHandler_ = handler; + exitTask_ = exitTask; +} + +void ServiceDelayExitTask::Start() +{ + if (serviceHandler_ == nullptr) { + WNMLOG_E("serviceHandler_ is null"); + return; + } + WNMLOG_I("Delay exit service after %{public}d ms", DELAY_EXIT_MILLISECONDS); + serviceHandler_->ProxyPostTask([exitTask = exitTask_] () { + if (exitTask) { + exitTask(); + } + }, DELAY_EXIT_TASK, DELAY_EXIT_MILLISECONDS); +} + +void ServiceDelayExitTask::Stop() +{ + if (serviceHandler_ == nullptr) { + WNMLOG_E("serviceHandler_ is null"); + return; + } + WNMLOG_I("service delay exit handler stop"); + serviceHandler_->ProxyRemoveTask(DELAY_EXIT_TASK); +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/service_delay_exit_task.h b/sa/web_native_messaging/service/service_delay_exit_task.h new file mode 100644 index 0000000000000000000000000000000000000000..f85b5745092bfd525fd778014d5af72e11f1c054 --- /dev/null +++ b/sa/web_native_messaging/service/service_delay_exit_task.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_WEB_NATIVE_MESSAGING_SERVICE_DELAY_EXIT_TASK_H +#define NWEB_WEB_NATIVE_MESSAGING_SERVICE_DELAY_EXIT_TASK_H + +#include +#include +#include +#include "nocopyable.h" +#include "service_event_handler.h" + +namespace OHOS::NWeb { +class ServiceDelayExitTask { +public: + ServiceDelayExitTask(const std::shared_ptr& handler, std::function exitTask); + virtual ~ServiceDelayExitTask() = default; + + void Start(); + void Stop(); +private: + std::shared_ptr serviceHandler_; + std::function exitTask_ = [] () { return; }; + + DISALLOW_COPY_AND_MOVE(ServiceDelayExitTask); +}; +} // namespace OHOS::NWeb +#endif // NWEB_WEB_NATIVE_MESSAGING_SERVICE_DELAY_EXIT_TASK_H diff --git a/sa/web_native_messaging/service/service_event_handler.cpp b/sa/web_native_messaging/service/service_event_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a9945fe7fe25e41175d446c5475947cc31fdecf --- /dev/null +++ b/sa/web_native_messaging/service/service_event_handler.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "service_event_handler.h" + +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +ServiceEventHandler::ServiceEventHandler(const std::shared_ptr& runner) + : AppExecFwk::EventHandler(runner) {} + +ServiceEventHandler::~ServiceEventHandler() = default; + +bool ServiceEventHandler::ProxyPostTask( + const Callback& callback, const std::string& name, int64_t delayTime) +{ + return AppExecFwk::EventHandler::PostTask(callback, name, delayTime); +} + +void ServiceEventHandler::ProxyRemoveTask(const std::string& name) +{ + AppExecFwk::EventHandler::RemoveTask(name); +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/service_event_handler.h b/sa/web_native_messaging/service/service_event_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..fc6795761b3dfe371e94aef2067bee2e82c5b0b1 --- /dev/null +++ b/sa/web_native_messaging/service/service_event_handler.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_WEB_NATIVE_MESSAGING_SERVICE_EVENT_HANDLER_H +#define NWEB_WEB_NATIVE_MESSAGING_SERVICE_EVENT_HANDLER_H + +#include +#include "event_handler.h" +#include "event_runner.h" + +namespace OHOS::NWeb { +class ServiceEventHandler : public AppExecFwk::EventHandler { +public: + explicit ServiceEventHandler(const std::shared_ptr& runner); + ~ServiceEventHandler() override; + + bool ProxyPostTask(const Callback& callback, const std::string& name = std::string(), int64_t delayTime = 0); + void ProxyRemoveTask(const std::string& name); +}; +} // namespace OHOS::NWeb +#endif // NWEB_WEB_NATIVE_MESSAGING_SERVICE_EVENT_HANDLER_H diff --git a/sa/web_native_messaging/service/web_extension_connection_callback_proxy.cpp b/sa/web_native_messaging/service/web_extension_connection_callback_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd04d0847cb767b560787dc5bd693521e6eebc6f --- /dev/null +++ b/sa/web_native_messaging/service/web_extension_connection_callback_proxy.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "web_extension_connection_callback_proxy.h" +#include "connection_native_info_parcel.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +WebExtensionConnectionCallbackProxy::WebExtensionConnectionCallbackProxy(const sptr& impl) + : IRemoteProxy(impl) {} + +WebExtensionConnectionCallbackProxy::~WebExtensionConnectionCallbackProxy() {} + +void WebExtensionConnectionCallbackProxy::OnConnect(ConnectionNativeInfo& info) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(IWebExtensionConnectionCallback::GetDescriptor())) { + WNMLOG_E("write descriptor failed"); + return; + } + sptr parcel = new (std::nothrow) ConnectionNativeInfoParcel(); + if (parcel == nullptr) { + WNMLOG_E("new ConnectionNativeInfoParcel failed"); + return; + } + parcel->connectionNativeInfo_ = info; + if (!data.WriteParcelable(parcel)) { + WNMLOG_E("write ConnectionNativeInfoParcel failed"); + return; + } + MessageParcel reply; + MessageOption option(MessageOption::TF_SYNC); + sptr remote = Remote(); + if (remote == nullptr) { + WNMLOG_E("callback is null"); + return; + } + int32_t requestResult = remote->SendRequest( + static_cast(IWebExtensionConnectionCallback::ON_CONNECT_CODE), data, reply, option); + if (requestResult != NO_ERROR) { + WNMLOG_E("send request fail, result: %{public}d", requestResult); + return; + } + WNMLOG_D("SendRequest success"); +} + +void WebExtensionConnectionCallbackProxy::OnDisconnect(ConnectionNativeInfo& info) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(IWebExtensionConnectionCallback::GetDescriptor())) { + WNMLOG_E("write descriptor failed"); + return; + } + sptr parcel = new (std::nothrow) ConnectionNativeInfoParcel(); + if (parcel == nullptr) { + WNMLOG_E("new ConnectionNativeInfoParcel failed"); + return; + } + parcel->connectionNativeInfo_ = info; + if (!data.WriteParcelable(parcel)) { + WNMLOG_E("write ConnectionNativeInfoParcel failed"); + return; + } + MessageParcel reply; + MessageOption option(MessageOption::TF_SYNC); + sptr remote = Remote(); + if (remote == nullptr) { + WNMLOG_E("callback is null"); + return; + } + int32_t requestResult = remote->SendRequest( + static_cast(IWebExtensionConnectionCallback::ON_DISCONNECT_CODE), data, reply, option); + if (requestResult != NO_ERROR) { + WNMLOG_E("send request fail, result: %{public}d", requestResult); + return; + } + WNMLOG_D("SendRequest success"); +} + +void WebExtensionConnectionCallbackProxy::OnFailed(int32_t errorNum) +{ + MessageParcel data; + if (!data.WriteInterfaceToken(IWebExtensionConnectionCallback::GetDescriptor())) { + WNMLOG_E("write descriptor failed"); + return; + } + if (!data.WriteInt32(errorNum)) { + WNMLOG_E("write errorNum failed"); + return; + } + MessageParcel reply; + MessageOption option(MessageOption::TF_SYNC); + sptr remote = Remote(); + if (remote == nullptr) { + WNMLOG_E("callback is null"); + return; + } + int32_t requestResult = remote->SendRequest( + static_cast(IWebExtensionConnectionCallback::ON_FAILED_CODE), data, reply, option); + if (requestResult != NO_ERROR) { + WNMLOG_E("send request fail, result: %{public}d", requestResult); + return; + } + WNMLOG_D("SendRequest success"); +} +} // OHOS::NWeb diff --git a/sa/web_native_messaging/service/web_extension_connection_callback_proxy.h b/sa/web_native_messaging/service/web_extension_connection_callback_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..39047e21e39e82de8307582aa0b7d0f5c6731949 --- /dev/null +++ b/sa/web_native_messaging/service/web_extension_connection_callback_proxy.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_WEB_EXTENSION_CONNECTION_CALLBACK_PROXY_H +#define NWEB_WEB_EXTENSION_CONNECTION_CALLBACK_PROXY_H + +#include "i_web_extension_connection_callback.h" +#include "iremote_proxy.h" +#include "nocopyable.h" + +namespace OHOS::NWeb { +class WebExtensionConnectionCallbackProxy : public IRemoteProxy { +public: + explicit WebExtensionConnectionCallbackProxy(const sptr& impl); + ~WebExtensionConnectionCallbackProxy() override; + + void OnConnect(ConnectionNativeInfo& info) override; + void OnDisconnect(ConnectionNativeInfo& info) override; + void OnFailed(int32_t errorNum) override; +private: + static inline BrokerDelegator delegator_; +}; +} // namespace OHOS::NWeb +#endif // NWEB_WEB_EXTENSION_CONNECTION_CALLBACK_PROXY_H diff --git a/sa/web_native_messaging/service/web_native_messaging_manager.cpp b/sa/web_native_messaging/service/web_native_messaging_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1896a1259664df65108aac044bfc17dde8106c42 --- /dev/null +++ b/sa/web_native_messaging/service/web_native_messaging_manager.cpp @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "web_native_messaging_manager.h" + +#include "web_native_messaging_log.h" +#include "i_web_extension_connection_callback.h" +#include "iservice_registry.h" +#include "ipc_skeleton.h" +#include "accesstoken_kit.h" +#include "connect_native_request.h" +#include +#include "system_ability.h" +#include "web_native_messaging_common.h" +#include "system_ability_definition.h" +#include "ability_manager_client.h" +#include "bundle_info.h" +#include "if_system_ability_manager.h" +#include "bundle_mgr_client.h" +#include "bundlemgr/bundle_mgr_interface.h" + +namespace OHOS::NWeb { +namespace { +const std::string PERMISSION_WEB_NATIVE_MESSAGING = "ohos.permission.WEB_NATIVE_MESSAGING"; +const std::string WANT_EXTENSION_ORIGIN_PARAM_KEY = "ohos.arkweb.extensionOrigin"; +const std::string WANT_READ_PIPE_PARAM_KEY = "ohos.arkweb.messageReadPipe"; +const std::string WANT_WRITE_PIPE_PARAM_KEY = "ohos.arkweb.messageWritePipe"; +static int32_t g_ConnectionIdStart = -1; +static const int32_t MAX_REQUEST_SIZE = 10000; +constexpr int32_t UID_TRANSFORM_DIVISOR = 200000; +constexpr int32_t RANDOM_CONNECT_ID_BASE_RANGE = 1000000; +} // namespace + +static uint32_t GetRandomUint32FromUrandom(void) +{ + uint32_t random; + int32_t fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return 0; + } + ssize_t len = read(fd, &random, sizeof(random)); + close(fd); + if (len != sizeof(random)) { + return 0; + } + return random % RANDOM_CONNECT_ID_BASE_RANGE; +} + +static int32_t CreateUniqueConnectionID() +{ + if (g_ConnectionIdStart == -1) { + g_ConnectionIdStart = GetRandomUint32FromUrandom(); + } else { + if (g_ConnectionIdStart < INT32_MAX) { + g_ConnectionIdStart++; + } else { + g_ConnectionIdStart = 0; + } + } + return g_ConnectionIdStart; +} + +void WebNativeMessagingManager::DeleteIpcConnect(Security::AccessToken::AccessTokenID id, std::string& bundleName) +{ + std::lock_guard lock(AbilityConnectMutex_); + auto iter = AbilityConnectMap_.find(std::pair(id, bundleName)); + if (iter != AbilityConnectMap_.end()) { + AbilityConnectMap_.erase(iter); + } +} + +bool WebNativeMessagingManager::IsIpcConnectExist() +{ + std::lock_guard lock(AbilityConnectMutex_); + return AbilityConnectMap_.size() > 0; +} + +void WebNativeMessagingManager::CleanAbilityConnection(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName) +{ + DeleteIpcConnect(tokenId, bundleName); + if (!IsIpcConnectExist() && delayExitTask_) { + delayExitTask_->Start(); + } +} + +bool WebNativeMessagingManager::GetPidExtensionBundleName(int32_t pid, std::string& bundleName) +{ + if (pid <= 0) { + return false; + } + std::lock_guard lock(AbilityConnectMutex_); + auto iter = AbilityConnectMap_.begin(); + while (iter != AbilityConnectMap_.end()) { + if (iter->second && iter->second->GetTargetExtensionPid() == pid) { + bundleName = iter->second->GetTargetBundleName(); + return true; + } + iter++; + } + return false; +} + +sptr WebNativeMessagingManager::NewIpcConnectionUnlock( + Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token) +{ + sptr ipcConnect = + new (std::nothrow) ExtensionIpcConnection(tokenId, bundleName, abilityName, token, serviceHandler_); + if (ipcConnect == nullptr) { + WNMLOG_E("new ExtensionIpcConnection failed"); + return nullptr; + } + + wptr wpIpcConnect = ipcConnect; + std::shared_ptr weakPtrManager = shared_from_this(); + ipcConnect->SetThisWptr(wpIpcConnect); + ipcConnect->SetManagerWptr(weakPtrManager); + + AbilityConnectMap_.insert(std::pair, + sptr>({tokenId, bundleName}, ipcConnect)); + return ipcConnect; +} + +sptr WebNativeMessagingManager::NewIpcConnection( + Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token) +{ + std::lock_guard lock(AbilityConnectMutex_); + return NewIpcConnectionUnlock(tokenId, bundleName, abilityName, token); +} + +sptr WebNativeMessagingManager::LookUpIpcConnectionUnlock( + Security::AccessToken::AccessTokenID tokenId, std::string& bundleName) +{ + auto iter = AbilityConnectMap_.find( + std::pair(tokenId, bundleName)); + if (iter != AbilityConnectMap_.end()) { + return iter->second; + } + return nullptr; +} + +sptr WebNativeMessagingManager::LookUpIpcConnection( + Security::AccessToken::AccessTokenID tokenId, std::string& bundleName) +{ + std::lock_guard lock(AbilityConnectMutex_); + return LookUpIpcConnectionUnlock(tokenId, bundleName); +} + +sptr WebNativeMessagingManager::LookUpOrNewIpcConnection( + Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token) +{ + std::lock_guard lock(AbilityConnectMutex_); + auto connect = LookUpIpcConnectionUnlock(tokenId, bundleName); + if (connect) { + return connect; + } + return NewIpcConnectionUnlock(tokenId, bundleName, abilityName, token); +} + +static sptr GetBundleMgr() +{ + auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (!saMgr) { + return nullptr; + } + auto bundleMgrObj = saMgr->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID); + if (!bundleMgrObj) { + return nullptr; + } + return iface_cast(bundleMgrObj); +} + +static bool CheckAbilityIsUIAbility(const std::string& bundleName, + const std::string& abilityName, int32_t userId) +{ + if (bundleName.empty() || abilityName.empty() || userId <= 0) { + WNMLOG_E("check ability type: params is error"); + return false; + } + auto bundleMgr = GetBundleMgr(); + if (!bundleMgr) { + WNMLOG_E("check ability type: bundle manager is null"); + return false; + } + AppExecFwk::BundleInfo bundleInfo; + auto ret = bundleMgr->GetBundleInfo(bundleName, AppExecFwk::BundleFlag::GET_BUNDLE_WITH_ABILITIES, + bundleInfo, userId); + if (!ret) { + WNMLOG_E("check ability type: GetBundleInfo fail, error: %{public}d", ret); + return false; + } + for (auto ability : bundleInfo.abilityInfos) { + if (ability.name == abilityName) { + if (ability.type == AppExecFwk::AbilityType::PAGE) { + return true; + } + WNMLOG_E("check ability type: ability is found, but type is not page"); + return false; + } + } + + WNMLOG_E("check ability type: ability is not found"); + return false; +} + +int32_t WebNativeMessagingManager::GetAndCheckConnectParams(const sptr& token, + const sptr& connectionCallback, int32_t connectionId, + ConnectNativeParams& params) +{ + if (token == nullptr) { + WNMLOG_E("Check token failed"); + return ConnectNativeRet::CONTEXT_ERROR; + } + params.token = token; + if (connectionCallback == nullptr) { + WNMLOG_E("Check connectionCallback failed"); + return ConnectNativeRet::CALLBACK_ERROR; + } + params.connectionCallback = connectionCallback; + + params.callerTokenId = IPCSkeleton::GetCallingTokenID(); + params.callerPid = IPCSkeleton::GetCallingPid(); + if (Security::AccessToken::AccessTokenKit::VerifyAccessToken(params.callerTokenId, + PERMISSION_WEB_NATIVE_MESSAGING) == Security::AccessToken::PermissionState::PERMISSION_GRANTED) { + WNMLOG_E("Check permission %{public}s failed", PERMISSION_WEB_NATIVE_MESSAGING.c_str()); + return ConnectNativeRet::PERMISSION_CHECK_ERROR; + } + + Security::AccessToken::HapTokenInfo hapInfo; + if (Security::AccessToken::AccessTokenKit::GetHapTokenInfo(params.callerTokenId, hapInfo) != 0) { + WNMLOG_E("get hap token info failed"); + return ConnectNativeRet::PERMISSION_CHECK_ERROR; + } + params.callerBundleName = hapInfo.bundleName; + + if (ConnectionNativeRequest::GetInnerConnectionIdFromMap( + params.callerTokenId, params.callerPid, connectionId) != -1) { + WNMLOG_E("connectId is exist"); + return ConnectNativeRet::CONNECTION_ID_EXIST; + } + params.callerConnectId = connectionId; + + if (ConnectionNativeRequest::GetAliveSize() > MAX_REQUEST_SIZE) { + WNMLOG_E("connect count is too large"); + return ConnectNativeRet::REQUEST_SIZE_TOO_LARGE; + } + params.innerConnectId = CreateUniqueConnectionID(); + return ConnectNativeRet::SUCCESS; +} + +std::shared_ptr WebNativeMessagingManager::CreateNativeRequest(const AAFwk::Want& want, + ConnectNativeParams& params, int32_t& errorNum) +{ + std::shared_ptr request = std::make_shared(); + if (request == nullptr) { + WNMLOG_E("new connection native request failed"); + errorNum = ConnectNativeRet::MEMORY_ERROR; + return nullptr; + } + + if (request->FillRequestWithWant(want) != ConnectNativeRet::SUCCESS) { + WNMLOG_E("check want format error"); + errorNum = ConnectNativeRet::WANT_FORMAT_ERROR; + return nullptr; + } + + request->SetInnerConnectionId(params.innerConnectId); + request->SetCallerConnectionId(params.callerConnectId); + request->SetCallerPid(params.callerPid); + request->SetAppConnectCallback(params.connectionCallback); + request->SetCallerTokenId(params.callerTokenId); + request->SetCallerBundleName(params.callerBundleName); + + ConnectionNativeRequest::InsertRequestMap(request); + return request; +} + +void WebNativeMessagingManager::ConnectWebNativeMessagingExtension(const sptr& token, + const AAFwk::Want& want, const sptr& connectionCallback, + int32_t connectionId, int32_t& errorNum) +{ + ConnectNativeParams params; + int ret = GetAndCheckConnectParams(token, connectionCallback, connectionId, params); + if (ret != ConnectNativeRet::SUCCESS) { + errorNum = ret; + return; + } + + std::shared_ptr request = CreateNativeRequest(want, params, errorNum); + if (request == nullptr) { + return; + } + + sptr ipcConnect = + LookUpOrNewIpcConnection(params.callerTokenId, request->GetTargetBundleName(), + request->GetTargetAbilityName(), token); + if (!ipcConnect) { + WNMLOG_E("create ipc connect failed"); + errorNum = ConnectNativeRet::MEMORY_ERROR; + return; + } + int32_t res = ipcConnect->ConnectNative(request); + if (res == ConnectNativeRet::CONNECT_STATUS_ERROR) { + // retry to connect it. + DeleteIpcConnect(params.callerTokenId, request->GetTargetBundleName()); + ipcConnect = NewIpcConnection(params.callerTokenId, request->GetTargetBundleName(), + request->GetTargetAbilityName(), token); + if (ipcConnect) { + WNMLOG_E("new ipc connection failed"); + errorNum = ConnectNativeRet::MEMORY_ERROR; + return; + } + if (ipcConnect->ConnectNative(request) != ConnectNativeRet::SUCCESS) { + WNMLOG_E("retry connect native failed"); + if (ipcConnect->IsRequestListEmpty()) { + CleanAbilityConnection(params.callerTokenId, ipcConnect->GetTargetBundleName()); + } + errorNum = res; + return; + } + } else if (res != ConnectNativeRet::SUCCESS) { + WNMLOG_E("connect native failed"); + if (ipcConnect->IsRequestListEmpty()) { + CleanAbilityConnection(params.callerTokenId, ipcConnect->GetTargetBundleName()); + } + errorNum = res; + return; + } + if (delayExitTask_) { + delayExitTask_->Stop(); + } + errorNum = ConnectNativeRet::SUCCESS; +} + +void WebNativeMessagingManager::DisconnectWebNativeMessagingExtension(int32_t connectionId, int32_t& errorNum) +{ + Security::AccessToken::AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID(); + int32_t pid = IPCSkeleton::GetCallingPid(); + if (Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenId, PERMISSION_WEB_NATIVE_MESSAGING) == + Security::AccessToken::PermissionState::PERMISSION_GRANTED) { + WNMLOG_E("Check permission %{public}s failed", PERMISSION_WEB_NATIVE_MESSAGING.c_str()); + errorNum = ConnectNativeRet::PERMISSION_CHECK_ERROR; + return; + } + int32_t innerId = ConnectionNativeRequest::GetInnerConnectionIdFromMap(tokenId, pid, connectionId); + if (innerId == -1) { + WNMLOG_E("Get inner id failed"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + auto request = ConnectionNativeRequest::GetExistConnectId(innerId); + if (!request) { + WNMLOG_E("connectionId is not exist"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + + auto ipcConnect = LookUpIpcConnection(tokenId, request->GetTargetBundleName()); + if (!ipcConnect) { + WNMLOG_E("connection is not connected"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + + bool ipcConnectNeedDelete = false; + int32_t res = ipcConnect->DisconnectNative(innerId, ipcConnectNeedDelete); + if (ipcConnectNeedDelete) { + DeleteIpcConnect(tokenId, request->GetTargetBundleName()); + if (!IsIpcConnectExist() && delayExitTask_) { + delayExitTask_->Start(); + } + } + if (res != ConnectNativeRet::SUCCESS) { + WNMLOG_E("disconnect native failed."); + errorNum = res; + return; + } + errorNum = ConnectNativeRet::SUCCESS; +} + +void WebNativeMessagingManager::StartAbility(const sptr& token, + const AAFwk::Want& want, const AAFwk::StartOptions& startOptions, int32_t& errorNum) +{ + int32_t userId = IPCSkeleton::GetCallingUid() / UID_TRANSFORM_DIVISOR; + int32_t pid = IPCSkeleton::GetCallingPid(); + std::string extensionBundleName; + if (!GetPidExtensionBundleName(pid, extensionBundleName)) { + WNMLOG_E("pid %{public}d is not native messaging extension.", pid); + errorNum = ConnectNativeRet::PERMISSION_CHECK_ERROR; + return; + } + + if (extensionBundleName != want.GetBundle()) { + WNMLOG_E("start ability is not extension app."); + errorNum = ConnectNativeRet::PERMISSION_CHECK_ERROR; + return; + } + + if (!CheckAbilityIsUIAbility(want.GetBundle(), want.GetElement().GetAbilityName(), userId)) { + WNMLOG_E("start ability is not UIAbility."); + errorNum = ConnectNativeRet::PERMISSION_CHECK_ERROR; + return; + } + + auto client = AAFwk::AbilityManagerClient::GetInstance(); + if (client == nullptr) { + WNMLOG_E("client is null."); + errorNum = ConnectNativeRet::IPC_ERROR; + return; + } + ErrCode err = client->StartAbility(want, startOptions, token, userId); + if (err != ERR_OK) { + WNMLOG_E("StartAbility failed: %{public}d.", err); + errorNum = err; + return; + } + errorNum = ConnectNativeRet::SUCCESS; +} + +void WebNativeMessagingManager::StopNativeConnectionFromExtension(int32_t innerConnectId, int32_t& errorNum) +{ + int32_t pid = IPCSkeleton::GetCallingPid(); + auto request = ConnectionNativeRequest::GetExistConnectId(innerConnectId); + if (!request) { + WNMLOG_E("innerConnectId is not exist"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + + if (pid != request->GetExtensionPid()) { + WNMLOG_E("innerConnectId is not belong to this application"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + + auto ipcConnect = LookUpIpcConnection(request->GetCallerTokenId(), request->GetTargetBundleName()); + if (!ipcConnect) { + WNMLOG_E("connection is not connected"); + errorNum = ConnectNativeRet::CONNECTION_NOT_EXIST; + return; + } + + bool ipcConnectNeedDelete = false; + int32_t res = ipcConnect->DisconnectNative(innerConnectId, ipcConnectNeedDelete); + if (ipcConnectNeedDelete) { + DeleteIpcConnect(request->GetCallerTokenId(), request->GetTargetBundleName()); + if (!IsIpcConnectExist() && delayExitTask_) { + delayExitTask_->Start(); + } + } + if (res != ConnectNativeRet::SUCCESS) { + WNMLOG_E("disconnect native failed."); + errorNum = res; + return; + } + errorNum = ConnectNativeRet::SUCCESS; +} + +void WebNativeMessagingManager::ExitSaProcess() +{ + if (IsIpcConnectExist()) { + WNMLOG_E("App using service still exist, not exit sa."); + return; + } + WNMLOG_I("All processes using extension died, start sa exit."); + auto systemAbilityMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityMgr == nullptr) { + WNMLOG_E("Failed to get SystemAbilityManager."); + return; + } + int32_t ret = systemAbilityMgr->UnloadSystemAbility(SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID); + if (ret != 0) { + WNMLOG_E("Failed to UnloadSystemAbility errcode=%{public}d.", ret); + return; + } + WNMLOG_I("UnloadSystemAbility successfully."); +} + +int32_t WebNativeMessagingManager::Dump(int fd, const std::vector& args) +{ + if (fd < 0) { + return ERR_INVALID_VALUE; + } + dprintf(fd, "WebNativeMessagingService Dump:\n"); + std::string arg0 = (args.size() == 0) ? "" : Str16ToStr8(args.at(0)); + if (arg0.compare("-h") == 0) { + dprintf(fd, "Usage:\n"); + dprintf(fd, " -h: command help\n"); + dprintf(fd, " -d: default dump\n"); + } else if (arg0.compare("-d") == 0 || arg0.compare("") == 0) { + { + std::lock_guard lock(AbilityConnectMutex_); + dprintf(fd, "ExtensionAbilityConnection:\n"); + for (auto iter = AbilityConnectMap_.begin(); iter != AbilityConnectMap_.end(); iter++) { + auto info = iter->second; + if (info) { + info->DumpMesg(fd); + } + } + } + dprintf(fd, "AllNativeRequest:\n"); + ConnectionNativeRequest::DumpAllRequest(fd); + dprintf(fd, "IdMap:\n"); + ConnectionNativeRequest::DumpInnerIdMap(fd); + } + return ERR_OK; +} + +bool WebNativeMessagingManager::Init() +{ + runner_ = AppExecFwk::EventRunner::Create(true, AppExecFwk::ThreadMode::FFRT); + if (!runner_) { + WNMLOG_E("failed to create a runner"); + return false; + } + serviceHandler_ = std::make_shared(runner_); + if (!serviceHandler_) { + WNMLOG_E("failed to create service handler."); + return false; + } + delayExitTask_ = std::make_shared(serviceHandler_, [this]() { + this->ExitSaProcess(); + }); + if (!delayExitTask_) { + WNMLOG_E("failed to create a delay exit task"); + return false; + } + delayExitTask_->Start(); + WNMLOG_I("WebNativeMessagingManager init success."); + return true; +} + +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/web_native_messaging_manager.h b/sa/web_native_messaging/service/web_native_messaging_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..09dc0f2ac675ed35accb6f9e19ed90da10a71bd0 --- /dev/null +++ b/sa/web_native_messaging/service/web_native_messaging_manager.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_WEB_NATIVE_MESSAGING_MANAGER_H +#define NWEB_WEB_NATIVE_MESSAGING_MANAGER_H + +#include +#include +#include + +#include "connect_native_request.h" +#include "event_handler.h" +#include "extension_ipc_connection.h" +#include "i_web_native_messaging_manager.h" +#include "refbase.h" +#include "service_delay_exit_task.h" +#include "start_options.h" +#include "web_native_messaging_common.h" + +namespace OHOS::NWeb { + +using AbilityCallbackMapType = + std::map, sptr>; + +struct ConnectNativeParams { + Security::AccessToken::AccessTokenID callerTokenId; + int32_t callerPid; + int32_t callerConnectId; + int32_t innerConnectId; + std::string callerBundleName; + sptr connectionCallback; + sptr token; +}; + +class WebNativeMessagingManager : public IWebNativeMessagingManager, + public std::enable_shared_from_this { +public: + WebNativeMessagingManager() = default; + virtual ~WebNativeMessagingManager() = default; + + void ConnectWebNativeMessagingExtension(const sptr& token, + const AAFwk::Want& want, const sptr& connectionCallback, + int32_t connectionId, int32_t& errorNum); + void DisconnectWebNativeMessagingExtension(int32_t connectionId, int32_t& errorNum); + void StartAbility(const sptr& token, + const AAFwk::Want& want, const AAFwk::StartOptions& startOptions, int32_t& errorNum); + void StopNativeConnectionFromExtension(int32_t innerConnectId, int32_t& errorNum); + + void CleanAbilityConnection(Security::AccessToken::AccessTokenID tokenId, std::string& bundleName) override; + + void DeleteIpcConnect(Security::AccessToken::AccessTokenID id, std::string& bundleName); + void ExitSaProcess(); + bool Init(); + + int32_t Dump(int fd, const std::vector& args); + +private: + bool IsIpcConnectExist(); + sptr LookUpIpcConnectionUnlock(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName); + sptr LookUpIpcConnection(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName); + sptr NewIpcConnectionUnlock(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token); + sptr NewIpcConnection(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token); + sptr LookUpOrNewIpcConnection(Security::AccessToken::AccessTokenID tokenId, + std::string& bundleName, std::string& abilityName, sptr token); + + bool GetPidExtensionBundleName(int32_t pid, std::string& bundleName); + int32_t GetAndCheckConnectParams(const sptr& token, + const sptr& connectionCallback, int32_t connectionId, + ConnectNativeParams& param); + std::shared_ptr CreateNativeRequest(const AAFwk::Want& want, + ConnectNativeParams& params, int32_t& errorNum); + + std::shared_ptr serviceHandler_; + std::shared_ptr runner_; + std::shared_ptr delayExitTask_; + + std::mutex AbilityConnectMutex_; + AbilityCallbackMapType AbilityConnectMap_; +}; +} // namespace OHOS::NWeb +#endif // NWEB_WEB_NATIVE_MESSAGING_MANAGER_H diff --git a/sa/web_native_messaging/service/web_native_messaging_service.cpp b/sa/web_native_messaging/service/web_native_messaging_service.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ef08f9b6353903d521e0d30f5559d62fa618410 --- /dev/null +++ b/sa/web_native_messaging/service/web_native_messaging_service.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "web_native_messaging_service.h" + +#include "ipc_skeleton.h" +#include "iremote_object.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "sysversion.h" +#include "web_native_messaging_common.h" +#include "web_native_messaging_log.h" + +namespace OHOS::NWeb { +namespace { +REGISTER_SYSTEM_ABILITY_BY_ID(WebNativeMessagingService, SUBSYS_WEBVIEW_NATIVE_MESSAGING_SERVICE_ID, true); +} // namespace + +WebNativeMessagingService::WebNativeMessagingService(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate) {} + +WebNativeMessagingService::~WebNativeMessagingService() {} + +ErrCode WebNativeMessagingService::ConnectWebNativeMessagingExtension(const sptr& token, + const AAFwk::Want& want, const sptr& connectionCallback, + int32_t connectionId, int32_t& errorNum) +{ + if (!manager_) { + errorNum = ConnectNativeRet::SERVICE_INIT_ERROR; + return ERR_OK; + } + manager_->ConnectWebNativeMessagingExtension(token, want, connectionCallback, connectionId, errorNum); + return ERR_OK; +} + +ErrCode WebNativeMessagingService::DisconnectWebNativeMessagingExtension(int32_t connectionId, + int32_t& errorNum) +{ + if (!manager_) { + errorNum = ConnectNativeRet::SERVICE_INIT_ERROR; + return ERR_OK; + } + manager_->DisconnectWebNativeMessagingExtension(connectionId, errorNum); + return ERR_OK; +} + +ErrCode WebNativeMessagingService::StartAbility(const sptr& token, + const AAFwk::Want& want, const AAFwk::StartOptions& startOptions, int32_t& errorNum) +{ + if (!manager_) { + errorNum = ConnectNativeRet::SERVICE_INIT_ERROR; + return ERR_OK; + } + manager_->StartAbility(token, want, startOptions, errorNum); + return ERR_OK; +} + +ErrCode WebNativeMessagingService::StopNativeConnectionFromExtension(int32_t connectionId, int32_t& errorNum) +{ + if (!manager_) { + errorNum = ConnectNativeRet::SERVICE_INIT_ERROR; + return ERR_OK; + } + manager_->StopNativeConnectionFromExtension(connectionId, errorNum); + return ERR_OK; +} + +int32_t WebNativeMessagingService::Dump(int fd, const std::vector& args) +{ + if (!manager_) { + return ERR_INVALID_VALUE; + } + return manager_->Dump(fd, args); +} + +void WebNativeMessagingService::OnStart(const SystemAbilityOnDemandReason& startReason) +{ + WNMLOG_I("Service on start."); + if (registerToService_) { + WNMLOG_I("Web native messaging service is running."); + } + if (!Init(startReason)) { + return; + } +} + +bool WebNativeMessagingService::Init(const SystemAbilityOnDemandReason& startReason) +{ + std::string reasonName = startReason.GetName(); + std::string reasonValue = startReason.GetValue(); + WNMLOG_I("WebNativeMessagingService reasonName: %{public}s", reasonName.c_str()); + + manager_ = std::make_shared(); + if (!manager_) { + WNMLOG_I("create manager failed"); + return false; + } + manager_->Init(); + + bool ret = Publish(this); + if (!ret) { + WNMLOG_I("Service publish failed."); + return false; + } + + registerToService_ = true; + WNMLOG_I("Service init success."); + return true; +} + +void WebNativeMessagingService::OnStop() +{ + WNMLOG_I("Ready to stop service."); + registerToService_ = false; +} +} // namespace OHOS::NWeb diff --git a/sa/web_native_messaging/service/web_native_messaging_service.h b/sa/web_native_messaging/service/web_native_messaging_service.h new file mode 100644 index 0000000000000000000000000000000000000000..5338502ee5b97ea42ef57ee98e149e446bd66e48 --- /dev/null +++ b/sa/web_native_messaging/service/web_native_messaging_service.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef NWEB_WEB_NATIVE_MESSAGING_SERVICE_H +#define NWEB_WEB_NATIVE_MESSAGING_SERVICE_H + +#include + +#include "refbase.h" +#include "system_ability.h" +#include "web_native_messaging_manager.h" +#include "web_native_messaging_service_stub.h" + +namespace OHOS::NWeb { +class WebNativeMessagingService : public SystemAbility, public WebNativeMessagingServiceStub { + DECLARE_SYSTEM_ABILITY(WebNativeMessagingService); + +public: + WebNativeMessagingService(int32_t saId, bool runOnCreate); + ~WebNativeMessagingService(); + + ErrCode ConnectWebNativeMessagingExtension(const sptr& token, + const AAFwk::Want& want, const sptr& connectionCallback, + int32_t connectionId, int32_t& errorNum) override; + ErrCode DisconnectWebNativeMessagingExtension(int32_t connectionId, int32_t& errorNum) override; + ErrCode StartAbility(const sptr& token, + const AAFwk::Want& want, const AAFwk::StartOptions& startOptions, int32_t& errorNum) override; + ErrCode StopNativeConnectionFromExtension(int32_t connectionId, int32_t& errorNum) override; + int32_t Dump(int fd, const std::vector& args) override; + +protected: + void OnStart(const SystemAbilityOnDemandReason& startReason) override; + void OnStop() override; + +private: + bool Init(const SystemAbilityOnDemandReason& startReason); + bool registerToService_ = false; + + std::shared_ptr manager_; +}; +} // namespace OHOS::NWeb +#endif // NWEB_WEB_NATIVE_MESSAGING_SERVICE_H diff --git a/sa/web_native_messaging/web_native_messaging_service.cfg b/sa/web_native_messaging/web_native_messaging_service.cfg new file mode 100644 index 0000000000000000000000000000000000000000..7fe8801c7d8384e95c7a494d78e3fa9609114411 --- /dev/null +++ b/sa/web_native_messaging/web_native_messaging_service.cfg @@ -0,0 +1,16 @@ +{ + "services" : [{ + "name" : "web_native_messaging_service", + "path" : ["/system/bin/sa_main", "/system/profile/web_native_messaging_service.json"], + "ondemand" : true, + "uid" : "web_native_messaging_service", + "gid" : ["web_native_messaging_service"], + "permission" : [ + "ohos.permission.START_ABILITIES_FROM_BACKGROUND", + "ohos.permission.GET_BUNDLE_INFO" + ], + "apl" : "system_basic", + "secon" : "u:r:web_native_messaging_service:s0" + } + ] +} diff --git a/test/unittest/app_fwk_update_client_test/BUILD.gn b/test/unittest/app_fwk_update_client_test/BUILD.gn index 790433b92f629587454b9f0d36570510c6c41bc4..56c6c9d2afebd54a6e57cce220a9cc3af8c0f551 100644 --- a/test/unittest/app_fwk_update_client_test/BUILD.gn +++ b/test/unittest/app_fwk_update_client_test/BUILD.gn @@ -31,17 +31,17 @@ ohos_unittest("app_fwk_update_clent_test") { module_out_path = module_output_path sources = [ "app_fwk_update_client_test.cpp" ] sources += [ - "${target_gen_dir}/../../../sa/app_fwk_update_service_proxy.cpp", - "${target_gen_dir}/../../../sa/app_fwk_update_service_stub.cpp", - "${webview_path}/sa/src/app_fwk_update_client.cpp", - "${webview_path}/sa/src/app_fwk_update_load_callback.cpp", - "${webview_path}/sa/src/app_fwk_update_service.cpp", + "${target_gen_dir}/../../../sa/app_fwk_update/app_fwk_update_service_proxy.cpp", + "${target_gen_dir}/../../../sa/app_fwk_update/app_fwk_update_service_stub.cpp", + "${webview_path}/sa/app_fwk_update/src/app_fwk_update_client.cpp", + "${webview_path}/sa/app_fwk_update/src/app_fwk_update_load_callback.cpp", + "${webview_path}/sa/app_fwk_update/src/app_fwk_update_service.cpp", ] configs = [ ":module_private_config" ] deps = [ - "${webview_path}/sa:app_fwk_update_service", - "${webview_path}/sa:app_fwk_update_service_interface", + "${webview_path}/sa/app_fwk_update:app_fwk_update_service", + "${webview_path}/sa/app_fwk_update:app_fwk_update_service_interface", ] external_deps = [