1/*
2 * Copyright (c) 2024 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 "cj_runtime.h"
17
18#include <dlfcn.h>
19#include <unistd.h>
20#include <filesystem>
21#include <regex>
22
23#include "cj_envsetup.h"
24#include "hilog_tag_wrapper.h"
25#include "hdc_register.h"
26#include "connect_server_manager.h"
27
28using namespace OHOS::AbilityRuntime;
29
30#ifdef APP_USE_ARM64
31#define APP_LIB_NAME "arm64"
32#elif defined(APP_USE_ARM)
33#define APP_LIB_NAME "arm"
34#elif defined(APP_USE_X86_64)
35#define APP_LIB_NAME "x86_64"
36#else
37#error unsupported platform
38#endif
39
40namespace {
41const std::string DEBUGGER = "@Debugger";
42const std::string SANDBOX_LIB_PATH = "/data/storage/el1/bundle/libs/" APP_LIB_NAME;
43const std::string CJ_RT_PATH = SANDBOX_LIB_PATH + "/runtime";
44const std::string CJ_LIB_PATH = SANDBOX_LIB_PATH + "/ohos";
45const std::string CJ_SYSLIB_PATH = "/system/lib64:/system/lib64/platformsdk:/system/lib64/module:/system/lib64/ndk";
46const std::string CJ_CHIPSDK_PATH = "/system/lib64/chipset-pub-sdk";
47} // namespace
48
49#define LIB_NAME "libcj_environment.z.so"
50#define GET_ENV_INS_NAME "OHOS_GetCJEnvInstance"
51
52namespace OHOS {
53CJEnvMethods* CJEnv::LoadInstance()
54{
55    auto handle = dlopen(LIB_NAME, RTLD_NOW);
56    if (!handle) {
57        TAG_LOGE(AAFwkTag::CJRUNTIME, "dlopen failed %{public}s, %{public}s", LIB_NAME, dlerror());
58        return nullptr;
59    }
60    auto symbol = dlsym(handle, GET_ENV_INS_NAME);
61    if (!symbol) {
62        TAG_LOGE(AAFwkTag::CJRUNTIME, "dlsym failed %{public}s, %{public}s", GET_ENV_INS_NAME, dlerror());
63        dlclose(handle);
64        return nullptr;
65    }
66    auto func = reinterpret_cast<CJEnvMethods* (*)()>(symbol);
67    return func();
68}
69}
70AppLibPathVec CJRuntime::appLibPaths_;
71
72std::string CJRuntime::packageName_;
73
74std::unique_ptr<CJRuntime> CJRuntime::Create(const Options& options)
75{
76    auto instance = std::make_unique<CJRuntime>();
77    if (!instance || !instance->Initialize(options)) {
78        return nullptr;
79    }
80    return instance;
81}
82
83void CJRuntime::SetAppLibPath(const AppLibPathMap& appLibPaths)
84{
85    std::string appPath = "";
86    for (const auto& kv : appLibPaths) {
87        for (const auto& libPath : kv.second) {
88            TAG_LOGD(AAFwkTag::CJRUNTIME, "SetCJAppLibPath: %{public}s.", libPath.c_str());
89            CJRuntime::appLibPaths_.emplace_back(libPath);
90            appPath += appPath.empty() ? libPath : ":" + libPath;
91        }
92    }
93    auto cjEnv = OHOS::CJEnv::LoadInstance();
94    if (cjEnv == nullptr) {
95        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
96        return;
97    }
98    cjEnv->initCJChipSDKNS(CJ_CHIPSDK_PATH);
99    cjEnv->initCJAppNS(appPath);
100    cjEnv->initCJSDKNS(CJ_RT_PATH + ":" + CJ_LIB_PATH);
101    cjEnv->initCJSysNS(CJ_SYSLIB_PATH);
102}
103
104bool CJRuntime::Initialize(const Options& options)
105{
106    if (options.lang != GetLanguage()) {
107        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJRuntime Initialize fail, language mismatch");
108        return false;
109    }
110    auto cjEnv = OHOS::CJEnv::LoadInstance();
111    if (cjEnv == nullptr) {
112        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
113        return false;
114    }
115    if (!cjEnv->startRuntime()) {
116        TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj runtime failed");
117        return false;
118    }
119    if (!cjEnv->startUIScheduler()) {
120        TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj ui context failed");
121        return false;
122    }
123    if (!LoadCJAppLibrary(CJRuntime::appLibPaths_)) {
124        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJRuntime::Initialize fail, load app library fail.");
125        return false;
126    }
127    bundleName_ = options.bundleName;
128    instanceId_ = static_cast<uint32_t>(getproctid());
129    return true;
130}
131
132void CJRuntime::RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo& uncaughtExceptionInfo)
133{
134    auto cjEnv = OHOS::CJEnv::LoadInstance();
135    if (cjEnv == nullptr) {
136        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
137        return;
138    }
139    cjEnv->registerCJUncaughtExceptionHandler(uncaughtExceptionInfo);
140}
141
142bool CJRuntime::IsCJAbility(const std::string& info)
143{
144    // in cj application, the srcEntry format should be packageName.AbilityClassName.
145    std::string pattern = "^([a-zA-Z0-9_]+\\.)+[a-zA-Z0-9_]+$";
146    return std::regex_match(info, std::regex(pattern));
147}
148
149bool CJRuntime::LoadCJAppLibrary(const AppLibPathVec& appLibPaths)
150{
151    auto cjEnv = OHOS::CJEnv::LoadInstance();
152    if (cjEnv == nullptr) {
153        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
154        return false;
155    }
156    void* handle = nullptr;
157    // According to the OHOS rule, the format of the SO name is as follows
158    auto targetSoName = "lib" + packageName_ + ".so";
159
160    for (const auto& libPath : appLibPaths) {
161        for (auto& itor : std::filesystem::directory_iterator(libPath)) {
162            // According to the convention, the names of cj generated products must contain the following keywords
163            if (itor.path().string().find(targetSoName) == std::string::npos) {
164                continue;
165            }
166            handle = cjEnv->loadCJLibrary(itor.path().c_str());
167            if (handle == nullptr) {
168                char* errMsg = dlerror();
169                TAG_LOGE(AAFwkTag::CJRUNTIME,
170                    "Failed to load %{public}s : reason: %{public}s.", itor.path().c_str(), errMsg ? errMsg : "null");
171                return false;
172            }
173        }
174    }
175    appLibLoaded_ = true;
176    return true;
177}
178
179void CJRuntime::SetPackageName(std::string srcEntryName)
180{
181    // According to the srcEntry rule in the Cangjie application,
182    // the last '.' The previous strings were all package names
183    packageName_ = srcEntryName.substr(0, srcEntryName.find_last_of("."));
184}
185
186void CJRuntime::SetSanitizerVersion(SanitizerKind kind)
187{
188    auto cjEnv = OHOS::CJEnv::LoadInstance();
189    if (cjEnv == nullptr) {
190        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
191        return;
192    }
193    cjEnv->setSanitizerKindRuntimeVersion(kind);
194}
195
196void CJRuntime::StartDebugMode(const DebugOption dOption)
197{
198    if (debugModel_) {
199        TAG_LOGI(AAFwkTag::CJRUNTIME, "Already in debug mode");
200        return;
201    }
202
203    bool isStartWithDebug = dOption.isStartWithDebug;
204    bool isDebugApp = dOption.isDebugApp;
205    const std::string bundleName = bundleName_;
206    std::string inputProcessName = bundleName_ != dOption.processName ? dOption.processName : "";
207
208    TAG_LOGI(AAFwkTag::CJRUNTIME, "StartDebugMode %{public}s", bundleName_.c_str());
209
210    HdcRegister::Get().StartHdcRegister(bundleName_, inputProcessName, isDebugApp,
211        [bundleName, isStartWithDebug, isDebugApp](int socketFd, std::string option) {
212            TAG_LOGI(AAFwkTag::CJRUNTIME,
213                "HdcRegister callback is call, socket fd is %{public}d, option is %{public}s.",
214                socketFd, option.c_str());
215            if (option.find(DEBUGGER) == std::string::npos) {
216                if (!isDebugApp) {
217                    ConnectServerManager::Get().StopConnectServer(false);
218                }
219                ConnectServerManager::Get().SendDebuggerInfo(isStartWithDebug, isDebugApp);
220                ConnectServerManager::Get().StartConnectServer(bundleName, socketFd, false);
221            } else {
222                TAG_LOGE(AAFwkTag::CJRUNTIME, "debugger service unexpected option: %{public}s", option.c_str());
223            }
224        });
225    if (isDebugApp) {
226        ConnectServerManager::Get().StartConnectServer(bundleName_, -1, true);
227    }
228    ConnectServerManager::Get().AddInstance(instanceId_, instanceId_);
229
230    debugModel_ = StartDebugger();
231}
232
233bool CJRuntime::StartDebugger()
234{
235    auto cjEnv = OHOS::CJEnv::LoadInstance();
236    if (cjEnv == nullptr) {
237        TAG_LOGE(AAFwkTag::CJRUNTIME, "CJEnv LoadInstance failed.");
238        return false;
239    }
240    return cjEnv->startDebugger();
241}
242
243void CJRuntime::UnLoadCJAppLibrary()
244{
245    TAG_LOGI(AAFwkTag::CJRUNTIME, "UnLoadCJAppLibrary not support yet");
246}
247