1 /*
2 * Copyright (c) 2025 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
17 #include "special_input_device_parser.h"
18
19 #include "json_parser.h"
20 #include "util.h"
21
22 #include <cJSON.h>
23
24 #undef MMI_LOG_DOMAIN
25 #define MMI_LOG_DOMAIN MMI_LOG_SERVER
26 #undef MMI_LOG_TAG
27 #define MMI_LOG_TAG "SpecialInputDeviceParser"
28
29 namespace OHOS {
30 namespace MMI {
31 namespace {
32 constexpr std::string_view specialInputDeviceDir { "/etc/multimodalinput/special_input_device_config.json" };
33 constexpr int32_t maxJsonArraySize { 100 };
34 }
35
GetInstance()36 SpecialInputDeviceParser& SpecialInputDeviceParser::GetInstance()
37 {
38 static SpecialInputDeviceParser instance;
39 return instance;
40 }
41
Init()42 int32_t SpecialInputDeviceParser::Init()
43 {
44 CALL_INFO_TRACE;
45 if (isInitialized_.load()) {
46 return RET_OK;
47 }
48 std::string jsonStr = ReadJsonFile(std::string(specialInputDeviceDir));
49 if (jsonStr.empty()) {
50 MMI_HILOGE("Read specialInputDevice failed");
51 return RET_ERR;
52 }
53 JsonParser parser(jsonStr.c_str());
54 if (!cJSON_IsObject(parser.Get())) {
55 MMI_HILOGE("Not valid object");
56 return RET_ERR;
57 }
58 if (ParseExactlyMatch(parser) != RET_OK) {
59 MMI_HILOGE("ParseExactlyMatch failed");
60 return RET_ERR;
61 }
62 if (ParseContainMatch(parser) != RET_OK) {
63 MMI_HILOGE("ParseContainMatch failed");
64 return RET_ERR;
65 }
66 if (ParseSpecialInputDevice(parser) != RET_OK) {
67 MMI_HILOGE("ParseSpecialInputDevice failed");
68 return RET_ERR;
69 }
70 PrintSpecialInputDevice();
71 isInitialized_.store(true);
72 return RET_OK;
73 }
74
IsPointerDevice(const std::string & name,bool & isPointerDevice)75 int32_t SpecialInputDeviceParser::IsPointerDevice(const std::string &name, bool &isPointerDevice)
76 {
77 if (Init() != RET_OK) {
78 MMI_HILOGE("Init failed");
79 return RET_ERR;
80 }
81 std::shared_lock<std::shared_mutex> lock(lock_);
82 if (exactlyMatchInputDevice_.find(name) != exactlyMatchInputDevice_.end()) {
83 isPointerDevice = exactlyMatchInputDevice_[name].isMouse;
84 return RET_OK;
85 }
86 for (const auto &containItem : containMatchInputDevice_) {
87 if (IsAllKeywordsMatched(name, containItem.keywords)) {
88 isPointerDevice = containItem.isMouse;
89 return RET_OK;
90 }
91 }
92 return RET_ERR;
93 }
94
GetInputDevName(const std::string & alias)95 std::string SpecialInputDeviceParser::GetInputDevName(const std::string &alias)
96 {
97 if (Init() != RET_OK) {
98 MMI_HILOGE("Init failed");
99 return "";
100 }
101 std::shared_lock<std::shared_mutex> lock(lock_);
102 if (specialInputDevices_.find(alias) != specialInputDevices_.end()) {
103 return specialInputDevices_[alias];
104 }
105 MMI_HILOGW("No %{public}s matched.", alias.c_str());
106 return "";
107 }
108
ParseExactlyMatch(const JsonParser & jsonParser)109 int32_t SpecialInputDeviceParser::ParseExactlyMatch(const JsonParser &jsonParser)
110 {
111 cJSON *exactlyMatchJson = cJSON_GetObjectItemCaseSensitive(jsonParser.Get(), "exactly_match");
112 if (!cJSON_IsArray(exactlyMatchJson)) {
113 MMI_HILOGE("exactlyMatchJson is not array");
114 return RET_ERR;
115 }
116 int32_t arraySize = cJSON_GetArraySize(exactlyMatchJson);
117 if (arraySize > maxJsonArraySize) {
118 MMI_HILOGW("arraySize is too much, truncate it");
119 }
120 for (int32_t i = 0; i < std::min(arraySize, maxJsonArraySize); i++) {
121 cJSON* devItemJson = cJSON_GetArrayItem(exactlyMatchJson, i);
122 if (devItemJson == nullptr) {
123 MMI_HILOGE("The devItem init failed");
124 continue;
125 }
126 ExactlyMatchInputDevice inputDev;
127 if (ParseExactlyMatchItem(devItemJson, inputDev) != RET_OK) {
128 MMI_HILOGE("ParseExactlyMatchItem failed");
129 continue;
130 }
131 std::unique_lock<std::shared_mutex> lock(lock_);
132 exactlyMatchInputDevice_.insert({inputDev.devName, { inputDev.devName, inputDev.isMouse }});
133 }
134 return RET_OK;
135 }
136
ParseContainMatch(const JsonParser & jsonParser)137 int32_t SpecialInputDeviceParser::ParseContainMatch(const JsonParser &jsonParser)
138 {
139 cJSON *containMatchJson = cJSON_GetObjectItemCaseSensitive(jsonParser.Get(), "contain_match");
140 if (!cJSON_IsArray(containMatchJson)) {
141 MMI_HILOGE("containMatchJson is not array");
142 return RET_ERR;
143 }
144 int32_t arraySize = cJSON_GetArraySize(containMatchJson);
145 if (arraySize > maxJsonArraySize) {
146 MMI_HILOGE("arraySize is too much");
147 return RET_ERR;
148 }
149 for (int32_t i = 0; i < arraySize; i++) {
150 cJSON* devItemJson = cJSON_GetArrayItem(containMatchJson, i);
151 if (devItemJson == nullptr) {
152 MMI_HILOGE("The devItem init failed");
153 continue;
154 }
155 ContainMatchInputDevice inputDev;
156 if (ParseContainMatchItem(devItemJson, inputDev) != RET_OK) {
157 MMI_HILOGE("ParseContainMatchItem failed");
158 continue;
159 }
160 std::unique_lock<std::shared_mutex> lock(lock_);
161 containMatchInputDevice_.push_back(inputDev);
162 }
163 return RET_OK;
164 }
165
ParseSpecialInputDevice(const JsonParser & jsonParser)166 int32_t SpecialInputDeviceParser::ParseSpecialInputDevice(const JsonParser &jsonParser)
167 {
168 cJSON *specialInputDevJson = cJSON_GetObjectItemCaseSensitive(jsonParser.Get(), "special_input_device");
169 if (!cJSON_IsArray(specialInputDevJson)) {
170 MMI_HILOGE("specialInputDevJson is not array");
171 return RET_ERR;
172 }
173 int32_t arraySize = cJSON_GetArraySize(specialInputDevJson);
174 if (arraySize > maxJsonArraySize) {
175 MMI_HILOGE("arraySize is too much");
176 return RET_ERR;
177 }
178 for (int32_t i = 0; i < arraySize; i++) {
179 cJSON* devItemJson = cJSON_GetArrayItem(specialInputDevJson, i);
180 if (devItemJson == nullptr) {
181 MMI_HILOGE("The devItem init failed");
182 continue;
183 }
184 SpecialInputDevice inputDev;
185 if (ParseSpecialInputDeviceItem(devItemJson, inputDev) != RET_OK) {
186 MMI_HILOGE("ParseSpecialInputDeviceItem failed");
187 continue;
188 }
189 std::unique_lock<std::shared_mutex> lock(lock_);
190 specialInputDevices_.insert({ inputDev.inputDevAlias, inputDev.inputDevName });
191 }
192 return RET_OK;
193 }
194
ParseExactlyMatchItem(const cJSON * json,ExactlyMatchInputDevice & deviceProp)195 int32_t SpecialInputDeviceParser::ParseExactlyMatchItem(const cJSON *json, ExactlyMatchInputDevice &deviceProp)
196 {
197 if (JsonParser::ParseString(json, "device_name", deviceProp.devName) != RET_OK) {
198 MMI_HILOGE("Parse device_name failed");
199 return RET_ERR;
200 }
201 if (JsonParser::ParseBool(json, "is_mouse", deviceProp.isMouse) != RET_OK) {
202 MMI_HILOGE("Parse is_mouse failed");
203 return RET_ERR;
204 }
205 return RET_OK;
206 }
207
ParseContainMatchItem(const cJSON * json,ContainMatchInputDevice & deviceProp)208 int32_t SpecialInputDeviceParser::ParseContainMatchItem(const cJSON *json, ContainMatchInputDevice &deviceProp)
209 {
210 std::vector<std::string> keywords;
211 if (JsonParser::ParseStringArray(json, "keywords", keywords, maxJsonArraySize) != RET_OK) {
212 MMI_HILOGE("Parse keywords failed");
213 return RET_ERR;
214 }
215 deviceProp.keywords = std::move(keywords);
216 if (JsonParser::ParseBool(json, "is_mouse", deviceProp.isMouse) != RET_OK) {
217 MMI_HILOGE("Parse is_mouse failed");
218 return RET_ERR;
219 }
220 return RET_OK;
221 }
222
ParseSpecialInputDeviceItem(const cJSON * json,SpecialInputDevice & specialInputDev)223 int32_t SpecialInputDeviceParser::ParseSpecialInputDeviceItem(const cJSON *json, SpecialInputDevice &specialInputDev)
224 {
225 if (JsonParser::ParseString(json, "input_device_alias", specialInputDev.inputDevAlias) != RET_OK) {
226 MMI_HILOGE("Parse input_device_alias failed");
227 return RET_ERR;
228 }
229 if (JsonParser::ParseString(json, "input_device_name", specialInputDev.inputDevName) != RET_OK) {
230 MMI_HILOGE("Parse input_device_name failed");
231 return RET_ERR;
232 }
233 return RET_OK;
234 }
235
IsAllKeywordsMatched(const std::string & name,const std::vector<std::string> & keywords)236 bool SpecialInputDeviceParser::IsAllKeywordsMatched(const std::string &name, const std::vector<std::string> &keywords)
237 {
238 for (const auto &key : keywords) {
239 if (name.find(key) == std::string::npos) {
240 return false;
241 }
242 }
243 return true;
244 }
245
PrintSpecialInputDevice()246 void SpecialInputDeviceParser::PrintSpecialInputDevice()
247 {
248 std::shared_lock<std::shared_mutex> lock(lock_);
249 MMI_HILOGI("Excatly Match:");
250 for (const auto &elem : exactlyMatchInputDevice_) {
251 MMI_HILOGI("deviceName:%{public}s -> isMouse:%{public}d", elem.second.devName.c_str(), elem.second.isMouse);
252 }
253 MMI_HILOGI("Contain Match:");
254 for (const auto &elem : containMatchInputDevice_) {
255 std::string keywords;
256 std::for_each(elem.keywords.begin(), elem.keywords.end(), [&keywords](const std::string &key) {
257 keywords.append(key + ", ");
258 });
259 MMI_HILOGI("keywords:%{public}s -> isMouse:%{public}d", keywords.c_str(), elem.isMouse);
260 }
261 MMI_HILOGI("Special Input Device:");
262 for (const auto &elem : specialInputDevices_) {
263 MMI_HILOGI("devAlias:%{public}s -> devName:%{public}s", elem.first.c_str(), elem.second.c_str());
264 }
265 }
266 } // namespace MMI
267 } // namespace OHOS