* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "perform_reporter.h"
#include <hisysevent.h>
#include <iomanip>
#include "parameters.h"
#include "window_manager_hilog.h"
namespace OHOS {
namespace Rosen {
namespace {
constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "PerformReporter"};
const char* WINDOW_RECT_CHECK_NAME = "Window rect check";
const char* WINDOW_ZORDER_CHECK_NAME = "Window z-order check";
const char* WINDOW_FOCUS_CHECK_NAME = "Window focus check";
const char* WINDOW_TRANSPARENT_CHECK_NAME = "Window transparent check";
const char* WINDOW_MODAL_UIEXTENSION_UICONTENT_CHECK_NAME = "Window modal uiextension uicontent check";
const char* WINDOW_MODAL_UIEXTENSION_SUBWINDOW_CHECK_NAME = "Window modal uiextension subwindow check";
const char* WINDOW_UIEXTENSION_TRANSFER_DATA_FAIL_NAME = "Window uiextension transfer data fail";
const char* WINDOW_UIEXTENSION_START_ABILITY_FAIL_NAME = "Window uiextension start ability fail";
const char* WINDOW_FLUSH_EMPTY_DISPLAY_INFO_TO_MMI_EXCEPTION_NAME = "Window flush empty display info to mmi exception";
const char* WINDOW_CREATE_SUB_WINDOW_FAILED_NAME = "Window create sub window failed";
const char* WINDOW_UNEXPECTED_EVENT_CHECK_NAME = "Window unexpected event check";
const char* WINDOW_UICONTENT_TIMEOUT_CHECK_NAME = "Window UIContent timeout check";
const char* WINDOW_UIEXT_DESTROY_TIMEOUT_CHECK_NAME = "Window UIExt destroy timeout check";
const char* WINDOW_KEYBOARD_ANIM_TIMEOUT_CHECK_NAME = "Window keyboard anim timeout check";
const char* WINDOW_WALLPAPER_ZORDER_CHECK_NAME = "Window wallpaper z-order check";
const std::map<WindowDFXHelperType, const char*> WM_CHECK_TYPE_TO_CHECK_NAME_MAP {
{WindowDFXHelperType::WINDOW_RECT_CHECK, WINDOW_RECT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_ZORDER_CHECK, WINDOW_ZORDER_CHECK_NAME },
{WindowDFXHelperType::WINDOW_FOCUS_CHECK, WINDOW_FOCUS_CHECK_NAME },
{WindowDFXHelperType::WINDOW_TRANSPARENT_CHECK, WINDOW_TRANSPARENT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_MODAL_UIEXTENSION_UICONTENT_CHECK, WINDOW_MODAL_UIEXTENSION_UICONTENT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_MODAL_UIEXTENSION_SUBWINDOW_CHECK, WINDOW_MODAL_UIEXTENSION_SUBWINDOW_CHECK_NAME },
{WindowDFXHelperType::WINDOW_UIEXTENSION_TRANSFER_DATA_FAIL, WINDOW_UIEXTENSION_TRANSFER_DATA_FAIL_NAME },
{WindowDFXHelperType::WINDOW_UIEXTENSION_START_ABILITY_FAIL, WINDOW_UIEXTENSION_START_ABILITY_FAIL_NAME },
{WindowDFXHelperType::WINDOW_FLUSH_EMPTY_DISPLAY_INFO_TO_MMI_EXCEPTION,
WINDOW_FLUSH_EMPTY_DISPLAY_INFO_TO_MMI_EXCEPTION_NAME },
{WindowDFXHelperType::WINDOW_CREATE_SUB_WINDOW_FAILED, WINDOW_CREATE_SUB_WINDOW_FAILED_NAME },
{WindowDFXHelperType::WINDOW_UNEXPECTED_EVENT_CHECK, WINDOW_UNEXPECTED_EVENT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_UICONTENT_TIMEOUT_CHECK, WINDOW_UICONTENT_TIMEOUT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_UIEXT_DESTROY_TIMEOUT_CHECK, WINDOW_UIEXT_DESTROY_TIMEOUT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_KEYBOARD_ANIM_TIMEOUT_CHECK, WINDOW_KEYBOARD_ANIM_TIMEOUT_CHECK_NAME },
{WindowDFXHelperType::WINDOW_WALLPAPER_ZORDER_CHECK, WINDOW_WALLPAPER_ZORDER_CHECK_NAME },
};
const std::map<KeyboardLifeCycleException, std::string> KEYBOARD_LIFE_CYCLE_EXCEPTION_MAP = {
{KeyboardLifeCycleException::ANIM_SYNC_EXCEPTION, "ANIM_SYNC_EXCEPTION"},
{KeyboardLifeCycleException::CREATE_EXCEPTION, "CREATE_EXCEPTION"},
{KeyboardLifeCycleException::SHOW_EXCEPTION, "SHOW_EXCEPTION"},
{KeyboardLifeCycleException::HOT_AREA_EXCEPTION, "HOT_AREA_EXCEPTION"},
{KeyboardLifeCycleException::PANEL_AVOID_HEIGHT_EXCEPTION, "PANEL_AVOID_HEIGHT_EXCEPTION"},
{KeyboardLifeCycleException::MOVE_DRAG_EXCEPTION, "MOVE_DRAG_EXCEPTION"}
};
}
WM_IMPLEMENT_SINGLE_INSTANCE(WindowInfoReporter)
constexpr char EVENT_KEY_BUNDLE_NAME[] = "BUNDLE_NAME";
constexpr char EVENT_KEY_WINDOW_NAME[] = "WINDOW_NAME";
constexpr char EVENT_KEY_MISSION_ID[] = "MISSION_ID";
constexpr char EVENT_KEY_TIMESTAMP[] = "TIMESTAMP";
static const std::string REAL_TIME_ENABLED = OHOS::system::GetParameter("persist.window.realTimeIoDataOutput", "0");
* @brief Construct a new Perform Reporter:: Perform Reporter object
*
* @param tag A tag that in report string
* @param timeSpiltsMs The time-interval that data statistic, details look up the comments in function body
* @param reportInterval Report data after reportInterval round start-end
*/
PerformReporter::PerformReporter(const std::string& tag,
const std::vector<int64_t>& timeSpiltsMs, uint32_t reportInterval)
: tag_(tag), reportInterval_(reportInterval)
{
for (auto split : timeSpiltsMs) {
timeSplitCount_[split] = 0;
}
timeSplitCount_[BARRIER] = 0;
totalCount_ = 0;
}
void PerformReporter::start()
{
startTime_ = std::chrono::steady_clock::now();
}
void PerformReporter::end()
{
auto currentTime = std::chrono::steady_clock::now();
int64_t costTime = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime_).count();
count(costTime);
bool repSucc = report();
if (repSucc) {
clear();
}
}
bool PerformReporter::report()
{
if (totalCount_ < reportInterval_) {
return false;
}
std::ostringstream oss;
oss << tag_ << ": ";
auto maxSplit = 0;
for (const auto& iter: timeSplitCount_) {
if (iter.first != BARRIER) {
oss << "BELLOW" << iter.first << "(ms): " << iter.second << ", ";
maxSplit = iter.first;
}
}
oss << "ABOVE" << maxSplit << "(ms): " << timeSplitCount_[BARRIER];
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, tag_,
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "MSG", oss.str());
WLOGI("Write HiSysEvent ret:%{public}d", ret);
return ret == 0;
}
void PerformReporter::count(int64_t costTime)
{
totalCount_++;
for (auto& iter: timeSplitCount_) {
if (costTime <= iter.first) {
iter.second++;
break;
}
}
std::ostringstream oss;
oss << tag_ << " cost " << costTime << "ms, total count " << totalCount_;
WLOGI("%{public}s", oss.str().c_str());
}
void PerformReporter::clear()
{
totalCount_ = 0;
for (auto& iter: timeSplitCount_) {
iter.second = 0;
}
}
std::string WindowInfoReporter::GetMsgString(const FullInfoMap& infoMap)
{
if (infoMap.empty()) {
return "";
}
std::ostringstream oss;
oss << "{";
for (auto& bundleInfos : infoMap) {
if (bundleInfos.second.empty()) {
continue;
}
oss << "{";
for (auto& packageInfo : bundleInfos.second) {
oss << "BUNDLE_NAME:" << bundleInfos.first << ",";
oss << "ABILITY_NAME:" << packageInfo.first << ",";
oss << "COUNT:" << packageInfo.second;
}
oss << "},";
}
oss << "};";
return oss.str();
}
std::string WindowInfoReporter::GetMsgString(const BundleNameMap& infoMap)
{
if (infoMap.empty()) {
return "";
}
std::ostringstream oss;
oss << "{";
for (auto& bundleInfo : infoMap) {
oss << "{";
oss << "BUNDLE_NAME:" << bundleInfo.first << ",";
oss << "COUNT:" << bundleInfo.second;
oss << "},";
}
oss << "};";
return oss.str();
}
void WindowInfoReporter::InsertCreateReportInfo(const std::string& bundleName)
{
UpdateReportInfo(windowCreateReportInfos_, bundleName);
}
void WindowInfoReporter::InsertShowReportInfo(const std::string& bundleName)
{
UpdateReportInfo(windowShowReportInfos_, bundleName);
}
void WindowInfoReporter::InsertHideReportInfo(const std::string& bundleName)
{
UpdateReportInfo(windowHideReportInfos_, bundleName);
}
void WindowInfoReporter::InsertDestroyReportInfo(const std::string& bundleName)
{
UpdateReportInfo(windowDestoryReportInfos_, bundleName);
}
void WindowInfoReporter::InsertNavigationBarReportInfo(const std::string& bundleName, const std::string& packageName)
{
UpdateReportInfo(windowNavigationBarReportInfos_, bundleName, packageName);
}
void WindowInfoReporter::UpdateReportInfo(FullInfoMap& infoMap,
const std::string& bundleName, const std::string& packageName)
{
if (bundleName.empty() || packageName.empty()) {
return;
}
std::lock_guard<std::mutex> lock(mtx_);
auto iter = infoMap.find(bundleName);
if (iter == infoMap.end()) {
std::map<std::string, uint32_t> infos;
infos.insert(std::make_pair(packageName, 1));
infoMap.insert(std::make_pair(bundleName, infos));
return;
}
auto countPairIter = iter->second.find(packageName);
if (countPairIter == iter->second.end()) {
iter->second.insert(std::make_pair(packageName, 1));
return;
}
infoMap[bundleName][packageName]++;
}
void WindowInfoReporter::UpdateReportInfo(BundleNameMap& infoMap, const std::string& bundleName)
{
if (bundleName.empty()) {
return;
}
std::lock_guard<std::mutex> lock(mtx_);
auto iter = infoMap.find(bundleName);
if (iter == infoMap.end()) {
infoMap.insert(std::make_pair(bundleName, 1));
return;
}
infoMap[bundleName]++;
}
void WindowInfoReporter::ReportBackButtonInfoImmediately()
{
Report("WM_REPORT_BACK_KEYEVENT", "Click Back Button");
}
void WindowInfoReporter::ReportZeroOpacityInfoImmediately(const std::string& bundleName, const std::string& packageName)
{
if (bundleName.empty()) {
return;
}
std::ostringstream oss;
oss << "{ PROCESS_NAME:" << bundleName.c_str() << ", PACKAGE_NAME:" << "" << packageName.c_str() << " }";
Report("WM_REPORT_WINDOW_OPACITY_ZERO", oss.str());
}
void WindowInfoReporter::ReportStartWindow(const std::string& bundleName, const std::string& windowName)
{
std::string eventName = "START_WINDOW";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
EVENT_KEY_BUNDLE_NAME, bundleName,
EVENT_KEY_WINDOW_NAME, windowName);
if (ret != 0) {
WLOGFE("Write HiSysEvent error, ret:%{public}d", ret);
}
}
void WindowInfoReporter::ReportRecordedInfos()
{
std::lock_guard<std::mutex> lock(mtx_);
WLOGFD("----Report HiSysEvent write all-----");
Report("WM_REPORT_WINDOW_CREATE", GetMsgString(windowCreateReportInfos_));
Report("WM_REPORT_WINDOW_SHOW", GetMsgString(windowShowReportInfos_));
Report("WM_REPORT_WINDOW_HIDE", GetMsgString(windowHideReportInfos_));
Report("WM_REPORT_WINDOW_DESTORY", GetMsgString(windowDestoryReportInfos_));
Report("WM_REPORT_HIDE_NAVIGATIONBAR", GetMsgString(windowNavigationBarReportInfos_));
ClearRecordedInfos();
}
void WindowInfoReporter::ReportContainerStartBegin(int32_t missionId, const std::string& bundleName, int64_t timestamp)
{
std::string eventName = "CONTAINER_START_BEGIN";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
EVENT_KEY_MISSION_ID, missionId,
EVENT_KEY_BUNDLE_NAME, bundleName,
EVENT_KEY_TIMESTAMP, timestamp);
if (ret != 0) {
WLOGFE("Write HiSysEvent error, ret:%{public}d", ret);
}
}
void WindowInfoReporter::Report(const std::string& reportTag, const std::string& msg)
{
if (msg.empty()) {
return;
}
WLOGFD("Report Tag : [%{public}s], Msg: %{public}s", reportTag.c_str(), msg.c_str());
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, reportTag,
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "MSG", msg);
if (ret != 0) {
WLOGFE("Write HiSysEvent error, ret:%{public}d", ret);
}
}
void WindowInfoReporter::ClearRecordedInfos()
{
WLOGFD("Clear all hiSysEvent write information");
windowCreateReportInfos_.clear();
windowShowReportInfos_.clear();
windowHideReportInfos_.clear();
windowDestoryReportInfos_.clear();
windowNavigationBarReportInfos_.clear();
}
int32_t WindowInfoReporter::ReportWindowProfileInfo(const WindowProfileSum& windowProfileSum)
{
std::string eventName = "WINDOW_PROFILE_STATISTIC";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
"WINDOW_DETAIL", windowProfileSum.windowInfo,
"TOTAL_WINDOW_COUNT", windowProfileSum.totalWindowCount,
"VISIBLE_WINDOW_COUNT", windowProfileSum.visibleWindowCount,
"INVISIBLE_WINDOW_COUNT", windowProfileSum.invisibleWindowCount,
"MINIMIZE_WINDOW_COUNT", windowProfileSum.minimizeWindowCount);
if (ret != 0) {
WLOGFE("Write HiSysEvent error, ret:%{public}d", ret);
}
return ret;
}
void WindowInfoReporter::ReportWindowException(int32_t detectionType, int32_t pid, const std::string& windowInfo)
{
std::string eventName = "WINDOW_EXCEPTION_DETECTION";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
"DETECTION_TYPE", detectionType,
"PID", pid,
"MSG", windowInfo);
if (ret != 0) {
WLOGFE("Write HiSysEvent error, ret:%{public}d", ret);
}
}
int32_t WindowInfoReporter::ReportUIExtensionException(int32_t exceptionType, int32_t pid, int32_t persistentId,
const std::string& uiextInfo)
{
std::string eventName = "UIEXTENSION_EXCEPTION";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
"EXCEPTION_TYPE", exceptionType,
"PID", pid,
"PERSISTENTID", persistentId,
"MSG", uiextInfo);
if (ret != 0) {
TLOGE(WmsLogTag::WMS_UIEXT, "Write HiSysEvent error, ret: %{public}d", ret);
}
return ret;
}
int32_t WindowInfoReporter::ReportEventDispatchException(int32_t exceptionType, int32_t pid,
const std::string& flushInfo)
{
std::string eventName = "EVENT_DISPATCH_EXCEPTION";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
"EXCEPTION_TYPE", exceptionType,
"PID", pid,
"MSG", flushInfo);
if (ret != 0) {
TLOGE(WmsLogTag::WMS_EVENT, "Write HiSysEvent error, ret: %{public}d", ret);
}
return ret;
}
int32_t WindowInfoReporter::ReportKeyboardLifeCycleException(int32_t windowId, KeyboardLifeCycleException subType,
const std::string& msg)
{
std::string eventName = "KEYBOARD_LIFE_CYCLE_EXCEPTION";
int32_t ret = HiSysEventWrite(
HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
HiviewDFX::HiSysEvent::EventType::FAULT,
"WINDOW_ID", windowId,
"SUB_TYPE", KEYBOARD_LIFE_CYCLE_EXCEPTION_MAP.find(subType) != KEYBOARD_LIFE_CYCLE_EXCEPTION_MAP.end() ?
KEYBOARD_LIFE_CYCLE_EXCEPTION_MAP.at(subType) : "",
"MSG", msg);
if (ret != 0) {
TLOGE(WmsLogTag::DEFAULT, "write HiSysEvent error, ret: %{public}d", ret);
}
return ret;
}
int32_t WindowInfoReporter::ReportSpecWindowLifeCycleChange(WindowLifeCycleReportInfo reportInfo)
{
std::string eventName = "SPEC_WINDOW_LIFE_CYCLE_CHANGE";
int32_t ret = HiSysEventWrite(
HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
HiviewDFX::HiSysEvent::EventType::FAULT,
"BUNDLE_NAME", reportInfo.bundleName,
"WINDOW_ID", reportInfo.windowId,
"WINDOW_TYPE", reportInfo.windowType,
"WINDOW_MODE", reportInfo.windowMode,
"WINDOW_FLAG", reportInfo.windowFlag,
"STAGE", reportInfo.timeoutStage);
if (ret != 0) {
TLOGE(WmsLogTag::DEFAULT, "write HiSysEvent error, ret: %{public}d", ret);
}
return ret;
}
void WindowInfoReporter::ReportWindowIO(const std::string& subScene, double sizeKB)
{
bool sameDay = true;
{
std::lock_guard<std::mutex> lock(reportWindowIOMutex_);
if (!firstIOTimeInitialized_) {
firstIODayTime_ = std::chrono::floor<days>(std::chrono::system_clock::now());
firstIOSecondTime_ = std::chrono::system_clock::now();
firstIOTimeInitialized_ = true;
}
ioRecordMap_["TOTAL_WRITE_DATA"] += sizeKB;
ioRecordMap_[subScene] += sizeKB;
const auto currentDayTime = std::chrono::floor<days>(std::chrono::system_clock::now());
sameDay = (firstIODayTime_ == currentDayTime);
}
TLOGD(WmsLogTag::DEFAULT, "subScene: %{public}s, sizeKB: %{public}f, "
"sameDay: %{public}d, REAL_TIME_ENABLED: %{public}s",
subScene.c_str(), sizeKB, sameDay, REAL_TIME_ENABLED.c_str());
if (REAL_TIME_ENABLED == "1") {
sameDay = false;
}
if (sameDay) {
return;
}
ReportWindowIOPerDay();
}
void WindowInfoReporter::ReportWindowIOPerDay()
{
std::unordered_map<std::string, double> ioRecordMapCopy;
int64_t writeDate;
{
std::lock_guard<std::mutex> lock(reportWindowIOMutex_);
ioRecordMapCopy = ioRecordMap_;
writeDate = static_cast<int64_t>(std::chrono::duration_cast<std::chrono::seconds>(
firstIOSecondTime_.time_since_epoch()).count());
firstIOTimeInitialized_ = false;
ioRecordMap_.clear();
}
std::ostringstream oss;
for (const auto& elem : ioRecordMapCopy) {
if (elem.first == "TOTAL_WRITE_DATA") {
continue;
}
oss << elem.first << ": " << std::fixed << std::setprecision(2) << elem.second << "KB, ";
}
std::string msg = oss.str();
size_t redundantSize = 2;
if (msg.length() > redundantSize) {
msg.erase(msg.length() - redundantSize);
}
const double KILOBYTE = 1024.0;
double totalWriteData = ioRecordMapCopy["TOTAL_WRITE_DATA"] / KILOBYTE;
TLOGI(WmsLogTag::DEFAULT, "total: %{public}f, msg: %{public}s", totalWriteData, msg.c_str());
std::string eventName = "SCENEBOARD_IO_DFX";
static constexpr char WINDOW_IO_DOMAIN[] = "SCENE_BOARD_APP";
int32_t ret = HiSysEventWrite(
WINDOW_IO_DOMAIN, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
"PART_NAME", "Window",
"WRITE_DATE", writeDate,
"TOTAL_WRITE_DATA", totalWriteData,
"MSG", msg);
if (ret != 0) {
TLOGE(WmsLogTag::DEFAULT, "write HiSysEvent error, ret: %{public}d", ret);
}
}
void WindowInfoReporter::ReportWindowFrozen(WindowDFXHelperType detectionType, const std::string& windowInfo)
{
std::string eventName = "WINDOW_FROZEN_DETECTION";
std::string detectionName = WM_CHECK_TYPE_TO_CHECK_NAME_MAP.find(detectionType) !=
WM_CHECK_TYPE_TO_CHECK_NAME_MAP.end() ? WM_CHECK_TYPE_TO_CHECK_NAME_MAP.at(detectionType) : "";
int32_t ret = HiSysEventWrite(
OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER, eventName,
OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
"DETECTION_TYPE", static_cast<int32_t>(detectionType),
"DETECTION_NAME", detectionName,
"MSG", windowInfo);
if (ret != 0) {
TLOGE(WmsLogTag::DEFAULT, "Write HiSysEvent error, ret:%{public}d", ret);
}
}
bool WindowInfoReporter::IsKeyboardFrozenEnabled()
{
static bool enabled = [] {
bool isFrozen = OHOS::system::GetBoolParameter("persist.window.keyboard.frozen", false);
TLOGNI(WmsLogTag::WMS_KEYBOARD, "IsKeyboardFrozenEnabled: %{public}d", isFrozen);
return isFrozen;
}();
TLOGD(WmsLogTag::WMS_KEYBOARD, "enabled: %{public}d", enabled);
return enabled;
}
}
}