1e0dac50fSopenharmony_ci/* 2e0dac50fSopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd. 3e0dac50fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4e0dac50fSopenharmony_ci * you may not use this file except in compliance with the License. 5e0dac50fSopenharmony_ci * You may obtain a copy of the License at 6e0dac50fSopenharmony_ci * 7e0dac50fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8e0dac50fSopenharmony_ci * 9e0dac50fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10e0dac50fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11e0dac50fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12e0dac50fSopenharmony_ci * See the License for the specific language governing permissions and 13e0dac50fSopenharmony_ci * limitations under the License. 14e0dac50fSopenharmony_ci */ 15e0dac50fSopenharmony_ci 16e0dac50fSopenharmony_ci#include "perform_reporter.h" 17e0dac50fSopenharmony_ci 18e0dac50fSopenharmony_ci#include <hisysevent.h> 19e0dac50fSopenharmony_ci 20e0dac50fSopenharmony_ci#include "window_manager_hilog.h" 21e0dac50fSopenharmony_ci 22e0dac50fSopenharmony_cinamespace OHOS { 23e0dac50fSopenharmony_cinamespace Rosen { 24e0dac50fSopenharmony_cinamespace { 25e0dac50fSopenharmony_ciconstexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "PerformReporter"}; 26e0dac50fSopenharmony_ci} 27e0dac50fSopenharmony_ciWM_IMPLEMENT_SINGLE_INSTANCE(WindowInfoReporter) 28e0dac50fSopenharmony_ci 29e0dac50fSopenharmony_ciconstexpr char EVENT_KEY_BUNDLE_NAME[] = "BUNDLE_NAME"; 30e0dac50fSopenharmony_ciconstexpr char EVENT_KEY_WINDOW_NAME[] = "WINDOW_NAME"; 31e0dac50fSopenharmony_ciconstexpr char EVENT_KEY_MISSION_ID[] = "MISSION_ID"; 32e0dac50fSopenharmony_ciconstexpr char EVENT_KEY_TIMESTAMP[] = "TIMESTAMP"; 33e0dac50fSopenharmony_ci 34e0dac50fSopenharmony_ci/** 35e0dac50fSopenharmony_ci * @brief Construct a new Perform Reporter:: Perform Reporter object 36e0dac50fSopenharmony_ci * 37e0dac50fSopenharmony_ci * @param tag A tag that in report string 38e0dac50fSopenharmony_ci * @param timeSpiltsMs The time-interval that data statistic, details look up the comments in function body 39e0dac50fSopenharmony_ci * @param reportInterval Report data after reportInterval round start-end 40e0dac50fSopenharmony_ci */ 41e0dac50fSopenharmony_ciPerformReporter::PerformReporter(const std::string& tag, 42e0dac50fSopenharmony_ci const std::vector<int64_t>& timeSpiltsMs, uint32_t reportInterval) 43e0dac50fSopenharmony_ci : tag_(tag), reportInterval_(reportInterval) 44e0dac50fSopenharmony_ci{ 45e0dac50fSopenharmony_ci // re-organ data struct 46e0dac50fSopenharmony_ci // a, b, c, d --> 47e0dac50fSopenharmony_ci // (0, a] : cnt=0, (a, b] : cnt=0, (b, c] : cnt=0, (c, d] : cnt=0 48e0dac50fSopenharmony_ci for (auto split : timeSpiltsMs) { 49e0dac50fSopenharmony_ci timeSplitCount_[split] = 0; 50e0dac50fSopenharmony_ci } 51e0dac50fSopenharmony_ci // (d, +limit] : cnt=0 52e0dac50fSopenharmony_ci timeSplitCount_[BARRIER] = 0; 53e0dac50fSopenharmony_ci totalCount_ = 0; 54e0dac50fSopenharmony_ci} 55e0dac50fSopenharmony_ci 56e0dac50fSopenharmony_civoid PerformReporter::start() 57e0dac50fSopenharmony_ci{ 58e0dac50fSopenharmony_ci startTime_ = std::chrono::steady_clock::now(); 59e0dac50fSopenharmony_ci} 60e0dac50fSopenharmony_ci 61e0dac50fSopenharmony_civoid PerformReporter::end() 62e0dac50fSopenharmony_ci{ 63e0dac50fSopenharmony_ci auto currentTime = std::chrono::steady_clock::now(); 64e0dac50fSopenharmony_ci int64_t costTime = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime_).count(); 65e0dac50fSopenharmony_ci 66e0dac50fSopenharmony_ci count(costTime); 67e0dac50fSopenharmony_ci 68e0dac50fSopenharmony_ci bool repSucc = report(); 69e0dac50fSopenharmony_ci if (repSucc) { 70e0dac50fSopenharmony_ci clear(); 71e0dac50fSopenharmony_ci } 72e0dac50fSopenharmony_ci} 73e0dac50fSopenharmony_ci 74e0dac50fSopenharmony_cibool PerformReporter::report() 75e0dac50fSopenharmony_ci{ 76e0dac50fSopenharmony_ci if (totalCount_ < reportInterval_) { 77e0dac50fSopenharmony_ci return false; 78e0dac50fSopenharmony_ci } 79e0dac50fSopenharmony_ci 80e0dac50fSopenharmony_ci std::ostringstream oss; 81e0dac50fSopenharmony_ci oss << tag_ << ": "; 82e0dac50fSopenharmony_ci auto maxSplit = 0; 83e0dac50fSopenharmony_ci for (const auto& iter: timeSplitCount_) { 84e0dac50fSopenharmony_ci if (iter.first != BARRIER) { 85e0dac50fSopenharmony_ci oss << "BELLOW" << iter.first << "(ms): " << iter.second << ", "; 86e0dac50fSopenharmony_ci maxSplit = iter.first; 87e0dac50fSopenharmony_ci } 88e0dac50fSopenharmony_ci } 89e0dac50fSopenharmony_ci oss << "ABOVE" << maxSplit << "(ms): " << timeSplitCount_[BARRIER]; 90e0dac50fSopenharmony_ci 91e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 92e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, tag_, 93e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "MSG", oss.str()); 94e0dac50fSopenharmony_ci WLOGI("Write HiSysEvent ret:%{public}d", ret); 95e0dac50fSopenharmony_ci return ret == 0; 96e0dac50fSopenharmony_ci} 97e0dac50fSopenharmony_ci 98e0dac50fSopenharmony_civoid PerformReporter::count(int64_t costTime) 99e0dac50fSopenharmony_ci{ 100e0dac50fSopenharmony_ci totalCount_++; 101e0dac50fSopenharmony_ci for (auto& iter: timeSplitCount_) { 102e0dac50fSopenharmony_ci if (costTime <= iter.first) { 103e0dac50fSopenharmony_ci iter.second++; 104e0dac50fSopenharmony_ci break; 105e0dac50fSopenharmony_ci } 106e0dac50fSopenharmony_ci } 107e0dac50fSopenharmony_ci 108e0dac50fSopenharmony_ci std::ostringstream oss; 109e0dac50fSopenharmony_ci oss << tag_ << " cost " << costTime << "ms, total count " << totalCount_; 110e0dac50fSopenharmony_ci WLOGI("%{public}s", oss.str().c_str()); 111e0dac50fSopenharmony_ci} 112e0dac50fSopenharmony_ci 113e0dac50fSopenharmony_civoid PerformReporter::clear() 114e0dac50fSopenharmony_ci{ 115e0dac50fSopenharmony_ci totalCount_ = 0; 116e0dac50fSopenharmony_ci for (auto& iter: timeSplitCount_) { 117e0dac50fSopenharmony_ci iter.second = 0; 118e0dac50fSopenharmony_ci } 119e0dac50fSopenharmony_ci} 120e0dac50fSopenharmony_ci 121e0dac50fSopenharmony_cistd::string WindowInfoReporter::GetMsgString(const FullInfoMap& infoMap) 122e0dac50fSopenharmony_ci{ 123e0dac50fSopenharmony_ci if (infoMap.empty()) { 124e0dac50fSopenharmony_ci return ""; 125e0dac50fSopenharmony_ci } 126e0dac50fSopenharmony_ci std::ostringstream oss; 127e0dac50fSopenharmony_ci oss << "{"; 128e0dac50fSopenharmony_ci for (auto& bundleInfos : infoMap) { 129e0dac50fSopenharmony_ci if (bundleInfos.second.empty()) { 130e0dac50fSopenharmony_ci continue; 131e0dac50fSopenharmony_ci } 132e0dac50fSopenharmony_ci oss << "{"; 133e0dac50fSopenharmony_ci for (auto& packageInfo : bundleInfos.second) { 134e0dac50fSopenharmony_ci oss << "BUNDLE_NAME:" << bundleInfos.first << ","; 135e0dac50fSopenharmony_ci oss << "ABILITY_NAME:" << packageInfo.first << ","; 136e0dac50fSopenharmony_ci oss << "COUNT:" << packageInfo.second; 137e0dac50fSopenharmony_ci } 138e0dac50fSopenharmony_ci oss << "},"; 139e0dac50fSopenharmony_ci } 140e0dac50fSopenharmony_ci oss << "};"; 141e0dac50fSopenharmony_ci return oss.str(); 142e0dac50fSopenharmony_ci} 143e0dac50fSopenharmony_ci 144e0dac50fSopenharmony_cistd::string WindowInfoReporter::GetMsgString(const BundleNameMap& infoMap) 145e0dac50fSopenharmony_ci{ 146e0dac50fSopenharmony_ci if (infoMap.empty()) { 147e0dac50fSopenharmony_ci return ""; 148e0dac50fSopenharmony_ci } 149e0dac50fSopenharmony_ci std::ostringstream oss; 150e0dac50fSopenharmony_ci oss << "{"; 151e0dac50fSopenharmony_ci for (auto& bundleInfo : infoMap) { 152e0dac50fSopenharmony_ci oss << "{"; 153e0dac50fSopenharmony_ci oss << "BUNDLE_NAME:" << bundleInfo.first << ","; 154e0dac50fSopenharmony_ci oss << "COUNT:" << bundleInfo.second; 155e0dac50fSopenharmony_ci oss << "},"; 156e0dac50fSopenharmony_ci } 157e0dac50fSopenharmony_ci oss << "};"; 158e0dac50fSopenharmony_ci return oss.str(); 159e0dac50fSopenharmony_ci} 160e0dac50fSopenharmony_ci 161e0dac50fSopenharmony_civoid WindowInfoReporter::InsertCreateReportInfo(const std::string& bundleName) 162e0dac50fSopenharmony_ci{ 163e0dac50fSopenharmony_ci UpdateReportInfo(windowCreateReportInfos_, bundleName); 164e0dac50fSopenharmony_ci} 165e0dac50fSopenharmony_ci 166e0dac50fSopenharmony_civoid WindowInfoReporter::InsertShowReportInfo(const std::string& bundleName) 167e0dac50fSopenharmony_ci{ 168e0dac50fSopenharmony_ci UpdateReportInfo(windowShowReportInfos_, bundleName); 169e0dac50fSopenharmony_ci} 170e0dac50fSopenharmony_ci 171e0dac50fSopenharmony_civoid WindowInfoReporter::InsertHideReportInfo(const std::string& bundleName) 172e0dac50fSopenharmony_ci{ 173e0dac50fSopenharmony_ci UpdateReportInfo(windowHideReportInfos_, bundleName); 174e0dac50fSopenharmony_ci} 175e0dac50fSopenharmony_ci 176e0dac50fSopenharmony_civoid WindowInfoReporter::InsertDestroyReportInfo(const std::string& bundleName) 177e0dac50fSopenharmony_ci{ 178e0dac50fSopenharmony_ci UpdateReportInfo(windowDestoryReportInfos_, bundleName); 179e0dac50fSopenharmony_ci} 180e0dac50fSopenharmony_ci 181e0dac50fSopenharmony_civoid WindowInfoReporter::InsertNavigationBarReportInfo(const std::string& bundleName, const std::string& packageName) 182e0dac50fSopenharmony_ci{ 183e0dac50fSopenharmony_ci UpdateReportInfo(windowNavigationBarReportInfos_, bundleName, packageName); 184e0dac50fSopenharmony_ci} 185e0dac50fSopenharmony_ci 186e0dac50fSopenharmony_civoid WindowInfoReporter::UpdateReportInfo(FullInfoMap& infoMap, 187e0dac50fSopenharmony_ci const std::string& bundleName, const std::string& packageName) 188e0dac50fSopenharmony_ci{ 189e0dac50fSopenharmony_ci if (bundleName.empty() || packageName.empty()) { 190e0dac50fSopenharmony_ci return; 191e0dac50fSopenharmony_ci } 192e0dac50fSopenharmony_ci std::lock_guard<std::mutex> lock(mtx_); 193e0dac50fSopenharmony_ci auto iter = infoMap.find(bundleName); 194e0dac50fSopenharmony_ci if (iter == infoMap.end()) { 195e0dac50fSopenharmony_ci std::map<std::string, uint32_t> infos; 196e0dac50fSopenharmony_ci infos.insert(std::make_pair(packageName, 1)); 197e0dac50fSopenharmony_ci infoMap.insert(std::make_pair(bundleName, infos)); 198e0dac50fSopenharmony_ci return; 199e0dac50fSopenharmony_ci } 200e0dac50fSopenharmony_ci 201e0dac50fSopenharmony_ci auto countPairIter = iter->second.find(packageName); 202e0dac50fSopenharmony_ci if (countPairIter == iter->second.end()) { 203e0dac50fSopenharmony_ci iter->second.insert(std::make_pair(packageName, 1)); 204e0dac50fSopenharmony_ci return; 205e0dac50fSopenharmony_ci } 206e0dac50fSopenharmony_ci infoMap[bundleName][packageName]++; 207e0dac50fSopenharmony_ci} 208e0dac50fSopenharmony_ci 209e0dac50fSopenharmony_civoid WindowInfoReporter::UpdateReportInfo(BundleNameMap& infoMap, const std::string& bundleName) 210e0dac50fSopenharmony_ci{ 211e0dac50fSopenharmony_ci if (bundleName.empty()) { 212e0dac50fSopenharmony_ci return; 213e0dac50fSopenharmony_ci } 214e0dac50fSopenharmony_ci std::lock_guard<std::mutex> lock(mtx_); 215e0dac50fSopenharmony_ci auto iter = infoMap.find(bundleName); 216e0dac50fSopenharmony_ci if (iter == infoMap.end()) { 217e0dac50fSopenharmony_ci infoMap.insert(std::make_pair(bundleName, 1)); 218e0dac50fSopenharmony_ci return; 219e0dac50fSopenharmony_ci } 220e0dac50fSopenharmony_ci infoMap[bundleName]++; 221e0dac50fSopenharmony_ci} 222e0dac50fSopenharmony_ci 223e0dac50fSopenharmony_civoid WindowInfoReporter::ReportBackButtonInfoImmediately() 224e0dac50fSopenharmony_ci{ 225e0dac50fSopenharmony_ci Report("WM_REPORT_BACK_KEYEVENT", "Click Back Button"); 226e0dac50fSopenharmony_ci} 227e0dac50fSopenharmony_ci 228e0dac50fSopenharmony_civoid WindowInfoReporter::ReportZeroOpacityInfoImmediately(const std::string& bundleName, const std::string& packageName) 229e0dac50fSopenharmony_ci{ 230e0dac50fSopenharmony_ci if (bundleName.empty()) { 231e0dac50fSopenharmony_ci return; 232e0dac50fSopenharmony_ci } 233e0dac50fSopenharmony_ci std::ostringstream oss; 234e0dac50fSopenharmony_ci oss << "{ PROCESS_NAME:" << bundleName.c_str() << ", PACKAGE_NAME:" << "" << packageName.c_str() << " }"; 235e0dac50fSopenharmony_ci Report("WM_REPORT_WINDOW_OPACITY_ZERO", oss.str()); 236e0dac50fSopenharmony_ci} 237e0dac50fSopenharmony_ci 238e0dac50fSopenharmony_civoid WindowInfoReporter::ReportStartWindow(const std::string& bundleName, const std::string& windowName) 239e0dac50fSopenharmony_ci{ 240e0dac50fSopenharmony_ci std::string eventName = "START_WINDOW"; 241e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 242e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName, 243e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR, 244e0dac50fSopenharmony_ci EVENT_KEY_BUNDLE_NAME, bundleName, 245e0dac50fSopenharmony_ci EVENT_KEY_WINDOW_NAME, windowName); 246e0dac50fSopenharmony_ci if (ret != 0) { 247e0dac50fSopenharmony_ci WLOGFE("Write HiSysEvent error, ret:%{public}d", ret); 248e0dac50fSopenharmony_ci } 249e0dac50fSopenharmony_ci} 250e0dac50fSopenharmony_ci 251e0dac50fSopenharmony_civoid WindowInfoReporter::ReportRecordedInfos() 252e0dac50fSopenharmony_ci{ 253e0dac50fSopenharmony_ci std::lock_guard<std::mutex> lock(mtx_); 254e0dac50fSopenharmony_ci WLOGFD("----Report HiSysEvent write all-----"); 255e0dac50fSopenharmony_ci Report("WM_REPORT_WINDOW_CREATE", GetMsgString(windowCreateReportInfos_)); 256e0dac50fSopenharmony_ci Report("WM_REPORT_WINDOW_SHOW", GetMsgString(windowShowReportInfos_)); 257e0dac50fSopenharmony_ci Report("WM_REPORT_WINDOW_HIDE", GetMsgString(windowHideReportInfos_)); 258e0dac50fSopenharmony_ci Report("WM_REPORT_WINDOW_DESTORY", GetMsgString(windowDestoryReportInfos_)); 259e0dac50fSopenharmony_ci Report("WM_REPORT_HIDE_NAVIGATIONBAR", GetMsgString(windowNavigationBarReportInfos_)); 260e0dac50fSopenharmony_ci ClearRecordedInfos(); 261e0dac50fSopenharmony_ci} 262e0dac50fSopenharmony_ci 263e0dac50fSopenharmony_civoid WindowInfoReporter::ReportContainerStartBegin(int32_t missionId, const std::string& bundleName, int64_t timestamp) 264e0dac50fSopenharmony_ci{ 265e0dac50fSopenharmony_ci std::string eventName = "CONTAINER_START_BEGIN"; 266e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 267e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName, 268e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR, 269e0dac50fSopenharmony_ci EVENT_KEY_MISSION_ID, missionId, 270e0dac50fSopenharmony_ci EVENT_KEY_BUNDLE_NAME, bundleName, 271e0dac50fSopenharmony_ci EVENT_KEY_TIMESTAMP, timestamp); 272e0dac50fSopenharmony_ci if (ret != 0) { 273e0dac50fSopenharmony_ci WLOGFE("Write HiSysEvent error, ret:%{public}d", ret); 274e0dac50fSopenharmony_ci } 275e0dac50fSopenharmony_ci} 276e0dac50fSopenharmony_ci 277e0dac50fSopenharmony_civoid WindowInfoReporter::Report(const std::string& reportTag, const std::string& msg) 278e0dac50fSopenharmony_ci{ 279e0dac50fSopenharmony_ci if (msg.empty()) { 280e0dac50fSopenharmony_ci return; 281e0dac50fSopenharmony_ci } 282e0dac50fSopenharmony_ci WLOGFD("Report Tag : [%{public}s], Msg: %{public}s", reportTag.c_str(), msg.c_str()); 283e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 284e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, reportTag, 285e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "MSG", msg); 286e0dac50fSopenharmony_ci if (ret != 0) { 287e0dac50fSopenharmony_ci WLOGFE("Write HiSysEvent error, ret:%{public}d", ret); 288e0dac50fSopenharmony_ci } 289e0dac50fSopenharmony_ci} 290e0dac50fSopenharmony_ci 291e0dac50fSopenharmony_civoid WindowInfoReporter::ClearRecordedInfos() 292e0dac50fSopenharmony_ci{ 293e0dac50fSopenharmony_ci WLOGFD("Clear all hiSysEvent write information"); 294e0dac50fSopenharmony_ci windowCreateReportInfos_.clear(); 295e0dac50fSopenharmony_ci windowShowReportInfos_.clear(); 296e0dac50fSopenharmony_ci windowHideReportInfos_.clear(); 297e0dac50fSopenharmony_ci windowDestoryReportInfos_.clear(); 298e0dac50fSopenharmony_ci windowNavigationBarReportInfos_.clear(); 299e0dac50fSopenharmony_ci} 300e0dac50fSopenharmony_ci 301e0dac50fSopenharmony_ciint32_t WindowInfoReporter::ReportWindowProfileInfo(const WindowProfileInfo& windowProfileInfo) 302e0dac50fSopenharmony_ci{ 303e0dac50fSopenharmony_ci std::string eventName = "WINDOW_PROFILE_INFORMATION"; 304e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 305e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName, 306e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, 307e0dac50fSopenharmony_ci "BUNDLE_NAME", windowProfileInfo.bundleName, 308e0dac50fSopenharmony_ci "WINDOW_VISIBLE_STATE", windowProfileInfo.windowVisibleState, 309e0dac50fSopenharmony_ci "WINDOW_LOCATED_SCREEN", windowProfileInfo.windowLocatedScreen, 310e0dac50fSopenharmony_ci "WINDOW_SCENE_MODE", windowProfileInfo.windowSceneMode); 311e0dac50fSopenharmony_ci if (ret != 0) { 312e0dac50fSopenharmony_ci WLOGFE("Write HiSysEvent error, ret:%{public}d", ret); 313e0dac50fSopenharmony_ci } 314e0dac50fSopenharmony_ci return ret; 315e0dac50fSopenharmony_ci} 316e0dac50fSopenharmony_ci 317e0dac50fSopenharmony_civoid WindowInfoReporter::ReportWindowException(int32_t detectionType, int32_t pid, const std::string& windowInfo) 318e0dac50fSopenharmony_ci{ 319e0dac50fSopenharmony_ci std::string eventName = "WINDOW_EXCEPTION_DETECTION"; 320e0dac50fSopenharmony_ci int32_t ret = HiSysEventWrite( 321e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName, 322e0dac50fSopenharmony_ci OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, 323e0dac50fSopenharmony_ci "DETECTION_TYPE", detectionType, 324e0dac50fSopenharmony_ci "PID", pid, 325e0dac50fSopenharmony_ci "MSG", windowInfo); 326e0dac50fSopenharmony_ci if (ret != 0) { 327e0dac50fSopenharmony_ci WLOGFE("Write HiSysEvent error, ret:%{public}d", ret); 328e0dac50fSopenharmony_ci } 329e0dac50fSopenharmony_ci} 330e0dac50fSopenharmony_ci} 331e0dac50fSopenharmony_ci}