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 46namespace OHOS::Ace { 47 48namespace { 49 50sk_sp<SkColorSpace> ColorSpaceToSkColorSpace(const RefPtr<PixelMap>& pixmap) 51{ 52 return SkColorSpace::MakeSRGB(); 53} 54 55SkAlphaType 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 71SkColorType 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 95SkImageInfo 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 103const 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 126constexpr static char RECNODE_SELFID[] = "selfId"; 127constexpr static char RECNODE_NODEID[] = "nodeID"; 128constexpr static char RECNODE_NAME[] = "value"; 129constexpr static char RECNODE_DEBUGLINE[] = "debugLine"; 130constexpr static char RECNODE_CHILDREN[] = "RSNode"; 131 132bool LayoutInspector::stateProfilerStatus_ = false; 133bool LayoutInspector::layoutInspectorStatus_ = false; 134std::mutex LayoutInspector::recMutex_; 135ProfilerStatusCallback LayoutInspector::jsStateProfilerStatusCallback_ = nullptr; 136RsProfilerNodeMountCallback LayoutInspector::rsProfilerNodeMountCallback_ = nullptr; 137const char PNG_TAG[] = "png"; 138NG::InspectorTreeMap LayoutInspector::recNodeInfos_; 139 140void LayoutInspector::SupportInspector() 141{ 142 auto container = Container::Current(); 143 CHECK_NULL_VOID(container); 144 if (!layoutInspectorStatus_) { 145 return; 146 } 147 std::string treeJsonStr; 148 GetInspectorTreeJsonStr(treeJsonStr, ContainerScope::CurrentId()); 149 if (treeJsonStr.empty()) { 150 return; 151 } 152 auto message = JsonUtil::Create(true); 153 GetSnapshotJson(ContainerScope::CurrentId(), message); 154 CHECK_NULL_VOID(message); 155 156 auto sendTask = [treeJsonStr, jsonSnapshotStr = message->ToString(), container]() { 157 if (container->IsUseStageModel()) { 158 OHOS::AbilityRuntime::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr); 159 } else { 160 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr); 161 } 162 }; 163 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendTask)); 164} 165 166void LayoutInspector::SetStatus(bool layoutInspectorStatus) 167{ 168 layoutInspectorStatus_ = layoutInspectorStatus; 169} 170 171void LayoutInspector::TriggerJsStateProfilerStatusCallback(bool status) 172{ 173 if (jsStateProfilerStatusCallback_) { 174 stateProfilerStatus_ = status; 175 jsStateProfilerStatusCallback_(status); 176 } 177} 178 179void LayoutInspector::SetJsStateProfilerStatusCallback(ProfilerStatusCallback&& callback) 180{ 181 jsStateProfilerStatusCallback_ = callback; 182} 183 184bool LayoutInspector::GetStateProfilerStatus() 185{ 186 return stateProfilerStatus_; 187} 188 189RsProfilerNodeMountCallback LayoutInspector::GetRsProfilerNodeMountCallback() 190{ 191 return rsProfilerNodeMountCallback_; 192} 193 194void LayoutInspector::SetRsProfilerNodeMountCallback(RsProfilerNodeMountCallback&& callback) 195{ 196 rsProfilerNodeMountCallback_ = callback; 197} 198 199void LayoutInspector::SendStateProfilerMessage(const std::string& message) 200{ 201 OHOS::AbilityRuntime::ConnectServerManager::Get().SendArkUIStateProfilerMessage(message); 202} 203 204void LayoutInspector::SetStateProfilerStatus(bool status) 205{ 206 auto taskExecutor = Container::CurrentTaskExecutorSafely(); 207 CHECK_NULL_VOID(taskExecutor); 208 auto task = [status]() { LayoutInspector::TriggerJsStateProfilerStatusCallback(status); }; 209 taskExecutor->PostTask(std::move(task), TaskExecutor::TaskType::UI, "ArkUISetStateProfilerStatus"); 210} 211 212void LayoutInspector::SetCallback(int32_t instanceId) 213{ 214 auto container = AceEngine::Get().GetContainer(instanceId); 215 CHECK_NULL_VOID(container); 216 if (container->IsUseStageModel()) { 217 OHOS::AbilityRuntime::ConnectServerManager::Get().SetLayoutInspectorCallback( 218 [](int32_t containerId) { return CreateLayoutInfo(containerId); }, 219 [](bool status) { return SetStatus(status); }); 220 OHOS::AbilityRuntime::ConnectServerManager::Get().SetRecordCallback( 221 LayoutInspector::HandleStartRecord, LayoutInspector::HandleStopRecord); 222 } else { 223 OHOS::Ace::ConnectServerManager::Get().SetLayoutInspectorCallback( 224 [](int32_t containerId) { return CreateLayoutInfo(containerId); }, 225 [](bool status) { return SetStatus(status); }); 226 } 227 228 OHOS::AbilityRuntime::ConnectServerManager::Get().SetStateProfilerCallback( 229 [](bool status) { return SetStateProfilerStatus(status); }); 230} 231 232void LayoutInspector::CreateLayoutInfo(int32_t containerId) 233{ 234 auto container = Container::GetFoucsed(); 235 CHECK_NULL_VOID(container); 236 if (container->IsDynamicRender()) { 237 container = Container::CurrentSafely(); 238 CHECK_NULL_VOID(container); 239 } 240 containerId = container->GetInstanceId(); 241 ContainerScope socpe(containerId); 242 auto context = PipelineContext::GetCurrentContext(); 243 CHECK_NULL_VOID(context); 244 auto getInspectorTask = [container, containerId]() { 245 std::string treeJson; 246 GetInspectorTreeJsonStr(treeJson, containerId); 247 auto message = JsonUtil::Create(true); 248 GetSnapshotJson(containerId, message); 249 CHECK_NULL_VOID(message); 250 auto sendResultTask = [treeJsonStr = std::move(treeJson), jsonSnapshotStr = message->ToString(), container]() { 251 if (container->IsUseStageModel()) { 252 OHOS::AbilityRuntime::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr); 253 } else { 254 OHOS::Ace::ConnectServerManager::Get().SendInspector(treeJsonStr, jsonSnapshotStr); 255 } 256 }; 257 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask)); 258 }; 259 context->GetTaskExecutor()->PostTask( 260 std::move(getInspectorTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTreeJson"); 261} 262 263void LayoutInspector::GetInspectorTreeJsonStr(std::string& treeJsonStr, int32_t containerId) 264{ 265 auto container = AceEngine::Get().GetContainer(containerId); 266 CHECK_NULL_VOID(container); 267#ifdef NG_BUILD 268 treeJsonStr = NG::Inspector::GetInspector(true); 269#else 270 if (container->IsUseNewPipeline()) { 271 if (containerId >= MIN_SUBCONTAINER_ID && containerId < MIN_PLUGIN_SUBCONTAINER_ID) { 272 treeJsonStr = NG::Inspector::GetSubWindowInspector(true); 273 } else { 274 treeJsonStr = NG::Inspector::GetInspector(true); 275 } 276 } else { 277 auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext()); 278 CHECK_NULL_VOID(pipelineContext); 279 treeJsonStr = V2::Inspector::GetInspectorTree(pipelineContext, true); 280 } 281#endif 282} 283 284void LayoutInspector::GetSnapshotJson(int32_t containerId, std::unique_ptr<JsonValue>& message) 285{ 286 auto container = AceEngine::Get().GetContainer(containerId); 287 CHECK_NULL_VOID(container); 288 OHOS::sptr<OHOS::Rosen::Window> window = GetWindow(containerId); 289 CHECK_NULL_VOID(window); 290 auto pixelMap = window->Snapshot(); 291 CHECK_NULL_VOID(pixelMap); 292 auto acePixelMap = AceType::MakeRefPtr<PixelMapOhos>(pixelMap); 293 CHECK_NULL_VOID(acePixelMap); 294 auto imageInfo = MakeSkImageInfoFromPixelMap(acePixelMap); 295 SkPixmap imagePixmap( 296 imageInfo, reinterpret_cast<const void*>(acePixelMap->GetPixels()), acePixelMap->GetRowBytes()); 297 sk_sp<SkImage> image; 298 image = SkImage::MakeFromRaster(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(acePixelMap)); 299 CHECK_NULL_VOID(image); 300 auto data = image->encodeToData(SkEncodedImageFormat::kPNG, 100); 301 CHECK_NULL_VOID(data); 302 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay(); 303 CHECK_NULL_VOID(defaultDisplay); 304 auto deviceDpi = defaultDisplay->GetDpi(); 305 auto deviceWidth = defaultDisplay->GetWidth(); 306 auto deviceHeight = defaultDisplay->GetHeight(); 307 message->Put("type", "snapShot"); 308 message->Put("format", PNG_TAG); 309 message->Put("width", (*pixelMap).GetWidth()); 310 message->Put("height", (*pixelMap).GetHeight()); 311 message->Put("posX", container->GetViewPosX()); 312 message->Put("posY", container->GetViewPosY()); 313 message->Put("deviceWidth", deviceWidth); 314 message->Put("deviceHeight", deviceHeight); 315 message->Put("deviceDpi", deviceDpi); 316 int32_t encodeLength = static_cast<int32_t>(SkBase64::Encode(data->data(), data->size(), nullptr)); 317 message->Put("size", data->size()); 318 SkString info(encodeLength); 319 SkBase64::Encode(data->data(), data->size(), info.writable_str()); 320 message->Put("pixelMapBase64", info.c_str()); 321} 322 323void LayoutInspector::HandleStopRecord() 324{ 325 std::unique_lock<std::mutex> lock(recMutex_); 326 SetRsProfilerNodeMountCallback(nullptr); 327 auto jsonRoot = JsonUtil::Create(true); 328 auto jsonNodeArray = JsonUtil::CreateArray(true); 329 for (auto& uiNode : recNodeInfos_) { 330 if (uiNode.second != nullptr) { 331 auto jsonNode = JsonUtil::Create(true); 332 jsonNode->Put(RECNODE_NODEID, std::to_string(uiNode.second->GetSelfId()).c_str()); 333 jsonNode->Put(RECNODE_SELFID, uiNode.second->GetNodeId()); 334 jsonNode->Put(RECNODE_NAME, uiNode.second->GetName().c_str()); 335 jsonNode->Put(RECNODE_DEBUGLINE, uiNode.second->GetDebugLine().c_str()); 336 jsonNodeArray->PutRef(std::move(jsonNode)); 337 } 338 } 339 recNodeInfos_.clear(); 340 lock.unlock(); 341 if (jsonNodeArray->GetArraySize()) { 342 jsonRoot->PutRef(RECNODE_CHILDREN, std::move(jsonNodeArray)); 343 } 344 std::string arrayJsonStr = jsonRoot->ToString(); 345 auto sendResultTask = [arrayJsonStr]() { 346 OHOS::AbilityRuntime::ConnectServerManager::Get().SetRecordResults(arrayJsonStr); 347 }; 348 BackgroundTaskExecutor::GetInstance().PostTask(std::move(sendResultTask)); 349} 350 351void LayoutInspector::HandleStartRecord() 352{ 353 // regist inner callback function 354 std::unique_lock<std::mutex> lock(recMutex_); 355 SetRsProfilerNodeMountCallback(LayoutInspector::HandleInnerCallback); 356 lock.unlock(); 357 auto container = Container::GetFoucsed(); 358 CHECK_NULL_VOID(container); 359 if (container->IsDynamicRender()) { 360 container = Container::CurrentSafely(); 361 CHECK_NULL_VOID(container); 362 } 363 auto containerId = container->GetInstanceId(); 364 ContainerScope socpe(containerId); 365 auto context = PipelineContext::GetCurrentContext(); 366 CHECK_NULL_VOID(context); 367 auto startRecordTask = []() { 368 std::lock_guard<std::mutex> lock(LayoutInspector::recMutex_); 369 NG::InspectorTreeMap recTreeNodes; 370 NG::InspectorTreeMap offScreenTreeNodes; 371 NG::Inspector::GetInspectorTree(recTreeNodes); 372 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get nodes size:%{public}zu", recTreeNodes.size()); 373 NG::Inspector::GetOffScreenTreeNodes(offScreenTreeNodes); 374 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, "Get offscreen nodes size:%{public}zu", offScreenTreeNodes.size()); 375 LayoutInspector::recNodeInfos_.swap(recTreeNodes); 376 for (auto& item : offScreenTreeNodes) { 377 recNodeInfos_.emplace(item); 378 } 379 }; 380 context->GetTaskExecutor()->PostTask( 381 std::move(startRecordTask), TaskExecutor::TaskType::UI, "ArkUIGetInspectorTree"); 382} 383 384void LayoutInspector::HandleInnerCallback(FrameNodeInfo node) 385{ 386 // convert FrameNodeInfo --> recNode 387 TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR, 388 "FrameNodeInfo:selfid:%{public}" PRIu64 ",nodid:%{public}d,type:%{public}s,debugline:%{public}s", 389 node.rsNodeId, node.frameNodeId, node.nodeType.c_str(), node.debugline.c_str()); 390 auto recNode = AceType::MakeRefPtr<NG::RecNode>(); 391 CHECK_NULL_VOID(recNode); 392 recNode->SetSelfId(node.rsNodeId); 393 recNode->SetNodeId(node.frameNodeId); 394 recNode->SetName(node.nodeType); 395 recNode->SetDebugLine(node.debugline); 396 std::lock_guard<std::mutex> lock(recMutex_); 397 recNodeInfos_.emplace(node.rsNodeId, recNode); 398} 399} // namespace OHOS::Ace 400