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 "hitrace_meter.h"
26 #include "hdc_register.h"
27 #include "connect_server_manager.h"
28
29 using namespace OHOS::AbilityRuntime;
30
31
32 namespace {
33 const std::string DEBUGGER = "@Debugger";
34 } // namespace
35
36 #define LIB_NAME "libcj_environment.z.so"
37 #define GET_ENV_INS_NAME "OHOS_GetCJEnvInstance"
38
39 namespace OHOS {
LoadInstance()40 CJEnvMethods* CJEnv::LoadInstance()
41 {
42 auto handle = dlopen(LIB_NAME, RTLD_NOW);
43 if (!handle) {
44 TAG_LOGE(AAFwkTag::CJRUNTIME, "dlopen failed %{public}s, %{public}s", LIB_NAME, dlerror());
45 return nullptr;
46 }
47 auto symbol = dlsym(handle, GET_ENV_INS_NAME);
48 if (!symbol) {
49 TAG_LOGE(AAFwkTag::CJRUNTIME, "dlsym failed %{public}s, %{public}s", GET_ENV_INS_NAME, dlerror());
50 dlclose(handle);
51 return nullptr;
52 }
53 auto func = reinterpret_cast<CJEnvMethods* (*)()>(symbol);
54 return func();
55 }
56 }
57 AppLibPathVec CJRuntime::appLibPaths_;
58
59 std::string CJRuntime::packageName_;
60
Create(const Options & options)61 std::unique_ptr<CJRuntime> CJRuntime::Create(const Options& options)
62 {
63 auto instance = std::make_unique<CJRuntime>();
64 if (!instance || !instance->Initialize(options)) {
65 return nullptr;
66 }
67 return instance;
68 }
69
SetAppLibPath(const AppLibPathMap & appLibPaths)70 void CJRuntime::SetAppLibPath(const AppLibPathMap& appLibPaths)
71 {
72 HITRACE_METER_NAME(HITRACE_TAG_APP, "Initialize cangjie runtime and namespace");
73 std::string appPath = "";
74 for (const auto& kv : appLibPaths) {
75 for (const auto& libPath : kv.second) {
76 TAG_LOGD(AAFwkTag::CJRUNTIME, "SetCJAppLibPath: %{public}s.", libPath.c_str());
77 CJRuntime::appLibPaths_.emplace_back(libPath);
78 appPath += appPath.empty() ? libPath : ":" + libPath;
79 }
80 }
81 auto cjEnv = OHOS::CJEnv::LoadInstance();
82 if (cjEnv == nullptr) {
83 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
84 return;
85 }
86
87 cjEnv->initCJAppNS(appPath);
88 }
89
Initialize(const Options & options)90 bool CJRuntime::Initialize(const Options& options)
91 {
92 if (options.lang != GetLanguage()) {
93 TAG_LOGE(AAFwkTag::CJRUNTIME, "language mismatch");
94 return false;
95 }
96 auto cjEnv = OHOS::CJEnv::LoadInstance();
97 if (cjEnv == nullptr) {
98 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
99 return false;
100 }
101 if (!cjEnv->startRuntime()) {
102 TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj runtime failed");
103 return false;
104 }
105 if (!cjEnv->startUIScheduler()) {
106 TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj ui context failed");
107 return false;
108 }
109 if (!LoadCJAppLibrary(CJRuntime::appLibPaths_)) {
110 TAG_LOGE(AAFwkTag::CJRUNTIME, "load app library fail");
111 return false;
112 }
113 bundleName_ = options.bundleName;
114 instanceId_ = static_cast<uint32_t>(getproctid());
115 return true;
116 }
117
RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo & uncaughtExceptionInfo)118 void CJRuntime::RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo& uncaughtExceptionInfo)
119 {
120 auto cjEnv = OHOS::CJEnv::LoadInstance();
121 if (cjEnv == nullptr) {
122 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
123 return;
124 }
125 cjEnv->registerCJUncaughtExceptionHandler(uncaughtExceptionInfo);
126 }
127
IsCJAbility(const std::string & info)128 bool CJRuntime::IsCJAbility(const std::string& info)
129 {
130 // in cj application, the srcEntry format should be packageName.AbilityClassName.
131 std::string pattern = "^([a-zA-Z0-9_]+\\.)+[a-zA-Z0-9_]+$";
132 return std::regex_match(info, std::regex(pattern));
133 }
134
LoadCJAppLibrary(const AppLibPathVec & appLibPaths)135 bool CJRuntime::LoadCJAppLibrary(const AppLibPathVec& appLibPaths)
136 {
137 HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__);
138 auto cjEnv = OHOS::CJEnv::LoadInstance();
139 if (cjEnv == nullptr) {
140 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
141 return false;
142 }
143 void* handle = nullptr;
144 // According to the OHOS rule, the format of the SO name is as follows
145 auto targetSoName = "lib" + packageName_ + ".so";
146
147 for (const auto& libPath : appLibPaths) {
148 for (auto& itor : std::filesystem::directory_iterator(libPath)) {
149 // According to the convention, the names of cj generated products must contain the following keywords
150 if (itor.path().string().find(targetSoName) == std::string::npos) {
151 continue;
152 }
153 handle = cjEnv->loadCJLibrary(itor.path().c_str());
154 if (handle == nullptr) {
155 char* errMsg = dlerror();
156 TAG_LOGE(AAFwkTag::CJRUNTIME,
157 "load %{public}s failed, reason: %{public}s", itor.path().c_str(), errMsg ? errMsg : "null");
158 return false;
159 }
160 }
161 }
162 appLibLoaded_ = true;
163 return true;
164 }
165
SetPackageName(std::string srcEntryName)166 void CJRuntime::SetPackageName(std::string srcEntryName)
167 {
168 // According to the srcEntry rule in the Cangjie application,
169 // the last '.' The previous strings were all package names
170 packageName_ = srcEntryName.substr(0, srcEntryName.find_last_of("."));
171 }
172
SetSanitizerVersion(SanitizerKind kind)173 void CJRuntime::SetSanitizerVersion(SanitizerKind kind)
174 {
175 auto cjEnv = OHOS::CJEnv::LoadInstance();
176 if (cjEnv == nullptr) {
177 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
178 return;
179 }
180 cjEnv->setSanitizerKindRuntimeVersion(kind);
181 }
182
RegisterCangjieCallback()183 bool CJRuntime::RegisterCangjieCallback()
184 {
185 auto cjEnv = OHOS::CJEnv::LoadInstance();
186 constexpr char CANGJIE_DEBUGGER_LIB_PATH[] = "libark_connect_inspector.z.so";
187 #define LIBARARYKIND_SYS 0
188 auto handlerConnectServerSo = cjEnv->loadLibrary(LIBARARYKIND_SYS, CANGJIE_DEBUGGER_LIB_PATH);
189 if (handlerConnectServerSo == nullptr) {
190 TAG_LOGE(AAFwkTag::CJRUNTIME, "null handlerConnectServerSo: %{public}s", dlerror());
191 return false;
192 }
193 using SendMsgCB = const std::function<void(const std::string& message)>;
194 using SetCangjieCallback = void(*)(const std::function<void(const std::string& message, SendMsgCB)>);
195 using CangjieCallback = void(*)(const std::string& message, SendMsgCB);
196 auto setCangjieCallback = reinterpret_cast<SetCangjieCallback>(
197 cjEnv->getSymbol(handlerConnectServerSo, "SetCangjieCallback"));
198 if (setCangjieCallback == nullptr) {
199 TAG_LOGE(AAFwkTag::CJRUNTIME, "null setCangjieCallback: %{public}s", dlerror());
200 return false;
201 }
202 #define RTLIB_NAME "libcangjie-runtime.so"
203 #define LIBARARYKIND_SDK 1
204 auto dso = cjEnv->loadLibrary(LIBARARYKIND_SDK, RTLIB_NAME);
205 if (!dso) {
206 TAG_LOGE(AAFwkTag::CJRUNTIME, "load library failed: %{public}s", RTLIB_NAME);
207 return false;
208 }
209 TAG_LOGE(AAFwkTag::CJRUNTIME, "load libcangjie-runtime.so success");
210 #define PROFILERAGENT "ProfilerAgent"
211 CangjieCallback cangjieCallback = reinterpret_cast<CangjieCallback>(cjEnv->getSymbol(dso, PROFILERAGENT));
212 if (cangjieCallback == nullptr) {
213 TAG_LOGE(AAFwkTag::CJRUNTIME, "runtime api not found: %{public}s", PROFILERAGENT);
214 dlclose(handlerConnectServerSo);
215 handlerConnectServerSo = nullptr;
216 return false;
217 }
218 TAG_LOGE(AAFwkTag::CJRUNTIME, "find runtime api success");
219 setCangjieCallback(cangjieCallback);
220 dlclose(handlerConnectServerSo);
221 handlerConnectServerSo = nullptr;
222 return true;
223 }
224
StartDebugMode(const DebugOption dOption)225 void CJRuntime::StartDebugMode(const DebugOption dOption)
226 {
227 if (debugModel_) {
228 TAG_LOGI(AAFwkTag::CJRUNTIME, "already debug mode");
229 return;
230 }
231
232 bool isStartWithDebug = dOption.isStartWithDebug;
233 bool isDebugApp = dOption.isDebugApp;
234 const std::string bundleName = bundleName_;
235 int32_t instanceId = static_cast<int32_t>(instanceId_);
236 std::string inputProcessName = bundleName_ != dOption.processName ? dOption.processName : "";
237
238 TAG_LOGI(AAFwkTag::CJRUNTIME, "StartDebugMode %{public}s", bundleName_.c_str());
239
240 HdcRegister::Get().StartHdcRegister(bundleName_, inputProcessName, isDebugApp,
241 HdcRegister::DebugRegisterMode::HDC_DEBUG_REG,
242 [bundleName, isStartWithDebug, isDebugApp, instanceId](int socketFd, std::string option) {
243 TAG_LOGI(AAFwkTag::CJRUNTIME, "hdcRegister callback call, socket fd: %{public}d, option: %{public}s.",
244 socketFd, option.c_str());
245 if (option.find(DEBUGGER) == std::string::npos) {
246 ConnectServerManager::Get().StopConnectServer(false);
247 TAG_LOGI(AAFwkTag::CJRUNTIME, "start SendInstanceMessage");
248 ConnectServerManager::Get().SendInstanceMessage(instanceId, instanceId, bundleName);
249 ConnectServerManager::Get().SendDebuggerInfo(isStartWithDebug, isDebugApp);
250 ConnectServerManager::Get().StartConnectServer(bundleName, socketFd, false);
251 CJRuntime::RegisterCangjieCallback();
252 } else {
253 TAG_LOGE(AAFwkTag::CJRUNTIME, "debugger service unexpected option: %{public}s", option.c_str());
254 }
255 });
256 if (isDebugApp) {
257 ConnectServerManager::Get().StartConnectServer(bundleName_, -1, true);
258 }
259 ConnectServerManager::Get().AddInstance(instanceId_, instanceId_);
260
261 debugModel_ = StartDebugger();
262 }
263
StartDebugger()264 bool CJRuntime::StartDebugger()
265 {
266 auto cjEnv = OHOS::CJEnv::LoadInstance();
267 if (cjEnv == nullptr) {
268 TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
269 return false;
270 }
271 return cjEnv->startDebugger();
272 }
273
UnLoadCJAppLibrary()274 void CJRuntime::UnLoadCJAppLibrary()
275 {
276 TAG_LOGI(AAFwkTag::CJRUNTIME, "UnLoadCJAppLibrary not support yet");
277 }
278