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 "core/common/layout_inspector.h"
17
18 #ifdef USE_NEW_SKIA
19 #include "include/core/SkPixmap.h"
20 #include "include/core/SkData.h"
21 #include "include/encode/SkPngEncoder.h"
22 #include "src/base/SkBase64.h"
23 #else
24 #include "include/utils/SkBase64.h"
25 #endif
26
27 #include "include/core/SkImage.h"
28 #include "include/core/SkString.h"
29
30 #include "connect_server_manager.h"
31
32 #include "adapter/ohos/osal/pixel_map_ohos.h"
33 #include "adapter/ohos/entrance/rs_adapter.h"
34 #include "adapter/ohos/entrance/subwindow/subwindow_ohos.h"
35 #include "base/log/ace_checker.h"
36 #include "base/subwindow/subwindow_manager.h"
37 #include "base/thread/background_task_executor.h"
38 #include "base/websocket/websocket_manager.h"
39 #include "core/common/ace_engine.h"
40 #include "core/common/connect_server_manager.h"
41 #include "core/components_ng/render/adapter/component_snapshot.h"
42 #include "core/components_ng/render/adapter/rosen_render_context.h"
43 #include "core/components_v2/inspector/inspector.h"
44 #include "render_service_client/core/pipeline/rs_node_map.h"
45 namespace OHOS::Ace {
46
47 namespace {
48 constexpr size_t SNAP_PARTITION_SIZE = 100;
49 constexpr int64_t FIND_RSNODE_ERROR = -1;
50 constexpr int32_t UI_TREE = 0;
51 constexpr int32_t THREE_DIMENSIONS_TREE = 1;
52 constexpr int32_t QUERY_ABILITY = 2;
53 #ifndef USE_NEW_SKIA
54 constexpr int32_t PNG_ENCODE_QUALITY = 100;
55 #endif
ColorSpaceToSkColorSpace(const RefPtr<PixelMap> & pixmap)56 sk_sp<SkColorSpace> ColorSpaceToSkColorSpace(const RefPtr<PixelMap>& pixmap)
57 {
58 return SkColorSpace::MakeSRGB();
59 }
60
AlphaTypeToSkAlphaType(const RefPtr<PixelMap> & pixmap)61 SkAlphaType AlphaTypeToSkAlphaType(const RefPtr<PixelMap>& pixmap)
62 {
63 switch (pixmap->GetAlphaType()) {
64 case AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
65 return SkAlphaType::kUnknown_SkAlphaType;
66 case AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
67 return SkAlphaType::kOpaque_SkAlphaType;
68 case AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
69 return SkAlphaType::kPremul_SkAlphaType;
70 case AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
71 return SkAlphaType::kUnpremul_SkAlphaType;
72 default:
73 return SkAlphaType::kUnknown_SkAlphaType;
74 }
75 }
76
PixelFormatToSkColorType(const RefPtr<PixelMap> & pixmap)77 SkColorType PixelFormatToSkColorType(const RefPtr<PixelMap>& pixmap)
78 {
79 switch (pixmap->GetPixelFormat()) {
80 case PixelFormat::RGB_565:
81 return SkColorType::kRGB_565_SkColorType;
82 case PixelFormat::RGBA_8888:
83 return SkColorType::kRGBA_8888_SkColorType;
84 case PixelFormat::BGRA_8888:
85 return SkColorType::kBGRA_8888_SkColorType;
86 case PixelFormat::ALPHA_8:
87 return SkColorType::kAlpha_8_SkColorType;
88 case PixelFormat::RGBA_F16:
89 return SkColorType::kRGBA_F16_SkColorType;
90 case PixelFormat::UNKNOWN:
91 case PixelFormat::ARGB_8888:
92 case PixelFormat::RGB_888:
93 case PixelFormat::NV21:
94 case PixelFormat::NV12:
95 case PixelFormat::CMYK:
96 default:
97 return SkColorType::kUnknown_SkColorType;
98 }
99 }
100
MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap> & pixmap)101 SkImageInfo MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap>& pixmap)
102 {
103 SkColorType colorType = PixelFormatToSkColorType(pixmap);
104 SkAlphaType alphaType = AlphaTypeToSkAlphaType(pixmap);
105 sk_sp<SkColorSpace> colorSpace = ColorSpaceToSkColorSpace(pixmap);
106 return SkImageInfo::Make(pixmap->GetWidth(), pixmap->GetHeight(), colorType, alphaType, colorSpace);
107 }
108
GetWindow(int32_t containerId)109 const OHOS::sptr<OHOS::Rosen::Window> GetWindow(int32_t containerId)
110 {
111 auto container = AceEngine::Get().GetContainer(containerId);
112 if (containerId >= MIN_SUBCONTAINER_ID && containerId < MIN_PLUGIN_SUBCONTAINER_ID) {
113 auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(
114 SubwindowManager::GetInstance()->GetParentContainerId(containerId));
115 CHECK_NULL_RETURN(subwindow, nullptr);
116 if (AceType::InstanceOf<SubwindowOhos>(subwindow)) {
117 auto subWindowOhos = AceType::DynamicCast<SubwindowOhos>(subwindow);
118 CHECK_NULL_RETURN(subWindowOhos, nullptr);
119 return subWindowOhos->GetSubWindow();
120 }
121 } else {
122 auto aceContainer = AceType::DynamicCast<Platform::AceContainer>(container);
123 if (aceContainer != nullptr) {
124 return OHOS::Rosen::Window::Find(aceContainer->GetWindowName());
125 }
126 return OHOS::Rosen::Window::GetTopWindowWithId(container->GetWindowId());
127 }
128 return nullptr;
129 }
130 } // namespace
131
132 static std::vector<std::string> inspectorAbilities = {"3DLayers"};
133 constexpr static char RECNODE_SELFID[] = "selfId";
134 constexpr static char RECNODE_NODEID[] = "nodeID";
135 constexpr static char RECNODE_PARENTID[] = "parentID";
136 constexpr static char RECNODE_NAME[] = "value";
137 constexpr static char RECNODE_DEBUGLINE[] = "debugLine";
138 constexpr static char RECNODE_CHILDREN[] = "RSNode";
139 constexpr static char ARK_DEBUGGER_LIB_PATH[] = "libark_connect_inspector.z.so";
140 static constexpr char START_PERFORMANCE_CHECK_MESSAGE[] = "StartArkPerformanceCheck";
141 static constexpr char END_PERFORMANCE_CHECK_MESSAGE[] = "EndArkPerformanceCheck";
142
143 bool LayoutInspector::stateProfilerStatus_ = false;
144 bool LayoutInspector::layoutInspectorStatus_ = false;
145 bool LayoutInspector::isUseStageModel_ = false;
146 std::mutex LayoutInspector::recMutex_;
147 ProfilerStatusCallback LayoutInspector::jsStateProfilerStatusCallback_ = nullptr;
148 RsProfilerNodeMountCallback LayoutInspector::rsProfilerNodeMountCallback_ = nullptr;
149 const char PNG_TAG[] = "png";
150 NG::InspectorTreeMap LayoutInspector::recNodeInfos_;
151 std::once_flag LayoutInspector::loadFlag;
152 void* LayoutInspector::handlerConnectServerSo = nullptr;
153 LayoutInspector::SetArkUICallback LayoutInspector::setArkUICallback = nullptr;
154
SupportInspector()155 void LayoutInspector::SupportInspector()
156 {
157 auto container = Container::Current();
158 CHECK_NULL_VOID(container);
159 if (!layoutInspectorStatus_) {
160 return;
161 }
162 std::string treeJsonStr;
163 GetInspectorTreeJsonStr(treeJsonStr, ContainerScope::CurrentId());
164 if (treeJsonStr.empty()) {
165 return;
166 }
167 auto message = JsonUtil::Create(true);
168 GetSnapshotJson(ContainerScope::CurrentId(), message);
169 CHECK_NULL_VOID(message);
170
171 auto sendTask = [treeJsonStr, jsonSnapshotStr = message->ToString(), container]() {
172 if (container->IsUseStageModel()) {
173 WebSocketManager::SendInspector(treeJsonStr, jsonSnapshotStr);
174 } else {
175 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
176 }
177 };
178 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendTask));
179 }
180
SetStatus(bool layoutInspectorStatus)181 void LayoutInspector::SetStatus(bool layoutInspectorStatus)
182 {
183 layoutInspectorStatus_ = layoutInspectorStatus;
184 }
185
TriggerJsStateProfilerStatusCallback(bool status)186 void LayoutInspector::TriggerJsStateProfilerStatusCallback(bool status)
187 {
188 if (jsStateProfilerStatusCallback_) {
189 stateProfilerStatus_ = status;
190 jsStateProfilerStatusCallback_(status);
191 }
192 }
193
SetJsStateProfilerStatusCallback(ProfilerStatusCallback && callback)194 void LayoutInspector::SetJsStateProfilerStatusCallback(ProfilerStatusCallback&& callback)
195 {
196 jsStateProfilerStatusCallback_ = callback;
197 }
198
GetStateProfilerStatus()199 bool LayoutInspector::GetStateProfilerStatus()
200 {
201 return stateProfilerStatus_;
202 }
203
GetRsProfilerNodeMountCallback()204 RsProfilerNodeMountCallback LayoutInspector::GetRsProfilerNodeMountCallback()
205 {
206 return rsProfilerNodeMountCallback_;
207 }
208
SetRsProfilerNodeMountCallback(RsProfilerNodeMountCallback && callback)209 void LayoutInspector::SetRsProfilerNodeMountCallback(RsProfilerNodeMountCallback&& callback)
210 {
211 rsProfilerNodeMountCallback_ = callback;
212 }
213
SendMessage(const std::string & message)214 void LayoutInspector::SendMessage(const std::string& message)
215 {
216 WebSocketManager::SendMessage(message);
217 }
218
SetStateProfilerStatus(bool status)219 void LayoutInspector::SetStateProfilerStatus(bool status)
220 {
221 auto taskExecutor = Container::CurrentTaskExecutorSafely();
222 CHECK_NULL_VOID(taskExecutor);
223 auto task = [status]() { LayoutInspector::TriggerJsStateProfilerStatusCallback(status); };
224 taskExecutor->PostTask(std::move(task), TaskExecutor::TaskType::UI, "ArkUISetStateProfilerStatus");
225 }
226
ConnectServerCallback()227 void LayoutInspector::ConnectServerCallback()
228 {
229 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "connect server callback isStage:%{public}d", isUseStageModel_);
230 if (isUseStageModel_) {
231 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "connect server, reset callback.");
232 WebSocketManager::SetRecordCallback(LayoutInspector::HandleStartRecord, LayoutInspector::HandleStopRecord);
233 }
234 }
235
SetCallback(int32_t instanceId)236 void LayoutInspector::SetCallback(int32_t instanceId)
237 {
238 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "InstanceId:%{public}d", instanceId);
239 auto container = AceEngine::Get().GetContainer(instanceId);
240 CHECK_NULL_VOID(container);
241 if (container->IsUseStageModel()) {
242 WebSocketManager::SetProfilerCallBack([](bool status) { return SetStateProfilerStatus(status); });
243 WebSocketManager::SetSwitchCallback(
244 [](int32_t containerId) { return CreateLayoutInfo(containerId); }, instanceId);
245 WebSocketManager::SetRecordCallback(LayoutInspector::HandleStartRecord, LayoutInspector::HandleStopRecord);
246 WebSocketManager::RegisterConnectServerCallback(LayoutInspector::ConnectServerCallback);
247 isUseStageModel_ = true;
248 RegisterConnectCallback();
249 } else {
250 OHOS::Ace::ConnectServerManager::Get().SetLayoutInspectorCallback(
251 [](int32_t containerId) { return CreateLayoutInfo(containerId); });
252 isUseStageModel_ = false;
253 }
254
255 SendInstanceMessageCallBack sendInstanceMessageCallBack = [](int32_t id) {
256 WebSocketManager::SetProfilerCallBack(
257 [](bool status) { return SetStateProfilerStatus(status); });
258 WebSocketManager::SetSwitchCallback([](int32_t containerId) { return CreateLayoutInfo(containerId); }, id);
259 };
260 WebSocketManager::RegisterSendInstanceMessageCallback(sendInstanceMessageCallBack);
261 }
262
CreateContainerLayoutInfo(RefPtr<Container> & container)263 void LayoutInspector::CreateContainerLayoutInfo(RefPtr<Container>& container)
264 {
265 CHECK_NULL_VOID(container);
266 if (container->IsDynamicRender()) {
267 container = Container::CurrentSafely();
268 CHECK_NULL_VOID(container);
269 }
270 int32_t containerId = container->GetInstanceId();
271 ContainerScope socpe(containerId);
272 auto context = PipelineContext::GetCurrentContext();
273 CHECK_NULL_VOID(context);
274 auto getInspectorTask = [container, containerId]() {
275 std::string treeJson;
276 GetInspectorTreeJsonStr(treeJson, containerId);
277 auto message = JsonUtil::Create(true);
278 GetSnapshotJson(containerId, message);
279 CHECK_NULL_VOID(message);
280 auto sendResultTask = [treeJsonStr = std::move(treeJson), jsonSnapshotStr = message->ToString(), container]() {
281 if (container->IsUseStageModel()) {
282 WebSocketManager::SendInspector(treeJsonStr, jsonSnapshotStr);
283 } else {
284 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
285 }
286 };
287 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask));
288 };
289 context->GetTaskExecutor()->PostTask(
290 std::move(getInspectorTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTreeJson");
291 }
292
CreateContainer3DLayoutInfo(RefPtr<Container> & container)293 void LayoutInspector::CreateContainer3DLayoutInfo(RefPtr<Container>& container)
294 {
295 CHECK_NULL_VOID(container);
296 if (container->IsDynamicRender()) {
297 container = Container::CurrentSafely();
298 CHECK_NULL_VOID(container);
299 }
300 int32_t containerId = container->GetInstanceId();
301 ContainerScope scope(containerId);
302 auto pipelineContext = container->GetPipelineContext();
303 CHECK_NULL_VOID(pipelineContext);
304 auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipelineContext);
305 CHECK_NULL_VOID(ngPipeline);
306 auto getInspectorTask = [containerId, weakPipeline = AceType::WeakClaim(AceType::RawPtr(ngPipeline))]() {
307 std::string treeJson;
308 GetInspectorTreeJsonStr(treeJson, containerId);
309 auto sendJsonTreeTask = [treeJsonStr = std::move(treeJson)]() {
310 WebSocketManager::SendMessage(treeJsonStr);
311 };
312 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendJsonTreeTask));
313
314 auto pipeline = weakPipeline.Upgrade();
315 CHECK_NULL_VOID(pipeline);
316 auto root = pipeline->GetRootElement();
317 CHECK_NULL_VOID(root);
318 Get3DSnapshotJson(root);
319 };
320 pipelineContext->GetTaskExecutor()->PostTask(
321 std::move(getInspectorTask), TaskExecutor::TaskType::UI, "ArkUIGetInspector3DTreeJson");
322 }
323
CreateLayoutInfo(int32_t containerId)324 void LayoutInspector::CreateLayoutInfo(int32_t containerId)
325 {
326 auto container = Container::GetFocused();
327 return CreateContainerLayoutInfo(container);
328 }
329
CreateLayoutInfoByWinId(uint32_t windId)330 void LayoutInspector::CreateLayoutInfoByWinId(uint32_t windId)
331 {
332 auto container = Container::GetByWindowId(windId);
333 if (container) {
334 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "start get container %{public}d info", container->GetInstanceId());
335 }
336 return CreateContainerLayoutInfo(container);
337 }
338
SendInspctorAbilities()339 void LayoutInspector::SendInspctorAbilities()
340 {
341 auto jsonRoot = JsonUtil::Create(true);
342 jsonRoot->Put("type", "inspectorAbilities");
343 auto contentArray = JsonUtil::CreateArray(true);
344 for (size_t i = 0; i < inspectorAbilities.size(); ++i) {
345 contentArray->Put(std::to_string(i).c_str(), inspectorAbilities[i].c_str());
346 }
347 jsonRoot->PutRef("content", std::move(contentArray));
348 auto sendInspctorAbilitiesTask = [abilitiesJsonStr = jsonRoot->ToString()]() {
349 WebSocketManager::SendMessage(abilitiesJsonStr);
350 };
351 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendInspctorAbilitiesTask));
352 }
353
Create3DLayoutInfoByWinId(uint32_t windId)354 void LayoutInspector::Create3DLayoutInfoByWinId(uint32_t windId)
355 {
356 auto container = Container::GetByWindowId(windId);
357 if (container) {
358 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "start get container %{public}d info", container->GetInstanceId());
359 }
360 return CreateContainer3DLayoutInfo(container);
361 }
362
GetInspectorTreeJsonStr(std::string & treeJsonStr,int32_t containerId)363 void LayoutInspector::GetInspectorTreeJsonStr(std::string& treeJsonStr, int32_t containerId)
364 {
365 auto container = AceEngine::Get().GetContainer(containerId);
366 CHECK_NULL_VOID(container);
367 #ifdef NG_BUILD
368 treeJsonStr = NG::Inspector::GetInspector(true);
369 #else
370 if (container->IsUseNewPipeline()) {
371 if (containerId >= MIN_SUBCONTAINER_ID && containerId < MIN_PLUGIN_SUBCONTAINER_ID) {
372 treeJsonStr = NG::Inspector::GetSubWindowInspector(true);
373 } else {
374 treeJsonStr = NG::Inspector::GetInspector(true);
375 }
376 } else {
377 auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
378 CHECK_NULL_VOID(pipelineContext);
379 treeJsonStr = V2::Inspector::GetInspectorTree(pipelineContext, true);
380 }
381 #endif
382 }
383
BuildInfoForIDE(uint64_t id,const std::shared_ptr<Media::PixelMap> & pixelMap,std::unique_ptr<JsonValue> & message)384 void LayoutInspector::BuildInfoForIDE(uint64_t id, const std::shared_ptr<Media::PixelMap>& pixelMap,
385 std::unique_ptr<JsonValue>& message)
386 {
387 CHECK_NULL_VOID(pixelMap);
388 auto acePixelMap = AceType::MakeRefPtr<PixelMapOhos>(pixelMap);
389 auto imageInfo = MakeSkImageInfoFromPixelMap(acePixelMap);
390 SkPixmap imagePixmap(
391 imageInfo, reinterpret_cast<const void*>(acePixelMap->GetPixels()), acePixelMap->GetRowBytes());
392 sk_sp<SkImage> image;
393 #ifdef USE_NEW_SKIA
394 image = SkImages::RasterFromPixmap(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap));
395 CHECK_NULL_VOID(image);
396 auto data = image->refEncodedData();
397 if (!data) {
398 data = SkPngEncoder::Encode(nullptr, image.get(), {});
399 }
400 #else
401 image = SkImage::MakeFromRaster(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap));
402 CHECK_NULL_VOID(image);
403 auto data = image->encodeToData(SkEncodedImageFormat::kPNG, PNG_ENCODE_QUALITY);
404 #endif
405 CHECK_NULL_VOID(data);
406 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
407 CHECK_NULL_VOID(defaultDisplay);
408 auto deviceDpi = defaultDisplay->GetDpi();
409 auto deviceWidth = defaultDisplay->GetWidth();
410 auto deviceHeight = defaultDisplay->GetHeight();
411 message->Put("$ID", RsNodeIdToFrameNodeId(id));
412 message->Put("format", PNG_TAG);
413 message->Put("width", (*pixelMap).GetWidth());
414 message->Put("height", (*pixelMap).GetHeight());
415 message->Put("deviceWidth", deviceWidth);
416 message->Put("deviceHeight", deviceHeight);
417 message->Put("deviceDpi", deviceDpi);
418 int32_t encodeLength = static_cast<int32_t>(SkBase64::Encode(data->data(), data->size(), nullptr));
419 message->Put("size", data->size());
420 SkString info(encodeLength);
421 #ifdef USE_NEW_SKIA
422 SkBase64::Encode(data->data(), data->size(), info.data());
423 #else
424 SkBase64::Encode(data->data(), data->size(), info.writable_str());
425 #endif
426 message->Put("pixelMapBase64", info.c_str());
427 }
428
RsNodeIdToFrameNodeId(uint64_t rsNodeId)429 int64_t LayoutInspector::RsNodeIdToFrameNodeId(uint64_t rsNodeId)
430 {
431 auto context = PipelineContext::GetCurrentContext();
432 auto rsUIContext = RsAdapter::GetRSUIContext(context);
433 auto rsNode = rsUIContext ? rsUIContext->GetNodeMap().GetNode(rsNodeId)
434 : Rosen::RSNodeMap::Instance().GetNode(rsNodeId);
435 if (rsNode == nullptr) {
436 return FIND_RSNODE_ERROR;
437 }
438 return rsNode->GetFrameNodeId();
439 }
440
Filter3DSnapshot(const std::vector<PixelMapPair> & snapinfos)441 std::vector<PixelMapPair> LayoutInspector::Filter3DSnapshot(const std::vector<PixelMapPair>& snapinfos)
442 {
443 std::vector<PixelMapPair> infos;
444 for (const auto& snapInfo : snapinfos) {
445 if (snapInfo.second) {
446 infos.emplace_back(snapInfo);
447 }
448 }
449 return infos;
450 }
451
SendEmpty3DSnapJson()452 void LayoutInspector::SendEmpty3DSnapJson()
453 {
454 TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "SendEmpty3DSnapJson");
455 auto message = JsonUtil::Create(true);
456 CHECK_NULL_VOID(message);
457 auto contentMessage = JsonUtil::CreateArray(true);
458 CHECK_NULL_VOID(contentMessage);
459 message->Put("type", "3DLayers");
460 message->Put("totalParts", 1);
461 message->Put("partNum", 1);
462 message->Put("LayersCount", 0);
463 message->PutRef("content", std::move(contentMessage));
464 auto sendTask = [jsonSnapshotStr = message->ToString()]() {
465 WebSocketManager::SendMessage(jsonSnapshotStr);
466 };
467 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendTask));
468 }
469
Get3DSnapshotJson(const RefPtr<NG::FrameNode> & node)470 void LayoutInspector::Get3DSnapshotJson(const RefPtr<NG::FrameNode>& node)
471 {
472 std::vector<PixelMapPair> snapInfos = NG::ComponentSnapshot::GetSoloNode(node);
473 TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "3d snapInfos size:%{public}zu", snapInfos.size());
474 auto filterSnapInfos = Filter3DSnapshot(snapInfos);
475 TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "3d snapInfos after filter size:%{public}zu", filterSnapInfos.size());
476 if (filterSnapInfos.empty()) {
477 SendEmpty3DSnapJson();
478 return;
479 }
480 size_t totalParts = (filterSnapInfos.size() + SNAP_PARTITION_SIZE - 1) / SNAP_PARTITION_SIZE;
481 int partNum = 1;
482 for (size_t i = 0; i < filterSnapInfos.size(); i += SNAP_PARTITION_SIZE) {
483 auto message = JsonUtil::Create(true);
484 CHECK_NULL_VOID(message);
485 auto contentMessage = JsonUtil::CreateArray(true);
486 CHECK_NULL_VOID(contentMessage);
487 message->Put("type", "3DLayers");
488 message->Put("totalParts", totalParts);
489 message->Put("partNum", partNum++);
490 message->Put("LayersCount", filterSnapInfos.size());
491
492 for (size_t j = i; j < i + SNAP_PARTITION_SIZE && j < filterSnapInfos.size(); j++) {
493 auto snapInfo = filterSnapInfos[j];
494 auto snapPixelMap = snapInfo.second;
495 if (snapPixelMap) {
496 auto snapInfoJson = JsonUtil::Create(true);
497 BuildInfoForIDE(snapInfo.first, snapPixelMap, snapInfoJson);
498 contentMessage->PutRef(std::move(snapInfoJson));
499 }
500 }
501
502 message->PutRef("content", std::move(contentMessage));
503 auto sendTask = [jsonSnapshotStr = message->ToString()]() {
504 WebSocketManager::SendMessage(jsonSnapshotStr);
505 };
506 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendTask));
507 }
508 }
509
GetSnapshotJson(int32_t containerId,std::unique_ptr<JsonValue> & message)510 void LayoutInspector::GetSnapshotJson(int32_t containerId, std::unique_ptr<JsonValue>& message)
511 {
512 auto container = AceEngine::Get().GetContainer(containerId);
513 CHECK_NULL_VOID(container);
514 OHOS::sptr<OHOS::Rosen::Window> window = GetWindow(containerId);
515 CHECK_NULL_VOID(window);
516 auto pixelMap = window->Snapshot();
517 CHECK_NULL_VOID(pixelMap);
518 auto acePixelMap = AceType::MakeRefPtr<PixelMapOhos>(pixelMap);
519 CHECK_NULL_VOID(acePixelMap);
520 auto imageInfo = MakeSkImageInfoFromPixelMap(acePixelMap);
521 SkPixmap imagePixmap(
522 imageInfo, reinterpret_cast<const void*>(acePixelMap->GetPixels()), acePixelMap->GetRowBytes());
523 sk_sp<SkImage> image;
524 #ifdef USE_NEW_SKIA
525 image = SkImages::RasterFromPixmap(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap));
526 CHECK_NULL_VOID(image);
527 auto data = image->refEncodedData();
528 if (!data) {
529 data = SkPngEncoder::Encode(nullptr, image.get(), {});
530 }
531 #else
532 image = SkImage::MakeFromRaster(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap));
533 CHECK_NULL_VOID(image);
534 auto data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
535 #endif
536 CHECK_NULL_VOID(data);
537 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
538 CHECK_NULL_VOID(defaultDisplay);
539 auto deviceDpi = defaultDisplay->GetDpi();
540 auto deviceWidth = defaultDisplay->GetWidth();
541 auto deviceHeight = defaultDisplay->GetHeight();
542 message->Put("type", "snapShot");
543 message->Put("format", PNG_TAG);
544 message->Put("width", (*pixelMap).GetWidth());
545 message->Put("height", (*pixelMap).GetHeight());
546 message->Put("posX", container->GetViewPosX());
547 message->Put("posY", container->GetViewPosY());
548 message->Put("deviceWidth", deviceWidth);
549 message->Put("deviceHeight", deviceHeight);
550 message->Put("deviceDpi", deviceDpi);
551 int32_t encodeLength = static_cast<int32_t>(SkBase64::Encode(data->data(), data->size(), nullptr));
552 message->Put("size", data->size());
553 SkString info(encodeLength);
554 #ifdef USE_NEW_SKIA
555 SkBase64::Encode(data->data(), data->size(), info.data());
556 #else
557 SkBase64::Encode(data->data(), data->size(), info.writable_str());
558 #endif
559 message->Put("pixelMapBase64", info.c_str());
560 }
561
RegisterConnectCallback()562 void LayoutInspector::RegisterConnectCallback()
563 {
564 std::call_once(loadFlag, []() {
565 handlerConnectServerSo = dlopen(ARK_DEBUGGER_LIB_PATH, RTLD_NOLOAD | RTLD_NOW);
566 if (handlerConnectServerSo == nullptr) {
567 handlerConnectServerSo = dlopen(ARK_DEBUGGER_LIB_PATH, RTLD_NOW);
568 if (handlerConnectServerSo == nullptr) {
569 TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "null handlerConnectServerSo: %{public}s", dlerror());
570 return;
571 }
572 }
573
574 setArkUICallback = reinterpret_cast<SetArkUICallback>(dlsym(handlerConnectServerSo, "SetArkUICallback"));
575 if (setArkUICallback == nullptr) {
576 TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "null setArkUICallback: %{public}s", dlerror());
577 return;
578 }
579 });
580
581 if (setArkUICallback != nullptr) {
582 setArkUICallback([](const char* message) { ProcessMessages(message); });
583 }
584 }
585
ProcessMessages(const std::string & message)586 std::pair<uint32_t, int32_t> LayoutInspector::ProcessMessages(const std::string& message)
587 {
588 if (message.find(START_PERFORMANCE_CHECK_MESSAGE, 0) != std::string::npos) {
589 TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "performance check start");
590 AceChecker::SetPerformanceCheckStatus(true, message);
591 } else if (message.find(END_PERFORMANCE_CHECK_MESSAGE, 0) != std::string::npos) {
592 TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "performance check end");
593 AceChecker::SetPerformanceCheckStatus(false, message);
594 }
595 auto windowResult = NG::Inspector::ParseWindowIdFromMsg(message);
596 uint32_t windowId = windowResult.first;
597 if (windowId == OHOS::Ace::NG::INVALID_WINDOW_ID && windowResult.second != QUERY_ABILITY) {
598 TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "input message: %{public}s", message.c_str());
599 return windowResult;
600 }
601
602 switch (windowResult.second) {
603 case UI_TREE:
604 CreateLayoutInfoByWinId(windowId);
605 break;
606 case THREE_DIMENSIONS_TREE:
607 Create3DLayoutInfoByWinId(windowId);
608 break;
609 case QUERY_ABILITY:
610 SendInspctorAbilities();
611 break;
612 default:
613 TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "unsupport message: %{public}s", message.c_str());
614 break;
615 }
616 return windowResult;
617 }
618
HandleStopRecord()619 void LayoutInspector::HandleStopRecord()
620 {
621 std::unique_lock<std::mutex> lock(recMutex_);
622 SetRsProfilerNodeMountCallback(nullptr);
623 auto jsonRoot = JsonUtil::Create(true);
624 auto jsonNodeArray = JsonUtil::CreateArray(true);
625 for (auto& uiNode : recNodeInfos_) {
626 if (uiNode.second != nullptr) {
627 auto jsonNode = JsonUtil::Create(true);
628 jsonNode->Put(RECNODE_NODEID, std::to_string(uiNode.second->GetSelfId()).c_str());
629 jsonNode->Put(RECNODE_PARENTID, uiNode.second->GetParentId());
630 jsonNode->Put(RECNODE_SELFID, uiNode.second->GetNodeId());
631 jsonNode->Put(RECNODE_NAME, uiNode.second->GetName().c_str());
632 jsonNode->Put(RECNODE_DEBUGLINE, uiNode.second->GetDebugLine().c_str());
633 jsonNodeArray->PutRef(std::move(jsonNode));
634 }
635 }
636 recNodeInfos_.clear();
637 lock.unlock();
638 if (jsonNodeArray->GetArraySize()) {
639 jsonRoot->PutRef(RECNODE_CHILDREN, std::move(jsonNodeArray));
640 }
641 std::string arrayJsonStr = jsonRoot->ToString();
642 auto sendResultTask = [arrayJsonStr]() {
643 WebSocketManager::SetRecordResults(arrayJsonStr);
644 };
645 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask));
646 }
647
HandleStartRecord()648 void LayoutInspector::HandleStartRecord()
649 {
650 // regist inner callback function
651 std::unique_lock<std::mutex> lock(recMutex_);
652 SetRsProfilerNodeMountCallback(LayoutInspector::HandleInnerCallback);
653 lock.unlock();
654 auto container = Container::GetFocused();
655 CHECK_NULL_VOID(container);
656 if (container->IsDynamicRender()) {
657 container = Container::CurrentSafely();
658 CHECK_NULL_VOID(container);
659 }
660 auto containerId = container->GetInstanceId();
661 ContainerScope socpe(containerId);
662 auto context = PipelineContext::GetCurrentContext();
663 CHECK_NULL_VOID(context);
664 auto startRecordTask = []() {
665 std::lock_guard<std::mutex> lock(LayoutInspector::recMutex_);
666 NG::InspectorTreeMap recTreeNodes;
667 NG::InspectorTreeMap offScreenTreeNodes;
668 NG::Inspector::GetRecordAllPagesNodes(recTreeNodes);
669 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get nodes size:%{public}zu", recTreeNodes.size());
670 NG::Inspector::GetOffScreenTreeNodes(offScreenTreeNodes);
671 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get offscreen nodes size:%{public}zu", offScreenTreeNodes.size());
672 LayoutInspector::recNodeInfos_.swap(recTreeNodes);
673 for (auto& item : offScreenTreeNodes) {
674 recNodeInfos_.emplace(item);
675 }
676 };
677 context->GetTaskExecutor()->PostTask(
678 std::move(startRecordTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTree");
679 }
680
HandleInnerCallback(FrameNodeInfo node)681 void LayoutInspector::HandleInnerCallback(FrameNodeInfo node)
682 {
683 // convert FrameNodeInfo --> recNode
684 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR,
685 "FrameNodeInfo:selfid:%{public}" PRIu64 ",nodid:%{public}d,type:%{public}s,debugline:%{public}s",
686 node.rsNodeId, node.frameNodeId, node.nodeType.c_str(), node.debugline.c_str());
687 auto recNode = AceType::MakeRefPtr<NG::RecNode>();
688 CHECK_NULL_VOID(recNode);
689 recNode->SetSelfId(node.rsNodeId);
690 recNode->SetNodeId(node.frameNodeId);
691 recNode->SetName(node.nodeType);
692 recNode->SetDebugLine(node.debugline);
693 recNode->SetParentId(node.parentNodeId);
694 std::lock_guard<std::mutex> lock(recMutex_);
695 recNodeInfos_.emplace(node.rsNodeId, recNode);
696 }
697 } // namespace OHOS::Ace
698