• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ets_runtime.h"
17 
18 #include <cstddef>
19 #include <dlfcn.h>
20 #include <filesystem>
21 #include <fstream>
22 #include <nlohmann/json.hpp>
23 #include <regex>
24 #include <unistd.h>
25 
26 #include "constants.h"
27 #include "ets_interface.h"
28 #include "file_path_utils.h"
29 #include "hilog_tag_wrapper.h"
30 #include "hybrid_js_module_reader.h"
31 #include "nocopyable.h"
32 #include "static_core/plugins/ets/runtime/ets_namespace_manager.h"
33 
34 #ifdef SUPPORT_SCREEN
35 #include "ace_forward_compatibility.h"
36 #include "declarative_module_preloader.h"
37 #include "hot_reloader.h"
38 #endif //SUPPORT_SCREEN
39 
40 using namespace OHOS::AbilityBase;
41 using Extractor = OHOS::AbilityBase::Extractor;
42 
43 namespace OHOS {
44 namespace AbilityRuntime {
45 namespace {
46 #ifdef APP_USE_ARM64
47 const std::string SANDBOX_LIB_PATH = "/system/lib64";
48 const std::string ETS_RT_PATH = SANDBOX_LIB_PATH;
49 const std::string ETS_SYSLIB_PATH =
50     "/system/lib64:/system/lib64/platformsdk:/system/lib64/module:/system/lib64/ndk";
51 #else
52 const std::string SANDBOX_LIB_PATH = "/system/lib";
53 const std::string ETS_RT_PATH = SANDBOX_LIB_PATH;
54 const std::string ETS_SYSLIB_PATH =
55     "/system/lib:/system/lib/platformsdk:/system/lib/module:/system/lib/ndk";
56 #endif
57 constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/";
58 constexpr char SANDBOX_ARK_CACHE_PATH[] = "/data/storage/ark-cache/";
59 constexpr char MERGE_ABC_PATH[] = "/ets/modules_static.abc";
60 
61 const char *ETS_ENV_LIBNAME = "libets_environment.z.so";
62 const char *ETS_ENV_REGISTER_FUNCS = "OHOS_ETS_ENV_RegisterFuncs";
63 ETSEnvFuncs *g_etsEnvFuncs = nullptr;
64 
RegisterETSEnvFuncs()65 bool RegisterETSEnvFuncs()
66 {
67     if (g_etsEnvFuncs != nullptr) {
68         return true;
69     }
70     auto handle = dlopen(ETS_ENV_LIBNAME, RTLD_LAZY);
71     if (!handle) {
72         TAG_LOGE(AAFwkTag::ETSRUNTIME, "dlopen failed %{public}s, %{public}s", ETS_ENV_LIBNAME, dlerror());
73         return false;
74     }
75     auto symbol = dlsym(handle, ETS_ENV_REGISTER_FUNCS);
76     if (!symbol) {
77         TAG_LOGE(AAFwkTag::ETSRUNTIME, "dlsym failed %{public}s, %{public}s", ETS_ENV_REGISTER_FUNCS, dlerror());
78         dlclose(handle);
79         return false;
80     }
81     auto func = reinterpret_cast<ETSEnvFuncs* (*)()>(symbol);
82     g_etsEnvFuncs = func();
83     return true;
84 }
85 
86 class EtsAppLibNamespaceMgr : public std::enable_shared_from_this<EtsAppLibNamespaceMgr>, public NoCopyable {
87 public:
EtsAppLibNamespaceMgr(const AppLibPathMap & appLibPaths,bool isSystemApp)88     EtsAppLibNamespaceMgr(const AppLibPathMap& appLibPaths, bool isSystemApp)
89         : isSystemApp_(isSystemApp), appLibPathMap_(appLibPaths)
90     {
91     }
92 
CreateNamespace(const std::string & bundleModuleName,std::string & nsName)93     bool CreateNamespace(const std::string& bundleModuleName, std::string &nsName)
94     {
95         TAG_LOGD(AAFwkTag::ETSRUNTIME, "Create app ns: %{public}s", bundleModuleName.c_str());
96         if (bundleModuleName.empty()) {
97             TAG_LOGE(AAFwkTag::ETSRUNTIME, "empty bundleModuleName");
98             return false;
99         }
100         auto appLibPath = appLibPathMap_.find(bundleModuleName);
101         if (appLibPath == appLibPathMap_.end()) {
102             TAG_LOGE(AAFwkTag::ETSRUNTIME, "not found app lib path: %{public}s", bundleModuleName.c_str());
103             return false;
104         }
105 
106         auto moduleManager = NativeModuleManager::GetInstance();
107         if (moduleManager == nullptr) {
108             TAG_LOGE(AAFwkTag::ETSRUNTIME, "null moduleManager");
109             return false;
110         }
111         moduleManager->SetAppLibPath(appLibPath->first, appLibPath->second, isSystemApp_);
112         return moduleManager->GetLdNamespaceName(appLibPath->first, nsName);
113     }
114 
115 private:
116     bool isSystemApp_ = false;
117     AppLibPathMap appLibPathMap_;
118 };
119 std::shared_ptr<EtsAppLibNamespaceMgr> g_etsAppLibNamespaceMgr;
120 } // namespace
121 
PreFork(const Options & options,std::unique_ptr<JsRuntime> & jsRuntime)122 std::unique_ptr<ETSRuntime> ETSRuntime::PreFork(const Options &options, std::unique_ptr<JsRuntime> &jsRuntime)
123 {
124     TAG_LOGD(AAFwkTag::ETSRUNTIME, "PreFork begin");
125     std::unique_ptr<ETSRuntime> instance = std::make_unique<ETSRuntime>();
126 
127     if (!instance->Initialize(options, jsRuntime)) {
128         return std::unique_ptr<ETSRuntime>();
129     }
130     return instance;
131 }
132 
PostFork(const Options & options,std::unique_ptr<JsRuntime> & jsRuntime)133 bool ETSRuntime::PostFork(const Options &options, std::unique_ptr<JsRuntime> &jsRuntime)
134 {
135     TAG_LOGD(AAFwkTag::ETSRUNTIME, "PostFork begin");
136     codePath_ = options.codePath;
137 
138     if (g_etsEnvFuncs == nullptr ||
139         g_etsEnvFuncs->PostFork == nullptr) {
140         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or PostFork");
141         return false;
142     }
143 
144     if (jsRuntime != nullptr) {
145         jsRuntime_ = std::move(jsRuntime);
146     }
147     if (jsRuntime_ != nullptr) {
148         auto vm = static_cast<JsRuntime *>(jsRuntime_.get())->GetEcmaVm();
149         panda::JSNApi::SetHostResolveBufferTrackerForHybridApp(
150             vm, HybridJsModuleReader(options.bundleName, options.hapPath, options.isUnique));
151     }
152 
153     std::string aotFilePath = "";
154     if (!options.arkNativeFilePath.empty()) {
155         aotFilePath = SANDBOX_ARK_CACHE_PATH + options.arkNativeFilePath + options.moduleName + ".an";
156     }
157     napi_env napiEnv = static_cast<AbilityRuntime::JsRuntime *>(jsRuntime_.get())->GetNapiEnv();
158     g_etsEnvFuncs->PostFork(reinterpret_cast<void *>(napiEnv), aotFilePath);
159     return true;
160 }
161 
Create(const Options & options,std::unique_ptr<JsRuntime> & jsRuntime)162 std::unique_ptr<ETSRuntime> ETSRuntime::Create(const Options &options, std::unique_ptr<JsRuntime> &jsRuntime)
163 {
164     TAG_LOGD(AAFwkTag::ETSRUNTIME, "Create called");
165     if (!RegisterETSEnvFuncs()) {
166         TAG_LOGE(AAFwkTag::ETSRUNTIME, "RegisterETSEnvFuncs failed");
167         return std::unique_ptr<ETSRuntime>();
168     }
169 
170     if (g_etsEnvFuncs == nullptr) {
171         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs");
172         return std::unique_ptr<ETSRuntime>();
173     }
174 
175     if (jsRuntime == nullptr) {
176         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null jsRuntime");
177         return std::unique_ptr<ETSRuntime>();
178     }
179     std::unique_ptr<ETSRuntime> instance;
180     auto preloadedInstance = Runtime::GetPreloaded(Language::ETS);
181 #ifdef SUPPORT_SCREEN
182     // reload ace if compatible mode changes
183     if (Ace::AceForwardCompatibility::PipelineChanged() && preloadedInstance) {
184         preloadedInstance.reset();
185     }
186 #endif
187     if (preloadedInstance && preloadedInstance->GetLanguage() == Runtime::Language::ETS) {
188         instance.reset(static_cast<ETSRuntime *>(preloadedInstance.release()));
189     } else {
190         instance = PreFork(options, jsRuntime);
191     }
192 
193     if (instance != nullptr && !options.preload) {
194         instance->PostFork(options, jsRuntime);
195     }
196     return instance;
197 }
198 
SetAppLibPath(const AppLibPathMap & appLibPaths,const std::map<std::string,std::string> & abcPathsToBundleModuleNameMap,bool isSystemApp)199 void ETSRuntime::SetAppLibPath(const AppLibPathMap& appLibPaths,
200     const std::map<std::string, std::string>& abcPathsToBundleModuleNameMap, bool isSystemApp)
201 {
202     TAG_LOGD(AAFwkTag::ETSRUNTIME, "SetAppLibPath called");
203     if (!RegisterETSEnvFuncs()) {
204         TAG_LOGE(AAFwkTag::ETSRUNTIME, "RegisterETSEnvFuncs failed");
205         return;
206     }
207 
208     if (g_etsEnvFuncs == nullptr) {
209         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs");
210         return;
211     }
212 
213     if (g_etsEnvFuncs->InitETSSDKNS == nullptr) {
214         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null InitETSSDKNS");
215         return;
216     }
217     g_etsEnvFuncs->InitETSSDKNS(ETS_RT_PATH);
218 
219     if (g_etsEnvFuncs->InitETSSysNS == nullptr) {
220         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null InitETSSysNS");
221         return;
222     }
223     g_etsEnvFuncs->InitETSSysNS(ETS_SYSLIB_PATH);
224 
225     if (g_etsEnvFuncs->SetAppLibPath == nullptr) {
226         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null SetAppLibPath");
227         return;
228     }
229     g_etsAppLibNamespaceMgr = std::make_shared<EtsAppLibNamespaceMgr>(appLibPaths, isSystemApp);
230     std::function<bool(const std::string &bundleModuleName, std::string &namespaceName)> cb =
231         [weak = std::weak_ptr(g_etsAppLibNamespaceMgr)](const std::string &bundleModuleName, std::string &nsName) {
232         auto appLibNamespaceMgr = weak.lock();
233         if (appLibNamespaceMgr == nullptr) {
234             TAG_LOGE(AAFwkTag::ETSRUNTIME, "null appLibNamespaceMgr");
235             return false;
236         }
237         return appLibNamespaceMgr->CreateNamespace(bundleModuleName, nsName);
238     };
239     g_etsEnvFuncs->SetAppLibPath(abcPathsToBundleModuleNameMap, cb);
240 }
241 
SetExtensionApiCheckCallback(const std::function<bool (const std::string & className,const std::string & fileName)> & cb)242 void ETSRuntime::SetExtensionApiCheckCallback(
243     const std::function<bool(const std::string &className, const std::string &fileName)> &cb)
244 {
245     TAG_LOGD(AAFwkTag::ETSRUNTIME, "called");
246     ark::ets::EtsNamespaceManager::SetExtensionApiCheckCallback(std::move(cb));
247 }
248 
Initialize(const Options & options,std::unique_ptr<JsRuntime> & jsRuntime)249 bool ETSRuntime::Initialize(const Options &options, std::unique_ptr<JsRuntime> &jsRuntime)
250 {
251     TAG_LOGD(AAFwkTag::ETSRUNTIME, "Initialize called");
252     if (options.lang != GetLanguage()) {
253         TAG_LOGE(AAFwkTag::ETSRUNTIME, "language mismatch");
254         return false;
255     }
256 
257     if (jsRuntime != nullptr) {
258         jsRuntime_ = std::move(jsRuntime);
259     }
260     if (!CreateEtsEnv(options)) {
261         TAG_LOGE(AAFwkTag::ETSRUNTIME, "CreateEtsEnv failed");
262         return false;
263     }
264 
265     apiTargetVersion_ = options.apiTargetVersion;
266     TAG_LOGD(AAFwkTag::ETSRUNTIME, "Initialize: %{public}d", apiTargetVersion_);
267     return true;
268 }
269 
FinishPreload()270 void ETSRuntime::FinishPreload()
271 {
272     if (jsRuntime_ != nullptr) {
273         jsRuntime_->FinishPreload();
274     }
275 
276     if (g_etsEnvFuncs == nullptr ||
277         g_etsEnvFuncs->FinishPreload == nullptr) {
278         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or FinishPreload");
279         return;
280     }
281     g_etsEnvFuncs->FinishPreload();
282 }
283 
RegisterUncaughtExceptionHandler(const EtsEnv::ETSUncaughtExceptionInfo & uncaughtExceptionInfo)284 void ETSRuntime::RegisterUncaughtExceptionHandler(const EtsEnv::ETSUncaughtExceptionInfo &uncaughtExceptionInfo)
285 {
286     if (g_etsEnvFuncs == nullptr ||
287         g_etsEnvFuncs->RegisterUncaughtExceptionHandler == nullptr) {
288         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or RegisterUncaughtExceptionHandler");
289         return;
290     }
291     g_etsEnvFuncs->RegisterUncaughtExceptionHandler(uncaughtExceptionInfo);
292 }
293 
~ETSRuntime()294 ETSRuntime::~ETSRuntime()
295 {
296     TAG_LOGD(AAFwkTag::ETSRUNTIME, "~ETSRuntime called");
297     Deinitialize();
298 }
299 
Deinitialize()300 void ETSRuntime::Deinitialize()
301 {
302     TAG_LOGD(AAFwkTag::ETSRUNTIME, "Deinitialize called");
303 }
304 
CreateEtsEnv(const Options & options)305 bool ETSRuntime::CreateEtsEnv(const Options &options)
306 {
307     TAG_LOGD(AAFwkTag::ETSRUNTIME, "CreateEtsEnv called");
308     if (g_etsEnvFuncs == nullptr ||
309         g_etsEnvFuncs->Initialize == nullptr) {
310         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or Initialize");
311         return false;
312     }
313 
314     if (!g_etsEnvFuncs->Initialize()) {
315         TAG_LOGE(AAFwkTag::ETSRUNTIME, "Initialize failed");
316         return false;
317     }
318     return true;
319 }
320 
GetAniEnv()321 ani_env *ETSRuntime::GetAniEnv()
322 {
323     if (g_etsEnvFuncs == nullptr ||
324         g_etsEnvFuncs->GetAniEnv == nullptr) {
325         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or GetAniEnv");
326         return nullptr;
327     }
328     return g_etsEnvFuncs->GetAniEnv();
329 }
330 
PreloadModule(const std::string & moduleName,const std::string & hapPath,bool isEsMode,bool useCommonTrunk)331 void ETSRuntime::PreloadModule(const std::string &moduleName, const std::string &hapPath,
332     bool isEsMode, bool useCommonTrunk)
333 {
334     TAG_LOGD(AAFwkTag::ETSRUNTIME, "moduleName: %{public}s", moduleName.c_str());
335     if (g_etsEnvFuncs == nullptr ||
336         g_etsEnvFuncs->PreloadModule == nullptr) {
337         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or PreloadModule");
338         return;
339     }
340 
341     std::string modulePath = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_PATH;
342     if (!g_etsEnvFuncs->PreloadModule(modulePath)) {
343         TAG_LOGE(AAFwkTag::ETSRUNTIME, "PreloadModule failed");
344     }
345     return;
346 }
347 
LoadModule(const std::string & moduleName,const std::string & modulePath,const std::string & hapPath,bool esmodule,bool useCommonChunk,const std::string & srcEntrance)348 std::unique_ptr<AppExecFwk::ETSNativeReference> ETSRuntime::LoadModule(const std::string &moduleName,
349     const std::string &modulePath, const std::string &hapPath, bool esmodule, bool useCommonChunk,
350     const std::string &srcEntrance)
351 {
352     TAG_LOGD(AAFwkTag::ETSRUNTIME, "LoadModule(%{public}s, %{public}s, %{public}s, %{public}s)",
353         moduleName.c_str(), modulePath.c_str(), hapPath.c_str(), srcEntrance.c_str());
354 
355     std::string path = moduleName;
356     auto pos = path.find("::");
357     if (pos != std::string::npos) {
358         path.erase(pos, path.size() - pos);
359         moduleName_ = path;
360     }
361     TAG_LOGD(AAFwkTag::ETSRUNTIME, "moduleName_(%{public}s, path %{public}s",
362         moduleName_.c_str(), path.c_str());
363 
364     std::string fileName;
365     if (!hapPath.empty()) {
366         fileName.append(codePath_).append(Constants::FILE_SEPARATOR).append(modulePath);
367         std::regex pattern(std::string(Constants::FILE_DOT) + std::string(Constants::FILE_SEPARATOR));
368         fileName = std::regex_replace(fileName, pattern, "");
369     } else {
370         if (!MakeFilePath(codePath_, modulePath, fileName)) {
371             TAG_LOGE(AAFwkTag::ETSRUNTIME, "make module file path: %{public}s failed", modulePath.c_str());
372             return nullptr;
373         }
374     }
375     std::unique_ptr<AppExecFwk::ETSNativeReference> etsNativeReference = LoadEtsModule(moduleName, fileName,
376         hapPath, srcEntrance);
377     return etsNativeReference;
378 }
379 
LoadEtsModule(const std::string & moduleName,const std::string & fileName,const std::string & hapPath,const std::string & srcEntrance)380 std::unique_ptr<AppExecFwk::ETSNativeReference> ETSRuntime::LoadEtsModule(const std::string &moduleName,
381     const std::string &fileName, const std::string &hapPath, const std::string &srcEntrance)
382 {
383     if (g_etsEnvFuncs == nullptr ||
384         g_etsEnvFuncs->LoadModule == nullptr) {
385         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or LoadModule");
386         return std::unique_ptr<AppExecFwk::ETSNativeReference>();
387     }
388 
389     std::string modulePath = BUNDLE_INSTALL_PATH + moduleName_ + MERGE_ABC_PATH;
390     std::string entryPath = HandleOhmUrlSrcEntry(srcEntrance);
391     void *cls = nullptr;
392     void *obj = nullptr;
393     void *ref = nullptr;
394     if (!g_etsEnvFuncs->LoadModule(modulePath, entryPath, cls, obj, ref)) {
395         TAG_LOGE(AAFwkTag::ETSRUNTIME, "LoadModule failed");
396         return std::unique_ptr<AppExecFwk::ETSNativeReference>();
397     }
398     auto etsNativeReference = std::make_unique<AppExecFwk::ETSNativeReference>();
399     etsNativeReference->aniCls = reinterpret_cast<ani_class>(cls);
400     etsNativeReference->aniObj = reinterpret_cast<ani_object>(obj);
401     etsNativeReference->aniRef = reinterpret_cast<ani_ref>(ref);
402     return etsNativeReference;
403 }
404 
HandleUncaughtError()405 bool ETSRuntime::HandleUncaughtError()
406 {
407     TAG_LOGD(AAFwkTag::ETSRUNTIME, "HandleUncaughtError called");
408     if (g_etsEnvFuncs == nullptr ||
409         g_etsEnvFuncs->HandleUncaughtError == nullptr) {
410         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or HandleUncaughtError");
411         return false;
412     }
413     g_etsEnvFuncs->HandleUncaughtError();
414     return true;
415 }
416 
GetJsRuntime() const417 const std::unique_ptr<AbilityRuntime::Runtime> &ETSRuntime::GetJsRuntime() const
418 {
419     return jsRuntime_;
420 }
421 
MoveJsRuntime()422 std::unique_ptr<AbilityRuntime::Runtime> ETSRuntime::MoveJsRuntime()
423 {
424     return std::move(jsRuntime_);
425 }
426 
PreloadSystemModule(const std::string & moduleName)427 void ETSRuntime::PreloadSystemModule(const std::string &moduleName)
428 {
429     if (jsRuntime_ != nullptr) {
430         jsRuntime_->PreloadSystemModule(moduleName);
431     }
432 }
433 
SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate> moduleCheckerDelegate) const434 void ETSRuntime::SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate> moduleCheckerDelegate) const
435 {
436     if (jsRuntime_ != nullptr) {
437         jsRuntime_->SetModuleLoadChecker(moduleCheckerDelegate);
438     }
439 }
440 
HandleOhmUrlSrcEntry(const std::string & srcEntry)441 std::string ETSRuntime::HandleOhmUrlSrcEntry(const std::string &srcEntry)
442 {
443     size_t lastSlashPos = srcEntry.rfind('/');
444     if (lastSlashPos == std::string::npos) {
445         std::string fileName = srcEntry;
446         // If there is no slash, the entire string is processed directly.
447         HandleOhmUrlFileName(fileName);
448         return fileName;
449     }
450     std::string base = srcEntry.substr(0, lastSlashPos + 1);
451     std::string fileName = srcEntry.substr(lastSlashPos + 1);
452     HandleOhmUrlFileName(fileName);
453     return base + fileName;
454 }
455 
HandleOhmUrlFileName(std::string & fileName)456 void ETSRuntime::HandleOhmUrlFileName(std::string &fileName)
457 {
458     size_t colonPos = fileName.rfind(':');
459     if (colonPos != std::string::npos) {
460         // <fileName>:<className>  =>  <fileName>/<className>
461         fileName.replace(colonPos, 1, "/");
462     } else {
463         // <fileName>  =>  <fileName>/<fileName>
464         fileName = fileName + "/" + fileName;
465     }
466 }
467 
PreloadSystemClass(const char * className)468 bool ETSRuntime::PreloadSystemClass(const char *className)
469 {
470     TAG_LOGD(AAFwkTag::ETSRUNTIME, "PreloadSystemClass called");
471     if (g_etsEnvFuncs == nullptr ||
472         g_etsEnvFuncs->PreloadSystemClass == nullptr) {
473         TAG_LOGE(AAFwkTag::ETSRUNTIME, "null g_etsEnvFuncs or PreloadSystemClass");
474         return false;
475     }
476     g_etsEnvFuncs->PreloadSystemClass(className);
477     return true;
478 }
479 } // namespace AbilityRuntime
480 } // namespace OHOS