1/*
2 * Copyright (c) 2022 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#include "display_manager_config.h"
16
17#include <climits>
18#include <cstdint>
19#include <cstdlib>
20#include <libxml/globals.h>
21#include <libxml/xmlstring.h>
22#include <map>
23#include <string>
24#include <utility>
25#include <vector>
26
27#include "config_policy_utils.h"
28#include "window_manager_hilog.h"
29
30
31namespace OHOS::Rosen {
32namespace {
33constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "DisplayManagerConfig"};
34}
35
36std::map<std::string, bool> DisplayManagerConfig::enableConfig_;
37std::map<std::string, std::vector<int>> DisplayManagerConfig::intNumbersConfig_;
38std::map<std::string, std::string> DisplayManagerConfig::stringConfig_;
39
40std::vector<std::string> DisplayManagerConfig::Split(std::string str, std::string pattern)
41{
42    std::vector<std::string> result;
43    str += pattern;
44    size_t length = str.size();
45    for (size_t i = 0; i < length; i++) {
46        size_t position = str.find(pattern, i);
47        if (position < length) {
48            std::string tmp = str.substr(i, position - i);
49            result.push_back(tmp);
50            i = position + pattern.size() - 1;
51        }
52    }
53    return result;
54}
55
56bool inline DisplayManagerConfig::IsNumber(std::string str)
57{
58    for (int32_t i = 0; i < static_cast<int32_t>(str.size()); i++) {
59        if (str.at(i) < '0' || str.at(i) > '9') {
60            return false;
61        }
62    }
63    return true;
64}
65
66std::string DisplayManagerConfig::GetConfigPath(const std::string& configFileName)
67{
68    char buf[PATH_MAX + 1];
69    char* configPath = GetOneCfgFile(configFileName.c_str(), buf, PATH_MAX + 1);
70    char tmpPath[PATH_MAX + 1] = { 0 };
71    if (!configPath || strlen(configPath) == 0 || strlen(configPath) > PATH_MAX || !realpath(configPath, tmpPath)) {
72        WLOGFI("[DmConfig] can not get customization config file");
73        return "/system/" + configFileName;
74    }
75    return std::string(tmpPath);
76}
77
78bool DisplayManagerConfig::LoadConfigXml()
79{
80    auto configFilePath = GetConfigPath("etc/window/resources/display_manager_config.xml");
81    xmlDocPtr docPtr = nullptr;
82    {
83        std::lock_guard<std::recursive_mutex> lock(mutex_);
84        docPtr = xmlReadFile(configFilePath.c_str(), nullptr, XML_PARSE_NOBLANKS);
85    }
86    WLOGFI("[DmConfig] filePath: %{public}s", configFilePath.c_str());
87    if (docPtr == nullptr) {
88        WLOGFE("[DmConfig] load xml error!");
89        return false;
90    }
91
92    xmlNodePtr rootPtr = xmlDocGetRootElement(docPtr);
93    if (rootPtr == nullptr || rootPtr->name == nullptr ||
94        xmlStrcmp(rootPtr->name, reinterpret_cast<const xmlChar*>("Configs"))) {
95        WLOGFE("[DmConfig] get root element failed!");
96        xmlFreeDoc(docPtr);
97        return false;
98    }
99
100    for (xmlNodePtr curNodePtr = rootPtr->xmlChildrenNode; curNodePtr != nullptr; curNodePtr = curNodePtr->next) {
101        if (!IsValidNode(*curNodePtr)) {
102            WLOGFE("DmConfig]: invalid node!");
103            continue;
104        }
105
106        auto nodeName = curNodePtr->name;
107        if (!xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("isWaterfallDisplay")) ||
108            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("isWaterfallAreaCompressionEnableWhenHorizontal"))) {
109            ReadEnableConfigInfo(curNodePtr);
110            continue;
111        }
112        if (!xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("dpi")) ||
113            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("defaultDeviceRotationOffset")) ||
114            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("cutoutArea")) ||
115            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("curvedScreenBoundary")) ||
116            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("waterfallAreaCompressionSizeWhenHorzontal")) ||
117            !xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("buildInDefaultOrientation"))) {
118            ReadIntNumbersConfigInfo(curNodePtr);
119            continue;
120        }
121        if (!xmlStrcmp(nodeName, reinterpret_cast<const xmlChar*>("defaultDisplayCutoutPath"))) {
122            ReadStringConfigInfo(curNodePtr);
123            continue;
124        }
125    }
126    xmlFreeDoc(docPtr);
127    return true;
128}
129
130bool DisplayManagerConfig::IsValidNode(const xmlNode& currNode)
131{
132    if (currNode.name == nullptr || currNode.type == XML_COMMENT_NODE) {
133        return false;
134    }
135    return true;
136}
137
138void DisplayManagerConfig::ReadIntNumbersConfigInfo(const xmlNodePtr& currNode)
139{
140    xmlChar* context = xmlNodeGetContent(currNode);
141    if (context == nullptr) {
142        WLOGFE("[DmConfig] read xml node error: nodeName:(%{public}s)", currNode->name);
143        return;
144    }
145
146    std::vector<int> numbersVec;
147    std::string numbersStr = reinterpret_cast<const char*>(context);
148    if (numbersStr.empty()) {
149        xmlFree(context);
150        return;
151    }
152    auto numbers = Split(numbersStr, " ");
153    for (auto& num : numbers) {
154        if (!IsNumber(num)) {
155            WLOGFE("[DmConfig] read number error: nodeName:(%{public}s)", currNode->name);
156            xmlFree(context);
157            return;
158        }
159        numbersVec.emplace_back(std::stoi(num));
160    }
161
162    std::string nodeName = reinterpret_cast<const char *>(currNode->name);
163    intNumbersConfig_[nodeName] = numbersVec;
164    xmlFree(context);
165}
166
167void DisplayManagerConfig::ReadEnableConfigInfo(const xmlNodePtr& currNode)
168{
169    xmlChar* enable = xmlGetProp(currNode, reinterpret_cast<const xmlChar*>("enable"));
170    if (enable == nullptr) {
171        WLOGFE("[DmConfig] read xml node error: nodeName:(%{public}s)", currNode->name);
172        return;
173    }
174
175    std::string nodeName = reinterpret_cast<const char *>(currNode->name);
176    if (!xmlStrcmp(enable, reinterpret_cast<const xmlChar*>("true"))) {
177        enableConfig_[nodeName] = true;
178    } else {
179        enableConfig_[nodeName] = false;
180    }
181    xmlFree(enable);
182}
183
184void DisplayManagerConfig::ReadStringConfigInfo(const xmlNodePtr& currNode)
185{
186    xmlChar* context = xmlNodeGetContent(currNode);
187    if (context == nullptr) {
188        WLOGFE("[DmConfig] read xml node error: nodeName:(%{public}s)", currNode->name);
189        return;
190    }
191
192    std::string inputString = reinterpret_cast<const char*>(context);
193    std::string nodeName = reinterpret_cast<const char*>(currNode->name);
194    stringConfig_[nodeName] = inputString;
195    xmlFree(context);
196}
197
198const std::map<std::string, bool>& DisplayManagerConfig::GetEnableConfig()
199{
200    return enableConfig_;
201}
202
203const std::map<std::string, std::vector<int>>& DisplayManagerConfig::GetIntNumbersConfig()
204{
205    return intNumbersConfig_;
206}
207
208const std::map<std::string, std::string>& DisplayManagerConfig::GetStringConfig()
209{
210    return stringConfig_;
211}
212
213void DisplayManagerConfig::DumpConfig()
214{
215    for (auto& enable : enableConfig_) {
216        WLOGFI("[DmConfig] Enable: %{public}s %{public}u", enable.first.c_str(), enable.second);
217    }
218    for (auto& numbers : intNumbersConfig_) {
219        WLOGFI("[DmConfig] Numbers: %{public}s %{public}zu", numbers.first.c_str(), numbers.second.size());
220        for (auto& num : numbers.second) {
221            WLOGFI("[DmConfig] Num: %{public}d", num);
222        }
223    }
224    for (auto& string : stringConfig_) {
225        WLOGFI("[DmConfig] String: %{public}s", string.first.c_str());
226    }
227}
228} // namespace OHOS::Rosen
229