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