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