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 #include <mutex>
19 #include <string>
20
21 #include "include/core/SkImage.h"
22 #include "include/core/SkString.h"
23 #include "include/core/SkColorSpace.h"
24 #include "include/utils/SkBase64.h"
25
26 #include "wm/window.h"
27
28 #include "adapter/ohos/osal/pixel_map_ohos.h"
29 #include "adapter/ohos/entrance/ace_container.h"
30 #include "adapter/ohos/entrance/subwindow/subwindow_ohos.h"
31 #include "base/subwindow/subwindow_manager.h"
32 #include "base/thread/background_task_executor.h"
33 #include "base/utils/utils.h"
34 #include "base/json/json_util.h"
35 #include "base/utils/system_properties.h"
36 #include "core/common/ace_engine.h"
37 #include "core/common/connect_server_manager.h"
38 #include "core/common/container.h"
39 #include "core/common/container_scope.h"
40 #include "core/components_ng/base/inspector.h"
41 #include "core/components_v2/inspector/inspector.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43 #include "dm/display_manager.h"
44 #include "foundation/ability/ability_runtime/frameworks/native/runtime/connect_server_manager.h"
45
46 namespace OHOS::Ace {
47
48 namespace {
49
ColorSpaceToSkColorSpace(const RefPtr<PixelMap> & pixmap)50 sk_sp<SkColorSpace> ColorSpaceToSkColorSpace(const RefPtr<PixelMap>& pixmap)
51 {
52 return SkColorSpace::MakeSRGB();
53 }
54
AlphaTypeToSkAlphaType(const RefPtr<PixelMap> & pixmap)55 SkAlphaType AlphaTypeToSkAlphaType(const RefPtr<PixelMap>& pixmap)
56 {
57 switch (pixmap->GetAlphaType()) {
58 case AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
59 return SkAlphaType::kUnknown_SkAlphaType;
60 case AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
61 return SkAlphaType::kOpaque_SkAlphaType;
62 case AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
63 return SkAlphaType::kPremul_SkAlphaType;
64 case AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
65 return SkAlphaType::kUnpremul_SkAlphaType;
66 default:
67 return SkAlphaType::kUnknown_SkAlphaType;
68 }
69 }
70
PixelFormatToSkColorType(const RefPtr<PixelMap> & pixmap)71 SkColorType PixelFormatToSkColorType(const RefPtr<PixelMap>& pixmap)
72 {
73 switch (pixmap->GetPixelFormat()) {
74 case PixelFormat::RGB_565:
75 return SkColorType::kRGB_565_SkColorType;
76 case PixelFormat::RGBA_8888:
77 return SkColorType::kRGBA_8888_SkColorType;
78 case PixelFormat::BGRA_8888:
79 return SkColorType::kBGRA_8888_SkColorType;
80 case PixelFormat::ALPHA_8:
81 return SkColorType::kAlpha_8_SkColorType;
82 case PixelFormat::RGBA_F16:
83 return SkColorType::kRGBA_F16_SkColorType;
84 case PixelFormat::UNKNOWN:
85 case PixelFormat::ARGB_8888:
86 case PixelFormat::RGB_888:
87 case PixelFormat::NV21:
88 case PixelFormat::NV12:
89 case PixelFormat::CMYK:
90 default:
91 return SkColorType::kUnknown_SkColorType;
92 }
93 }
94
MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap> & pixmap)95 SkImageInfo MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap>& pixmap)
96 {
97 SkColorType colorType = PixelFormatToSkColorType(pixmap);
98 SkAlphaType alphaType = AlphaTypeToSkAlphaType(pixmap);
99 sk_sp<SkColorSpace> colorSpace = ColorSpaceToSkColorSpace(pixmap);
100 return SkImageInfo::Make(pixmap->GetWidth(), pixmap->GetHeight(), colorType, alphaType, colorSpace);
101 }
102
GetWindow(int32_t containerId)103 const OHOS::sptr<OHOS::Rosen::Window> GetWindow(int32_t containerId)
104 {
105 auto container = AceEngine::Get().GetContainer(containerId);
106 if (containerId >= MIN_SUBCONTAINER_ID && containerId < MIN_PLUGIN_SUBCONTAINER_ID) {
107 auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(
108 SubwindowManager::GetInstance()->GetParentContainerId(containerId));
109 CHECK_NULL_RETURN(subwindow, nullptr);
110 if (AceType::InstanceOf<SubwindowOhos>(subwindow)) {
111 auto subWindowOhos = AceType::DynamicCast<SubwindowOhos>(subwindow);
112 CHECK_NULL_RETURN(subWindowOhos, nullptr);
113 return subWindowOhos->GetSubWindow();
114 }
115 } else {
116 auto aceContainer = AceType::DynamicCast<Platform::AceContainer>(container);
117 if (aceContainer != nullptr) {
118 return OHOS::Rosen::Window::Find(aceContainer->GetWindowName());
119 }
120 return OHOS::Rosen::Window::GetTopWindowWithId(container->GetWindowId());
121 }
122 return nullptr;
123 }
124 } // namespace
125
126 constexpr static char RECNODE_SELFID[] = "selfId";
127 constexpr static char RECNODE_NODEID[] = "nodeID";
128 constexpr static char RECNODE_NAME[] = "value";
129 constexpr static char RECNODE_DEBUGLINE[] = "debugLine";
130 constexpr static char RECNODE_CHILDREN[] = "RSNode";
131
132 bool LayoutInspector::stateProfilerStatus_ = false;
133 bool LayoutInspector::layoutInspectorStatus_ = false;
134 bool LayoutInspector::isUseStageModel_ = false;
135 std::mutex LayoutInspector::recMutex_;
136 ProfilerStatusCallback LayoutInspector::jsStateProfilerStatusCallback_ = nullptr;
137 RsProfilerNodeMountCallback LayoutInspector::rsProfilerNodeMountCallback_ = nullptr;
138 const char PNG_TAG[] = "png";
139 NG::InspectorTreeMap LayoutInspector::recNodeInfos_;
140
SupportInspector()141 void LayoutInspector::SupportInspector()
142 {
143 auto container = Container::Current();
144 CHECK_NULL_VOID(container);
145 if (!layoutInspectorStatus_) {
146 return;
147 }
148 std::string treeJsonStr;
149 GetInspectorTreeJsonStr(treeJsonStr, ContainerScope::CurrentId());
150 if (treeJsonStr.empty()) {
151 return;
152 }
153 auto message = JsonUtil::Create(true);
154 GetSnapshotJson(ContainerScope::CurrentId(), message);
155 CHECK_NULL_VOID(message);
156
157 auto sendTask = [treeJsonStr, jsonSnapshotStr = message->ToString(), container]() {
158 if (container->IsUseStageModel()) {
159 OHOS::AbilityRuntime::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
160 } else {
161 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
162 }
163 };
164 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendTask));
165 }
166
SetStatus(bool layoutInspectorStatus)167 void LayoutInspector::SetStatus(bool layoutInspectorStatus)
168 {
169 layoutInspectorStatus_ = layoutInspectorStatus;
170 }
171
TriggerJsStateProfilerStatusCallback(bool status)172 void LayoutInspector::TriggerJsStateProfilerStatusCallback(bool status)
173 {
174 if (jsStateProfilerStatusCallback_) {
175 stateProfilerStatus_ = status;
176 jsStateProfilerStatusCallback_(status);
177 }
178 }
179
SetJsStateProfilerStatusCallback(ProfilerStatusCallback && callback)180 void LayoutInspector::SetJsStateProfilerStatusCallback(ProfilerStatusCallback&& callback)
181 {
182 jsStateProfilerStatusCallback_ = callback;
183 }
184
GetStateProfilerStatus()185 bool LayoutInspector::GetStateProfilerStatus()
186 {
187 return stateProfilerStatus_;
188 }
189
GetRsProfilerNodeMountCallback()190 RsProfilerNodeMountCallback LayoutInspector::GetRsProfilerNodeMountCallback()
191 {
192 return rsProfilerNodeMountCallback_;
193 }
194
SetRsProfilerNodeMountCallback(RsProfilerNodeMountCallback && callback)195 void LayoutInspector::SetRsProfilerNodeMountCallback(RsProfilerNodeMountCallback&& callback)
196 {
197 rsProfilerNodeMountCallback_ = callback;
198 }
199
SendStateProfilerMessage(const std::string & message)200 void LayoutInspector::SendStateProfilerMessage(const std::string& message)
201 {
202 OHOS::AbilityRuntime::ConnectServerManager::Get().SendArkUIStateProfilerMessage(message);
203 }
204
SetStateProfilerStatus(bool status)205 void LayoutInspector::SetStateProfilerStatus(bool status)
206 {
207 auto taskExecutor = Container::CurrentTaskExecutorSafely();
208 CHECK_NULL_VOID(taskExecutor);
209 auto task = [status]() { LayoutInspector::TriggerJsStateProfilerStatusCallback(status); };
210 taskExecutor->PostTask(std::move(task), TaskExecutor::TaskType::UI, "ArkUISetStateProfilerStatus");
211 }
212
ConnectServerCallback()213 void LayoutInspector::ConnectServerCallback()
214 {
215 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "connect server callback isStage:%{public}d", isUseStageModel_);
216 if (isUseStageModel_) {
217 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "connect server, reset callback.");
218 OHOS::AbilityRuntime::ConnectServerManager::Get().SetRecordCallback(
219 LayoutInspector::HandleStartRecord, LayoutInspector::HandleStopRecord);
220 }
221 }
222
SetCallback(int32_t instanceId)223 void LayoutInspector::SetCallback(int32_t instanceId)
224 {
225 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "InstanceId:%{public}d", instanceId);
226 auto container = AceEngine::Get().GetContainer(instanceId);
227 CHECK_NULL_VOID(container);
228 if (container->IsUseStageModel()) {
229 OHOS::AbilityRuntime::ConnectServerManager::Get().SetLayoutInspectorCallback(
230 [](int32_t containerId) { return CreateLayoutInfo(containerId); },
231 [](bool status) { return SetStatus(status); });
232 OHOS::AbilityRuntime::ConnectServerManager::Get().SetRecordCallback(
233 LayoutInspector::HandleStartRecord, LayoutInspector::HandleStopRecord);
234 OHOS::AbilityRuntime::ConnectServerManager::Get().RegistConnectServerCallback(
235 LayoutInspector::ConnectServerCallback);
236 isUseStageModel_ = true;
237 } else {
238 OHOS::Ace::ConnectServerManager::Get().SetLayoutInspectorCallback(
239 [](int32_t containerId) { return CreateLayoutInfo(containerId); },
240 [](bool status) { return SetStatus(status); });
241 isUseStageModel_ = false;
242 }
243
244 OHOS::AbilityRuntime::ConnectServerManager::Get().SetStateProfilerCallback(
245 [](bool status) { return SetStateProfilerStatus(status); });
246 }
247
CreateLayoutInfo(int32_t containerId)248 void LayoutInspector::CreateLayoutInfo(int32_t containerId)
249 {
250 auto container = Container::GetFoucsed();
251 CHECK_NULL_VOID(container);
252 if (container->IsDynamicRender()) {
253 container = Container::CurrentSafely();
254 CHECK_NULL_VOID(container);
255 }
256 containerId = container->GetInstanceId();
257 ContainerScope socpe(containerId);
258 auto context = PipelineContext::GetCurrentContext();
259 CHECK_NULL_VOID(context);
260 auto getInspectorTask = [container, containerId]() {
261 std::string treeJson;
262 GetInspectorTreeJsonStr(treeJson, containerId);
263 auto message = JsonUtil::Create(true);
264 GetSnapshotJson(containerId, message);
265 CHECK_NULL_VOID(message);
266 auto sendResultTask = [treeJsonStr = std::move(treeJson), jsonSnapshotStr = message->ToString(), container]() {
267 if (container->IsUseStageModel()) {
268 OHOS::AbilityRuntime::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
269 } else {
270 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr);
271 }
272 };
273 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask));
274 };
275 context->GetTaskExecutor()->PostTask(
276 std::move(getInspectorTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTreeJson");
277 }
278
GetInspectorTreeJsonStr(std::string & treeJsonStr,int32_t containerId)279 void LayoutInspector::GetInspectorTreeJsonStr(std::string& treeJsonStr, int32_t containerId)
280 {
281 auto container = AceEngine::Get().GetContainer(containerId);
282 CHECK_NULL_VOID(container);
283 #ifdef NG_BUILD
284 treeJsonStr = NG::Inspector::GetInspector(true);
285 #else
286 if (container->IsUseNewPipeline()) {
287 if (containerId >= MIN_SUBCONTAINER_ID && containerId < MIN_PLUGIN_SUBCONTAINER_ID) {
288 treeJsonStr = NG::Inspector::GetSubWindowInspector(true);
289 } else {
290 treeJsonStr = NG::Inspector::GetInspector(true);
291 }
292 } else {
293 auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
294 CHECK_NULL_VOID(pipelineContext);
295 treeJsonStr = V2::Inspector::GetInspectorTree(pipelineContext, true);
296 }
297 #endif
298 }
299
GetSnapshotJson(int32_t containerId,std::unique_ptr<JsonValue> & message)300 void LayoutInspector::GetSnapshotJson(int32_t containerId, std::unique_ptr<JsonValue>& message)
301 {
302 auto container = AceEngine::Get().GetContainer(containerId);
303 CHECK_NULL_VOID(container);
304 OHOS::sptr<OHOS::Rosen::Window> window = GetWindow(containerId);
305 CHECK_NULL_VOID(window);
306 auto pixelMap = window->Snapshot();
307 CHECK_NULL_VOID(pixelMap);
308 auto acePixelMap = AceType::MakeRefPtr<PixelMapOhos>(pixelMap);
309 CHECK_NULL_VOID(acePixelMap);
310 auto imageInfo = MakeSkImageInfoFromPixelMap(acePixelMap);
311 SkPixmap imagePixmap(
312 imageInfo, reinterpret_cast<const void*>(acePixelMap->GetPixels()), acePixelMap->GetRowBytes());
313 sk_sp<SkImage> image;
314 image = SkImage::MakeFromRaster(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap));
315 CHECK_NULL_VOID(image);
316 auto data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
317 CHECK_NULL_VOID(data);
318 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
319 CHECK_NULL_VOID(defaultDisplay);
320 auto deviceDpi = defaultDisplay->GetDpi();
321 auto deviceWidth = defaultDisplay->GetWidth();
322 auto deviceHeight = defaultDisplay->GetHeight();
323 message->Put("type", "snapShot");
324 message->Put("format", PNG_TAG);
325 message->Put("width", (*pixelMap).GetWidth());
326 message->Put("height", (*pixelMap).GetHeight());
327 message->Put("posX", container->GetViewPosX());
328 message->Put("posY", container->GetViewPosY());
329 message->Put("deviceWidth", deviceWidth);
330 message->Put("deviceHeight", deviceHeight);
331 message->Put("deviceDpi", deviceDpi);
332 int32_t encodeLength = static_cast<int32_t>(SkBase64::Encode(data->data(), data->size(), nullptr));
333 message->Put("size", data->size());
334 SkString info(encodeLength);
335 SkBase64::Encode(data->data(), data->size(), info.writable_str());
336 message->Put("pixelMapBase64", info.c_str());
337 }
338
HandleStopRecord()339 void LayoutInspector::HandleStopRecord()
340 {
341 std::unique_lock<std::mutex> lock(recMutex_);
342 SetRsProfilerNodeMountCallback(nullptr);
343 auto jsonRoot = JsonUtil::Create(true);
344 auto jsonNodeArray = JsonUtil::CreateArray(true);
345 for (auto& uiNode : recNodeInfos_) {
346 if (uiNode.second != nullptr) {
347 auto jsonNode = JsonUtil::Create(true);
348 jsonNode->Put(RECNODE_NODEID, std::to_string(uiNode.second->GetSelfId()).c_str());
349 jsonNode->Put(RECNODE_SELFID, uiNode.second->GetNodeId());
350 jsonNode->Put(RECNODE_NAME, uiNode.second->GetName().c_str());
351 jsonNode->Put(RECNODE_DEBUGLINE, uiNode.second->GetDebugLine().c_str());
352 jsonNodeArray->PutRef(std::move(jsonNode));
353 }
354 }
355 recNodeInfos_.clear();
356 lock.unlock();
357 if (jsonNodeArray->GetArraySize()) {
358 jsonRoot->PutRef(RECNODE_CHILDREN, std::move(jsonNodeArray));
359 }
360 std::string arrayJsonStr = jsonRoot->ToString();
361 auto sendResultTask = [arrayJsonStr]() {
362 OHOS::AbilityRuntime::ConnectServerManager::Get().SetRecordResults(arrayJsonStr);
363 };
364 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask));
365 }
366
HandleStartRecord()367 void LayoutInspector::HandleStartRecord()
368 {
369 // regist inner callback function
370 std::unique_lock<std::mutex> lock(recMutex_);
371 SetRsProfilerNodeMountCallback(LayoutInspector::HandleInnerCallback);
372 lock.unlock();
373 auto container = Container::GetFoucsed();
374 CHECK_NULL_VOID(container);
375 if (container->IsDynamicRender()) {
376 container = Container::CurrentSafely();
377 CHECK_NULL_VOID(container);
378 }
379 auto containerId = container->GetInstanceId();
380 ContainerScope socpe(containerId);
381 auto context = PipelineContext::GetCurrentContext();
382 CHECK_NULL_VOID(context);
383 auto startRecordTask = []() {
384 std::lock_guard<std::mutex> lock(LayoutInspector::recMutex_);
385 NG::InspectorTreeMap recTreeNodes;
386 NG::InspectorTreeMap offScreenTreeNodes;
387 NG::Inspector::GetRecordAllPagesNodes(recTreeNodes);
388 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get nodes size:%{public}zu", recTreeNodes.size());
389 NG::Inspector::GetOffScreenTreeNodes(offScreenTreeNodes);
390 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get offscreen nodes size:%{public}zu", offScreenTreeNodes.size());
391 LayoutInspector::recNodeInfos_.swap(recTreeNodes);
392 for (auto& item : offScreenTreeNodes) {
393 recNodeInfos_.emplace(item);
394 }
395 };
396 context->GetTaskExecutor()->PostTask(
397 std::move(startRecordTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTree");
398 }
399
HandleInnerCallback(FrameNodeInfo node)400 void LayoutInspector::HandleInnerCallback(FrameNodeInfo node)
401 {
402 // convert FrameNodeInfo --> recNode
403 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR,
404 "FrameNodeInfo:selfid:%{public}" PRIu64 ",nodid:%{public}d,type:%{public}s,debugline:%{public}s",
405 node.rsNodeId, node.frameNodeId, node.nodeType.c_str(), node.debugline.c_str());
406 auto recNode = AceType::MakeRefPtr<NG::RecNode>();
407 CHECK_NULL_VOID(recNode);
408 recNode->SetSelfId(node.rsNodeId);
409 recNode->SetNodeId(node.frameNodeId);
410 recNode->SetName(node.nodeType);
411 recNode->SetDebugLine(node.debugline);
412 std::lock_guard<std::mutex> lock(recMutex_);
413 recNodeInfos_.emplace(node.rsNodeId, recNode);
414 }
415 } // namespace OHOS::Ace
416