1 /*
2  * Copyright (c) 2022-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 "connect_server_manager.h"
17 
18 #include <dlfcn.h>
19 #include <unistd.h>
20 
21 #include "hilog_tag_wrapper.h"
22 
23 namespace OHOS::AbilityRuntime {
24 namespace {
GetInstanceMapMessage(const std::string & messageType,int32_t instanceId,const std::string & instanceName,int32_t tid)25 std::string GetInstanceMapMessage(
26     const std::string& messageType, int32_t instanceId, const std::string& instanceName, int32_t tid)
27 {
28     std::string message;
29     message.append("{\"type\":\"");
30     message.append(messageType);
31     message.append("\",\"instanceId\":");
32     message.append(std::to_string(instanceId));
33     message.append(",\"name\":\"");
34     message.append(instanceName);
35     message.append("\",\"tid\":");
36     message.append(std::to_string(tid));
37     message.append(",\"apiType\":\"");
38     message.append("stageMode\"");
39     message.append(",\"language\":\"");
40     message.append("ets\"");
41     message.append("}");
42     return message;
43 }
44 }
45 
46 using StartServer = void (*)(const std::string&);
47 using StartServerForSocketPair = void (*)(int);
48 using SendMessage = void (*)(const std::string&);
49 using SendLayoutMessage = void (*)(const std::string&);
50 using StopServer = void (*)(const std::string&);
51 using StoreMessage = void (*)(int32_t, const std::string&);
52 using StoreInspectorInfo = void (*)(const std::string&, const std::string&);
53 using SetProfilerCallback = void (*)(const std::function<void(bool)> &setArkUIStateProfilerStatus);
54 using SetSwitchCallBack = void (*)(const std::function<void(bool)> &setStatus,
55     const std::function<void(int32_t)> &createLayoutInfo, int32_t instanceId);
56 using SetConnectCallback = void (*)(const std::function<void(bool)>);
57 using RemoveMessage = void (*)(int32_t);
58 using WaitForConnection = bool (*)();
59 using SetRecordCallBack = void (*)(const std::function<void(void)> &startRecordFunc,
60     const std::function<void(void)> &stopRecordFunc);
61 
62 std::mutex g_debuggerMutex;
63 std::mutex g_loadsoMutex;
64 std::mutex ConnectServerManager::instanceMutex_;
65 std::mutex ConnectServerManager::callbackMutex_;
66 std::unordered_map<int, std::pair<void*, const DebuggerPostTask>> g_debuggerInfo;
67 
~ConnectServerManager()68 ConnectServerManager::~ConnectServerManager()
69 {
70     StopConnectServer();
71 }
72 
Get()73 ConnectServerManager& ConnectServerManager::Get()
74 {
75     static ConnectServerManager connectServerManager;
76     return connectServerManager;
77 }
78 
LoadConnectServerDebuggerSo()79 void ConnectServerManager::LoadConnectServerDebuggerSo()
80 {
81     std::lock_guard<std::mutex> lock(g_loadsoMutex);
82     if (handlerConnectServerSo_ == nullptr) {
83         handlerConnectServerSo_ = dlopen("libark_connect_inspector.z.so", RTLD_LAZY);
84         if (handlerConnectServerSo_ == nullptr) {
85             TAG_LOGE(AAFwkTag::JSRUNTIME, "null handlerConnectServerSo_");
86             return;
87         }
88     }
89 }
90 
StartConnectServer(const std::string & bundleName,int socketFd,bool isLocalAbstract)91 void ConnectServerManager::StartConnectServer(const std::string& bundleName, int socketFd, bool isLocalAbstract)
92 {
93     TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
94 
95     LoadConnectServerDebuggerSo();
96     bundleName_ = bundleName;
97     if (isLocalAbstract) {
98         auto startServer = reinterpret_cast<StartServer>(dlsym(handlerConnectServerSo_, "StartServer"));
99         if (startServer == nullptr) {
100             TAG_LOGE(AAFwkTag::JSRUNTIME, "null startServer");
101             return;
102         }
103         startServer(bundleName_);
104         return;
105     }
106     auto startServerForSocketPair =
107         reinterpret_cast<StartServerForSocketPair>(dlsym(handlerConnectServerSo_, "StartServerForSocketPair"));
108     if (startServerForSocketPair == nullptr) {
109         TAG_LOGE(
110             AAFwkTag::JSRUNTIME, "null startServerForSocketPair");
111         return;
112     }
113     startServerForSocketPair(socketFd);
114 
115     std::lock_guard<std::mutex> lock(callbackMutex_);
116     for (const auto &callback : connectServerCallbacks_) {
117         if (callback != nullptr) {
118             callback();
119         }
120     }
121 }
122 
StopConnectServer(bool isCloseSo)123 void ConnectServerManager::StopConnectServer(bool isCloseSo)
124 {
125     TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
126     if (handlerConnectServerSo_ == nullptr) {
127         TAG_LOGE(AAFwkTag::JSRUNTIME, "null handlerConnectServerSo_");
128         return;
129     }
130     auto stopServer = reinterpret_cast<StopServer>(dlsym(handlerConnectServerSo_, "StopServer"));
131     if (stopServer != nullptr) {
132         stopServer(bundleName_);
133     } else {
134         TAG_LOGE(AAFwkTag::JSRUNTIME, "null StopServer");
135     }
136     if (isCloseSo) {
137         dlclose(handlerConnectServerSo_);
138         handlerConnectServerSo_ = nullptr;
139     }
140 }
141 
142 
StoreInstanceMessage(int32_t tid,int32_t instanceId,const std::string & instanceName)143 bool ConnectServerManager::StoreInstanceMessage(int32_t tid, int32_t instanceId, const std::string& instanceName)
144 {
145     {
146         std::lock_guard<std::mutex> lock(mutex_);
147         auto result = instanceMap_.try_emplace(instanceId, std::make_pair(instanceName, tid));
148         if (!result.second) {
149             TAG_LOGW(AAFwkTag::JSRUNTIME, "Instance %{public}d added", instanceId);
150             return false;
151         }
152     }
153     return true;
154 }
155 
StoreDebuggerInfo(int32_t tid,void * vm,const panda::JSNApi::DebugOption & debugOption,const DebuggerPostTask & debuggerPostTask,bool isDebugApp)156 void ConnectServerManager::StoreDebuggerInfo(int32_t tid, void* vm, const panda::JSNApi::DebugOption& debugOption,
157     const DebuggerPostTask& debuggerPostTask, bool isDebugApp)
158 {
159     std::lock_guard<std::mutex> lock(g_debuggerMutex);
160     if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
161         g_debuggerInfo.emplace(tid, std::make_pair(vm, debuggerPostTask));
162     }
163 
164     if (!isConnected_) {
165         TAG_LOGW(AAFwkTag::JSRUNTIME, "not Connected");
166         return;
167     }
168 
169     panda::JSNApi::StoreDebugInfo(tid, reinterpret_cast<panda::EcmaVM*>(vm), debugOption, debuggerPostTask, isDebugApp);
170 }
171 
SendDebuggerInfo(bool needBreakPoint,bool isDebugApp)172 void ConnectServerManager::SendDebuggerInfo(bool needBreakPoint, bool isDebugApp)
173 {
174     ConnectServerManager::Get().SetConnectedCallback();
175     std::lock_guard<std::mutex> lock(mutex_);
176     for (const auto& instance : instanceMap_) {
177         auto instanceId = instance.first;
178         auto instanceName = instance.second.first;
179         auto tid = instance.second.second;
180 
181         panda::EcmaVM* vm = reinterpret_cast<panda::EcmaVM*>(g_debuggerInfo[tid].first);
182         std::lock_guard<std::mutex> lock(g_debuggerMutex);
183         const auto &debuggerPostTask = g_debuggerInfo[tid].second;
184         if (!debuggerPostTask) {
185             continue;
186         }
187         ConnectServerManager::Get().SendInstanceMessage(tid, instanceId, instanceName);
188         panda::JSNApi::DebugOption debugOption = {ARK_DEBUGGER_LIB_PATH, isDebugApp ? needBreakPoint : false};
189         panda::JSNApi::StoreDebugInfo(tid, vm, debugOption, debuggerPostTask, isDebugApp);
190     }
191 }
192 
SetConnectedCallback()193 void ConnectServerManager::SetConnectedCallback()
194 {
195     LoadConnectServerDebuggerSo();
196 
197     auto setConnectCallBack = reinterpret_cast<SetConnectCallback>(
198         dlsym(handlerConnectServerSo_, "SetConnectCallback"));
199     if (setConnectCallBack == nullptr) {
200         TAG_LOGE(AAFwkTag::JSRUNTIME, "null setConnectCallBack");
201         return;
202     }
203 
204     setConnectCallBack([](bool isConnected) {
205         ConnectServerManager::Get().isConnected_ = isConnected;
206     });
207 }
208 
SetSwitchCallback(int32_t instanceId)209 void ConnectServerManager::SetSwitchCallback(int32_t instanceId)
210 {
211     LoadConnectServerDebuggerSo();
212     auto setSwitchCallBack = reinterpret_cast<SetSwitchCallBack>(
213         dlsym(handlerConnectServerSo_, "SetSwitchCallBack"));
214     if (setSwitchCallBack == nullptr) {
215         TAG_LOGE(AAFwkTag::JSRUNTIME, "null setSwitchCallBack");
216         return;
217     }
218     setSwitchCallBack(
219         [this](bool status) {
220             if (setStatus_ != nullptr) {
221                 setStatus_(status);
222             } else {
223                 TAG_LOGE(AAFwkTag::JSRUNTIME, "null setStatus_");
224             }
225         },
226         [this](int32_t containerId) {
227             if (createLayoutInfo_ != nullptr) {
228                 createLayoutInfo_(containerId);
229             } else {
230                 TAG_LOGE(AAFwkTag::JSRUNTIME, "null createLayoutInfo_");
231             }
232         }, instanceId);
233 }
234 
SetProfilerCallBack()235 void ConnectServerManager::SetProfilerCallBack()
236 {
237     LoadConnectServerDebuggerSo();
238     auto setProfilerCallback = reinterpret_cast<SetProfilerCallback>(
239         dlsym(handlerConnectServerSo_, "SetProfilerCallback"));
240     if (setProfilerCallback == nullptr) {
241         TAG_LOGE(AAFwkTag::JSRUNTIME,
242                  "ConnectServerManager::AddInstance failed to find symbol 'setProfilerCallback'");
243         return;
244     }
245     setProfilerCallback([this](bool status) {
246         if (setArkUIStateProfilerStatus_ != nullptr) {
247             setArkUIStateProfilerStatus_(status);
248         } else {
249             TAG_LOGE(AAFwkTag::JSRUNTIME, "null etArkUIStateProfilerStatus_");
250         }
251     });
252 }
253 
SendInstanceMessage(int32_t tid,int32_t instanceId,const std::string & instanceName)254 bool ConnectServerManager::SendInstanceMessage(int32_t tid, int32_t instanceId, const std::string& instanceName)
255 {
256     TAG_LOGI(AAFwkTag::JSRUNTIME, "called");
257     ConnectServerManager::Get().SetSwitchCallback(instanceId);
258     ConnectServerManager::Get().SetProfilerCallBack();
259     std::string message = GetInstanceMapMessage("addInstance", instanceId, instanceName, tid);
260     LoadConnectServerDebuggerSo();
261     auto storeMessage = reinterpret_cast<StoreMessage>(dlsym(handlerConnectServerSo_, "StoreMessage"));
262     if (storeMessage == nullptr) {
263         TAG_LOGE(AAFwkTag::JSRUNTIME, "null storeMessage");
264         return false;
265     }
266     storeMessage(instanceId, message);
267     return true;
268 }
269 
270 
AddInstance(int32_t tid,int32_t instanceId,const std::string & instanceName)271 bool ConnectServerManager::AddInstance(int32_t tid, int32_t instanceId, const std::string& instanceName)
272 {
273     {
274         std::lock_guard<std::mutex> lock(mutex_);
275         auto result = instanceMap_.try_emplace(instanceId, std::make_pair(instanceName, tid));
276         if (!result.second) {
277             TAG_LOGW(
278                 AAFwkTag::JSRUNTIME, "Instance %{public}d added", instanceId);
279             return false;
280         }
281     }
282 
283     if (!isConnected_) {
284         TAG_LOGW(AAFwkTag::JSRUNTIME, "not Connected");
285         return false;
286     }
287 
288     TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
289 
290     ConnectServerManager::Get().SetSwitchCallback(instanceId);
291     ConnectServerManager::Get().SetProfilerCallBack();
292     LoadConnectServerDebuggerSo();
293     // Get the message including information of new instance, which will be send to IDE.
294     std::string message = GetInstanceMapMessage("addInstance", instanceId, instanceName, tid);
295 
296     auto storeMessage = reinterpret_cast<StoreMessage>(dlsym(handlerConnectServerSo_, "StoreMessage"));
297     if (storeMessage == nullptr) {
298         TAG_LOGE(AAFwkTag::JSRUNTIME, "null StoreMessage");
299         return false;
300     }
301     storeMessage(instanceId, message);
302 
303     // WaitForConnection() means the connection state of the connect server
304     auto sendMessage = reinterpret_cast<SendMessage>(dlsym(handlerConnectServerSo_, "SendMessage"));
305     if (sendMessage == nullptr) {
306         TAG_LOGE(AAFwkTag::JSRUNTIME, "null SendMessage");
307         return false;
308     }
309     // if connected, message will be sent immediately.
310     sendMessage(message);
311     return true;
312 }
313 
RemoveInstance(int32_t instanceId)314 void ConnectServerManager::RemoveInstance(int32_t instanceId)
315 {
316     TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
317     std::string instanceName;
318     int32_t tid;
319 
320     {
321         std::lock_guard<std::mutex> lock(mutex_);
322         auto it = instanceMap_.find(instanceId);
323         if (it == instanceMap_.end()) {
324             TAG_LOGW(AAFwkTag::JSRUNTIME, "Instance %{public}d not found", instanceId);
325             return;
326         }
327 
328         instanceName = std::move(it->second.first);
329         tid = std::move(it->second.second);
330         instanceMap_.erase(it);
331     }
332 
333     if (!isConnected_) {
334         TAG_LOGW(AAFwkTag::JSRUNTIME, "not Connected");
335         return;
336     }
337 
338     LoadConnectServerDebuggerSo();
339     auto waitForConnection = reinterpret_cast<WaitForConnection>(dlsym(handlerConnectServerSo_, "WaitForConnection"));
340     if (waitForConnection == nullptr) {
341         TAG_LOGE(AAFwkTag::JSRUNTIME, "null WaitForConnection");
342         return;
343     }
344 
345     // Get the message including information of deleted instance, which will be send to IDE.
346     std::string message = GetInstanceMapMessage("destroyInstance", instanceId, instanceName, tid);
347 
348     auto removeMessage = reinterpret_cast<RemoveMessage>(dlsym(handlerConnectServerSo_, "RemoveMessage"));
349     if (removeMessage == nullptr) {
350         TAG_LOGE(AAFwkTag::JSRUNTIME, "null RemoveMessage");
351         return;
352     }
353     removeMessage(instanceId);
354 
355     if (waitForConnection()) {
356         return;
357     }
358 
359     auto sendMessage = reinterpret_cast<SendMessage>(dlsym(handlerConnectServerSo_, "SendMessage"));
360     if (sendMessage == nullptr) {
361         TAG_LOGE(AAFwkTag::JSRUNTIME, "null sendMessage");
362         return;
363     }
364     sendMessage(message);
365 }
366 
SendInspector(const std::string & jsonTreeStr,const std::string & jsonSnapshotStr)367 void ConnectServerManager::SendInspector(const std::string& jsonTreeStr, const std::string& jsonSnapshotStr)
368 {
369     TAG_LOGI(AAFwkTag::JSRUNTIME, "called");
370     auto sendLayoutMessage = reinterpret_cast<SendMessage>(dlsym(handlerConnectServerSo_, "SendLayoutMessage"));
371     if (sendLayoutMessage == nullptr) {
372         TAG_LOGE(AAFwkTag::JSRUNTIME, "null sendLayoutMessage");
373         return;
374     }
375 
376     sendLayoutMessage(jsonTreeStr);
377     sendLayoutMessage(jsonSnapshotStr);
378     auto storeInspectorInfo = reinterpret_cast<StoreInspectorInfo>(
379         dlsym(handlerConnectServerSo_, "StoreInspectorInfo"));
380     if (storeInspectorInfo == nullptr) {
381         TAG_LOGE(AAFwkTag::JSRUNTIME, "null StoreInspectorInfo");
382         return;
383     }
384     storeInspectorInfo(jsonTreeStr, jsonSnapshotStr);
385 }
386 
SetLayoutInspectorCallback(const std::function<void (int32_t)> & createLayoutInfo,const std::function<void (bool)> & setStatus)387 void ConnectServerManager::SetLayoutInspectorCallback(
388     const std::function<void(int32_t)>& createLayoutInfo, const std::function<void(bool)>& setStatus)
389 {
390     createLayoutInfo_ = createLayoutInfo;
391     setStatus_ = setStatus;
392 }
393 
GetLayoutInspectorCallback()394 std::function<void(int32_t)> ConnectServerManager::GetLayoutInspectorCallback()
395 {
396     return createLayoutInfo_;
397 }
398 
SetStateProfilerCallback(const std::function<void (bool)> & setArkUIStateProfilerStatus)399 void ConnectServerManager::SetStateProfilerCallback(const std::function<void(bool)> &setArkUIStateProfilerStatus)
400 {
401     setArkUIStateProfilerStatus_ = setArkUIStateProfilerStatus;
402 }
403 
SendArkUIStateProfilerMessage(const std::string & message)404 void ConnectServerManager::SendArkUIStateProfilerMessage(const std::string &message)
405 {
406     TAG_LOGI(AAFwkTag::JSRUNTIME, "called");
407     auto sendProfilerMessage = reinterpret_cast<SendMessage>(dlsym(handlerConnectServerSo_, "SendProfilerMessage"));
408     if (sendProfilerMessage == nullptr) {
409         TAG_LOGE(AAFwkTag::JSRUNTIME, "null sendProfilerMessage");
410         return;
411     }
412 
413     sendProfilerMessage(message);
414 }
415 
GetDebuggerPostTask(int32_t tid)416 DebuggerPostTask ConnectServerManager::GetDebuggerPostTask(int32_t tid)
417 {
418     auto it = g_debuggerInfo.find(tid);
419     if (it == g_debuggerInfo.end()) {
420         TAG_LOGW(AAFwkTag::JSRUNTIME, "tid %{public}d not found", tid);
421         return nullptr;
422     }
423     return it->second.second;
424 }
425 
SetRecordCallback(const std::function<void (void)> & startRecordFunc,const std::function<void (void)> & stopRecordFunc)426 bool ConnectServerManager::SetRecordCallback(const std::function<void(void)> &startRecordFunc,
427     const std::function<void(void)> &stopRecordFunc)
428 {
429     if (handlerConnectServerSo_ == nullptr) {
430         TAG_LOGE(AAFwkTag::JSRUNTIME, "No connected server");
431         return false;
432     }
433     auto setRecordCallback = reinterpret_cast<SetRecordCallBack>(dlsym(handlerConnectServerSo_, "SetRecordCallback"));
434     if (setRecordCallback == nullptr) {
435         TAG_LOGE(AAFwkTag::JSRUNTIME, "Failed to find SetRecordCallback");
436         return false;
437     }
438     setRecordCallback(startRecordFunc, stopRecordFunc);
439     return true;
440 }
441 
SetRecordResults(const std::string & jsonArrayStr)442 void ConnectServerManager::SetRecordResults(const std::string &jsonArrayStr)
443 {
444     if (handlerConnectServerSo_ == nullptr) {
445         TAG_LOGE(AAFwkTag::JSRUNTIME, "No connected server");
446         return;
447     }
448     auto sendLayoutMessage = reinterpret_cast<SendMessage>(dlsym(handlerConnectServerSo_, "SendLayoutMessage"));
449     if (sendLayoutMessage == nullptr) {
450         TAG_LOGE(AAFwkTag::JSRUNTIME, "Failed to find sendLayoutMessage");
451         return;
452     }
453     sendLayoutMessage(jsonArrayStr);
454 }
455 
RegistConnectServerCallback(const ServerConnectCallback & connectServerCallback)456 void ConnectServerManager::RegistConnectServerCallback(const ServerConnectCallback &connectServerCallback)
457 {
458     if (connectServerCallback == nullptr) {
459         TAG_LOGE(AAFwkTag::JSRUNTIME, "null callback");
460         return;
461     }
462     std::lock_guard<std::mutex> lock(callbackMutex_);
463     for (const auto &callback : connectServerCallbacks_) {
464         if (callback == connectServerCallback) {
465             TAG_LOGE(AAFwkTag::JSRUNTIME, "callback exist");
466             return;
467         }
468     }
469     connectServerCallbacks_.emplace_back(connectServerCallback);
470     TAG_LOGD(AAFwkTag::JSRUNTIME, "regist succeed");
471 }
472 
473 } // namespace OHOS::AbilityRuntime