diff --git a/frameworks/native/runtime/ets_runtime.cpp b/frameworks/native/runtime/ets_runtime.cpp index f32bb200bb..431177bfe9 100644 --- a/frameworks/native/runtime/ets_runtime.cpp +++ b/frameworks/native/runtime/ets_runtime.cpp @@ -27,6 +27,7 @@ #include "ets_interface.h" #include "file_path_utils.h" #include "hilog_tag_wrapper.h" +#include "hybrid_js_module_reader.h" #ifdef SUPPORT_SCREEN #include "ace_forward_compatibility.h" @@ -228,7 +229,6 @@ std::unique_ptr ETSRuntime::Create(const Options &options, std::uniq return std::unique_ptr(); } EntryPathManager::GetInstance().Init(); - return instance; } @@ -272,6 +272,12 @@ bool ETSRuntime::Initialize(const Options &options, std::unique_ptr & return false; } + if (jsRuntime_ != nullptr) { + auto vm = static_cast(jsRuntime_.get())->GetEcmaVm(); + panda::JSNApi::SetHostResolveBufferTrackerForHybridApp( + vm, HybridJsModuleReader(options.bundleName, options.hapPath, options.isUnique)); + } + apiTargetVersion_ = options.apiTargetVersion; TAG_LOGD(AAFwkTag::ETSRUNTIME, "Initialize: %{public}d", apiTargetVersion_); return true; diff --git a/frameworks/native/runtime/hybrid_js_module_reader.cpp b/frameworks/native/runtime/hybrid_js_module_reader.cpp new file mode 100644 index 0000000000..f1bc295596 --- /dev/null +++ b/frameworks/native/runtime/hybrid_js_module_reader.cpp @@ -0,0 +1,273 @@ +/* + * 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 "hybrid_js_module_reader.h" + +#include +#include "bundle_info.h" +#include "bundle_mgr_helper.h" +#include "bundle_mgr_proxy.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "iservice_registry.h" +#include "js_runtime_utils.h" +#include "singleton.h" + +using namespace OHOS::AbilityBase; + +namespace OHOS { +namespace AbilityRuntime { +bool HybridJsModuleReader::needFindPluginHsp_ = true; + +HybridJsModuleReader::HybridJsModuleReader(const std::string& bundleName, const std::string& hapPath, bool isFormRender) + : JsModuleSearcher(bundleName), isFormRender_(isFormRender) +{ + if (!hapPath.empty() && hapPath.find(std::string(ABS_DATA_CODE_PATH)) != 0) { + isSystemPath_ = true; + } else { + isSystemPath_ = false; + } +} + +std::shared_ptr HybridJsModuleReader::GetExtractor( + const std::string& inputPath, std::string& errorMsg) const +{ + auto realHapPath = GetAppPath(inputPath, SHARED_FILE_SUFFIX); + if (realHapPath.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "empty realHapPath"); + return nullptr; + } + if (needFindPluginHsp_) { + realHapPath = GetPluginHspPath(inputPath); + if (realHapPath.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "empty realHapPath"); + return nullptr; + } + needFindPluginHsp_ = true; + } + bool newCreate = false; + std::shared_ptr extractor = ExtractorUtil::GetExtractor(realHapPath, newCreate); + if (extractor != nullptr) { + return extractor; + } + + realHapPath = GetAppPath(inputPath, ABILITY_FILE_SUFFIX); + if (realHapPath.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "empty realHapPath"); + return nullptr; + } + extractor = ExtractorUtil::GetExtractor(realHapPath, newCreate); + if (extractor == nullptr) { + errorMsg = "hap path error: " + inputPath; + TAG_LOGE(AAFwkTag::JSRUNTIME, "inputPath %{private}s GetExtractor failed", inputPath.c_str()); + return nullptr; + } + return extractor; +} + +bool HybridJsModuleReader::operator()(const std::string& inputPath, + uint8_t **buff, size_t *buffSize, std::string& errorMsg) const +{ + TAG_LOGD(AAFwkTag::JSRUNTIME, "called start: %{private}s", inputPath.c_str()); + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (inputPath.empty() || buff == nullptr || buffSize == nullptr) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "Invalid param"); + return false; + } + + std::shared_ptr extractor = GetExtractor(inputPath, errorMsg); + if (extractor == nullptr) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "failed to get extractor %{private}s", inputPath.c_str()); + return false; + } + + auto data = extractor->GetSafeData(MERGE_ABC_PATH); + if (!data) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "null data"); + return false; + } + + *buff = data->GetDataPtr(); + *buffSize = data->GetDataLen(); + return true; +} + +std::string HybridJsModuleReader::GetPluginHspPath(const std::string& inputPath) const +{ + std::string presetAppHapPath = ""; + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "null bundleMgrHelper"); + return presetAppHapPath; + } + std::string moduleName = inputPath.substr(inputPath.find_last_of("/") + 1); + std::string tmpPath = inputPath.substr(inputPath.find_first_of("/") + 1); + const std::string sharedBundleName = tmpPath.substr(0, tmpPath.find_first_of("/")); + TAG_LOGI(AAFwkTag::JSRUNTIME, "moduleName: %{public}s, sharedBundleName: %{public}s", + moduleName.c_str(), sharedBundleName.c_str()); + if (moduleName.empty() || sharedBundleName.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "empty moduleName"); + return presetAppHapPath; + } + + std::vector pluginBundleInfos; + if (bundleMgrHelper->GetPluginInfosForSelf(pluginBundleInfos) != ERR_OK) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "GetPluginInfosForSelf failed"); + return presetAppHapPath; + } + + for (auto &pluginBundleInfo : pluginBundleInfos) { + for (auto &pluginModuleInfo : pluginBundleInfo.pluginModuleInfos) { + if (moduleName == pluginModuleInfo.moduleName + && sharedBundleName == pluginBundleInfo.pluginBundleName) { + presetAppHapPath = pluginModuleInfo.hapPath; + TAG_LOGD(AAFwkTag::JSRUNTIME, "presetAppHapPath %{public}s", presetAppHapPath.c_str()); + std::regex pattern(std::string(ABS_DATA_CODE_PATH) + bundleName_ + "/"); + presetAppHapPath = std::regex_replace( + presetAppHapPath, pattern, std::string(ABS_CODE_PATH) + std::string(BUNDLE)); + TAG_LOGD(AAFwkTag::JSRUNTIME, "presetAppHapPath %{public}s", presetAppHapPath.c_str()); + return presetAppHapPath; + } + } + } + TAG_LOGE(AAFwkTag::JSRUNTIME, "GetPluginHspPath failed"); + return presetAppHapPath; +} + +std::string HybridJsModuleReader::GetAppPath(const std::string& inputPath, const std::string& suffix) const +{ + if (isFormRender_) { + return GetFormAppPath(inputPath, suffix); + } + return GetCommonAppPath(inputPath, suffix); +} + +std::string HybridJsModuleReader::GetFormAppPath(const std::string& inputPath, const std::string& suffix) const +{ + std::string realHapPath; + realHapPath.append("/data/bundles/") + .append(bundleName_).append("/") + .append(GetModuleName(inputPath)) + .append(SHARED_FILE_SUFFIX); + + TAG_LOGI(AAFwkTag::JSRUNTIME, "realHapPath: %{private}s", realHapPath.c_str()); + if (realHapPath.empty() || + realHapPath.length() < suffix.length() || + realHapPath.compare(realHapPath.length() - suffix.length(), suffix.length(), suffix) != 0) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "obtain realHapPath failed"); + return realHapPath; + } + return realHapPath; +} + +std::string HybridJsModuleReader::GetModuleName(const std::string& inputPath) const +{ + return inputPath.substr(inputPath.find_last_of("/") + 1); +} + +std::string HybridJsModuleReader::GetCommonAppPath(const std::string& inputPath, const std::string& suffix) const +{ + std::string realHapPath = GetPresetAppHapPath(inputPath, bundleName_); + if ((realHapPath.find(ABS_DATA_CODE_PATH) == 0) || (realHapPath == inputPath)) { + realHapPath = std::string(ABS_CODE_PATH) + inputPath + suffix; + } + + TAG_LOGD(AAFwkTag::JSRUNTIME, "realHapPath: %{private}s", realHapPath.c_str()); + if (realHapPath.empty() || + realHapPath.length() < suffix.length() || + realHapPath.compare(realHapPath.length() - suffix.length(), suffix.length(), suffix) != 0) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "obtain realHapPath failed"); + return realHapPath; + } + return realHapPath; +} + +std::string HybridJsModuleReader::GetOtherHspPath(const std::string& bundleName, const std::string& moduleName, + const std::string& inputPath) +{ + std::string presetAppHapPath = inputPath; + + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "null bundleMgrHelper"); + return presetAppHapPath; + } + + std::vector baseSharedBundleInfos; + if (bundleMgrHelper->GetBaseSharedBundleInfos(bundleName, baseSharedBundleInfos) != 0) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "GetBaseSharedBundleInfos failed"); + return presetAppHapPath; + } + std::string tmpPath = inputPath.substr(inputPath.find_first_of("/") + 1); + const std::string sharedBundleName = tmpPath.substr(0, tmpPath.find_first_of("/")); + for (const auto &info : baseSharedBundleInfos) { + if ((info.bundleName == sharedBundleName) && (info.moduleName == moduleName)) { + presetAppHapPath = info.hapPath; + needFindPluginHsp_ = false; + break; + } + } + AppExecFwk::BundleInfo bundleInfo; + int32_t ret = bundleMgrHelper->GetDependentBundleInfo(sharedBundleName, bundleInfo, + AppExecFwk::GetDependentBundleInfoFlag::GET_APP_SERVICE_HSP_BUNDLE_INFO); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "GetDependentBundleInfo failed"); + return presetAppHapPath; + } + for (const auto &info : bundleInfo.hapModuleInfos) { + if (info.moduleName == moduleName) { + presetAppHapPath = info.hapPath; + needFindPluginHsp_ = false; + break; + } + } + return presetAppHapPath; +} + +std::string HybridJsModuleReader::GetPresetAppHapPath(const std::string& inputPath, const std::string& bundleName) +{ + std::string presetAppHapPath = inputPath; + std::string moduleName = inputPath.substr(inputPath.find_last_of("/") + 1); + if (moduleName.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "empty moduleName"); + return presetAppHapPath; + } + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "null bundleMgrHelper"); + return presetAppHapPath; + } + if (inputPath.find_first_of("/") == inputPath.find_last_of("/")) { + AppExecFwk::BundleInfo bundleInfo; + auto getInfoResult = bundleMgrHelper->GetBundleInfoForSelf(static_cast(AppExecFwk::GetBundleInfoFlag:: + GET_BUNDLE_INFO_WITH_HAP_MODULE), bundleInfo); + if (getInfoResult != 0 || bundleInfo.hapModuleInfos.empty()) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "GetBundleInfoForSelf failed"); + return presetAppHapPath; + } + for (auto hapModuleInfo : bundleInfo.hapModuleInfos) { + if (hapModuleInfo.moduleName == moduleName) { + presetAppHapPath = hapModuleInfo.hapPath; + needFindPluginHsp_ = false; + break; + } + } + } else { + presetAppHapPath = GetOtherHspPath(bundleName, moduleName, presetAppHapPath); + } + return presetAppHapPath; +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/runtime/hybrid_js_module_reader.h b/frameworks/native/runtime/hybrid_js_module_reader.h new file mode 100644 index 0000000000..0267dd4264 --- /dev/null +++ b/frameworks/native/runtime/hybrid_js_module_reader.h @@ -0,0 +1,66 @@ +/* + * 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_ABILITY_RUNTIME_HYBRID_JS_MODULE_READER_H +#define OHOS_ABILITY_RUNTIME_HYBRID_JS_MODULE_READER_H + +#include +#include + +#include "js_module_searcher.h" +#include "extractor.h" + +using Extractor = OHOS::AbilityBase::Extractor; + +namespace OHOS { +namespace AbilityRuntime { +class HybridJsModuleReader final : private JsModuleSearcher { +public: + static constexpr char ABS_CODE_PATH[] = "/data/storage/el1/"; + static constexpr char ABS_DATA_CODE_PATH[] = "/data/app/el1/bundle/public/"; + static constexpr char BUNDLE[] = "bundle/"; + static constexpr char MERGE_ABC_PATH[] = "ets/modules.abc"; + static constexpr char SYS_ABS_CODE_PATH[] = "/system/app/appServiceFwk/"; + static constexpr char SHARED_FILE_SUFFIX[] = ".hsp"; + static constexpr char ABILITY_FILE_SUFFIX[] = ".hap"; + HybridJsModuleReader(const std::string& bundleName, const std::string& hapPath, bool isFormRender = false); + ~HybridJsModuleReader() = default; + + HybridJsModuleReader(const HybridJsModuleReader&) = default; + HybridJsModuleReader(HybridJsModuleReader&&) = default; + HybridJsModuleReader& operator=(const HybridJsModuleReader&) = default; + HybridJsModuleReader& operator=(HybridJsModuleReader&&) = default; + + bool operator()(const std::string& inputPath, uint8_t **buff, size_t *buffSize, std::string& errorMsg) const; + static std::string GetPresetAppHapPath(const std::string& inputPath, const std::string& bundleName); + +private: + std::string GetAppPath(const std::string& inputPath, const std::string& suffix) const; + std::string GetCommonAppPath(const std::string& inputPath, const std::string& suffix) const; + std::string GetFormAppPath(const std::string& inputPath, const std::string& suffix) const; + std::string GetModuleName(const std::string& inputPath) const; + std::string GetPluginHspPath(const std::string& inputPath) const; + std::shared_ptr GetExtractor(const std::string& inputPath, std::string& errorMsg) const; + static std::string GetOtherHspPath(const std::string& bundleName, const std::string& moduleName, + const std::string& inputPath); + + bool isSystemPath_ = false; + bool isFormRender_ = false; + static bool needFindPluginHsp_; +}; +} // namespace AbilityRuntime +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_HYBRID_JS_MODULE_READER_H diff --git a/interfaces/inner_api/runtime/BUILD.gn b/interfaces/inner_api/runtime/BUILD.gn index 5fe4bec9b9..b86ef2879d 100644 --- a/interfaces/inner_api/runtime/BUILD.gn +++ b/interfaces/inner_api/runtime/BUILD.gn @@ -66,6 +66,7 @@ ohos_shared_library("runtime") { "${ability_runtime_native_path}/runtime/ets_data_struct_converter.cpp", "${ability_runtime_native_path}/runtime/ets_runtime.cpp", "${ability_runtime_native_path}/runtime/hdc_register.cpp", + "${ability_runtime_native_path}/runtime/hybrid_js_module_reader.cpp", "${ability_runtime_native_path}/runtime/js_app_process_state.cpp", "${ability_runtime_native_path}/runtime/js_data_struct_converter.cpp", "${ability_runtime_native_path}/runtime/js_error_utils.cpp", diff --git a/test/unittest/runtime_test/BUILD.gn b/test/unittest/runtime_test/BUILD.gn index 04ea956914..99d92adbb8 100644 --- a/test/unittest/runtime_test/BUILD.gn +++ b/test/unittest/runtime_test/BUILD.gn @@ -65,6 +65,31 @@ ohos_unittest("runtime_test") { ] } +ohos_unittest("hybrid_js_module_reader_test") { + module_out_path = module_output_path + + include_dirs = [ + "${ability_runtime_native_path}/runtime", + "${ability_runtime_native_path}/runtime/utils/include", + ] + + sources = [ + # add mock file + "hybrid_js_module_reader_test.cpp", + ] + + configs = [ "${ability_runtime_services_path}/abilitymgr:abilityms_config" ] + deps = [] + + external_deps = [ + "ability_base:extractortool", + "ability_runtime:runtime", + "c_utils:utils", + "zlib:libz", + "zlib:shared_libz", + ] +} + ohos_unittest("js_runtime_test") { module_out_path = module_output_path sanitize = { @@ -289,6 +314,7 @@ group("unittest") { deps = [ ":hdc_register_test", ":ets_runtime_test", + ":hybrid_js_module_reader_test", ":js_runtime_first_test", ":js_runtime_test", ":js_worker_test", diff --git a/test/unittest/runtime_test/hybrid_js_module_reader_test.cpp b/test/unittest/runtime_test/hybrid_js_module_reader_test.cpp new file mode 100644 index 0000000000..1bbefa014c --- /dev/null +++ b/test/unittest/runtime_test/hybrid_js_module_reader_test.cpp @@ -0,0 +1,104 @@ +/* + * 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 + +#include "hybrid_js_module_reader.h" +#include "extractor.h" + +using namespace testing; +using namespace testing::ext; +using HybridJsModuleReader = OHOS::AbilityRuntime::HybridJsModuleReader; +using Extractor = OHOS::AbilityBase::Extractor; + +namespace OHOS { +namespace AAFwk { +class HybridJsModuleReaderTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; +}; + +void HybridJsModuleReaderTest::SetUpTestCase() +{} + +void HybridJsModuleReaderTest::TearDownTestCase() +{} + +void HybridJsModuleReaderTest::SetUp() +{} + +void HybridJsModuleReaderTest::TearDown() +{} + +/** + * @tc.name: HybridJsModuleReaderTest_0100 + * @tc.desc: HybridJsModuleReaderTest Test + * @tc.type: FUNC + * @tc.require: issueI581SE + */ +HWTEST_F(HybridJsModuleReaderTest, HybridJsModuleReaderTest_0100, TestSize.Level0) +{ + HybridJsModuleReader HybridJsModuleReader("HybridJsModuleReader", ""); + uint8_t *buff = nullptr; + size_t buffSize = 0; + std::string errorMsg = ""; + auto result = HybridJsModuleReader("", &buff, &buffSize, errorMsg); + EXPECT_EQ(result, false); +} + +/** + * @tc.name: HybridJsModuleReaderTest_0200 + * @tc.desc: HybridJsModuleReaderTest Test + * @tc.type: FUNC + * @tc.require: issueI581RO + */ +HWTEST_F(HybridJsModuleReaderTest, HybridJsModuleReaderTest_0200, TestSize.Level0) +{ + HybridJsModuleReader HybridJsModuleReader("HybridJsModuleReader", ""); + uint8_t *buff = nullptr; + size_t buffSize = 0; + std::string errorMsg = ""; + auto result = HybridJsModuleReader("bundleName/moduleName", &buff, &buffSize, errorMsg); + EXPECT_EQ(result, false); +} + +/** + * @tc.name: GetPresetAppHapPathTest_0100 + * @tc.desc: GetPresetAppHapPath Test + * @tc.type: FUNC + */ +HWTEST_F(HybridJsModuleReaderTest, GetPresetAppHapPathTest_0100, TestSize.Level0) +{ + HybridJsModuleReader HybridJsModuleReader("HybridJsModuleReader", ""); + std::string hapPath = HybridJsModuleReader.GetPresetAppHapPath("", ""); + EXPECT_TRUE(hapPath.empty()); +} + +/** + * @tc.name: GetPresetAppHapPathTest_0200 + * @tc.desc: GetPresetAppHapPath Test + * @tc.type: FUNC + */ +HWTEST_F(HybridJsModuleReaderTest, GetPresetAppHapPathTest_0200, TestSize.Level0) +{ + HybridJsModuleReader HybridJsModuleReader("HybridJsModuleReader", "/data/storage/el1/test.hsp"); + std::string hapPath = HybridJsModuleReader.GetPresetAppHapPath("", ""); + EXPECT_TRUE(hapPath.empty()); +} +} // namespace AAFwk +} // namespace OHOS