1 /*
2 * Copyright (c) 2021-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
16 #include "condition_arbitrator.h"
17
18 #include "ace_log.h"
19 #include "js_fwk_common.h"
20 #include "securec.h"
21 #include "stdlib.h"
22 #include "string_util.h"
23 #include "strings.h"
24 #include "system_info.h"
25
26 namespace OHOS {
27 namespace ACELite {
28 constexpr uint32_t LIMIT_CONDITION_LEN = 512;
29 constexpr int32_t OPERATOR_BRACKETS = 1;
30 constexpr int32_t OPERATOR_OR = 2;
31 constexpr int32_t OPERATOR_AND = 3;
32 constexpr uint32_t SPACE_CHAR_LIMIT_SCOPE = 5;
33 constexpr int32_t SLICE_FROM_CURRENT_POS = 0;
34 constexpr uint8_t MAX_LENGTH_PER_CONDITION = 32;
35 constexpr uint8_t MAX_LENGTH_PER_CONDITION_ADD_BRACKETS = 35;
36 constexpr uint8_t ASSICLL_DIGIT_START = 48;
37 constexpr uint8_t ASSICLL_DIGIT_END = 57;
38
39 /**
40 * @brief Absolute value of x.
41 */
42 #define ABS_VALUE(x) ((x) > 0 ? (x) : (-(x)))
43
44 /**
45 * @brief parse the whole condition string into single condition items, and check if it matches the current device
46 * environment, and gives the final result of the entire media query expression.
47 * @param conditions the input media query condition string
48 * @return the result representing if the media query matches the current environment, true for positive result
49 *
50 */
Decide(const char * conditions) const51 bool ConditionArbitrator::Decide(const char *conditions) const
52 {
53 bool result = IsValid(conditions);
54 if (!result) {
55 return false;
56 }
57 if (strstr(conditions, "and") == nullptr && strstr(conditions, "or") == nullptr) {
58 return JudgeCondition(conditions);
59 }
60 LinkQueue queue;
61 LinkQueue expressionQueue;
62 result = DecomPositionConditions(conditions, &queue);
63 if (!result || queue.IsEmpty()) {
64 FreeMallocData(&queue);
65 return false;
66 }
67 TransformExpression(&queue, &expressionQueue);
68 result = Calculate(&expressionQueue);
69 FreeMallocData(&queue);
70 return result;
71 }
72
JudgeCondition(const char * condition) const73 bool ConditionArbitrator::JudgeCondition(const char *condition) const
74 {
75 if (condition == nullptr || strlen(condition) == 0) {
76 HILOG_ERROR(HILOG_MODULE_ACE, "the condition is invalid");
77 return false;
78 }
79
80 // treat screen as one single media query feature, and it always is true for any devices
81 if (strcmp(condition, "screen") == 0) {
82 return true;
83 }
84
85 uint8_t conditionLen = strlen(condition);
86 // the condition must start with '(', end with ')'
87 if (condition[0] != '(' || (conditionLen <= 1) || (condition[conditionLen - 1] != ')')) {
88 HILOG_ERROR(HILOG_MODULE_ACE, "error format, condition is not properly packed with ( )");
89 return false;
90 }
91 const uint8_t minTargetLen = 2;
92 char *targetCondition = StringUtil::Slice(condition, 1, conditionLen - minTargetLen + 1);
93 if (targetCondition == nullptr) {
94 return false;
95 }
96 // devide the target condition into two parts : conditionName and value through ":"
97 char *targetValue = nullptr;
98 char *conditionNameStr = strtok_s(targetCondition, ":", &targetValue);
99 // judge the condition is success or not
100 ConditionName conditionId = GetConditionName(StringUtil::Trim(conditionNameStr));
101 char *trimedTargetValue = StringUtil::Trim(targetValue);
102 if (conditionId == ConditionName::UNKOWN || trimedTargetValue == nullptr) {
103 ACE_FREE(targetCondition);
104 targetCondition = nullptr;
105 return false;
106 }
107 bool result = JudgeConditionAction(conditionId, trimedTargetValue);
108 ACE_FREE(targetCondition);
109 targetCondition = nullptr;
110 return result;
111 }
112
JudgeConditionAction(ConditionName conditionId,const char * trimedTargetValue) const113 bool ConditionArbitrator::JudgeConditionAction(ConditionName conditionId, const char *trimedTargetValue) const
114 {
115 if (trimedTargetValue == nullptr || strlen(trimedTargetValue) == 0) {
116 return false;
117 }
118 switch (conditionId) {
119 case ConditionName::DEVICE_TYPE: // fall through
120 case ConditionName::ROUND_SCREEN:
121 return JudgeConditionByStrValue(conditionId, trimedTargetValue);
122 case ConditionName::WIDTH: // fall through
123 case ConditionName::MIN_WIDTH: // fall through
124 case ConditionName::MAX_WIDTH: // fall through
125 case ConditionName::HEIGHT: // fall through
126 case ConditionName::MIN_HEIGHT: // fall through
127 case ConditionName::MAX_HEIGHT: // fall through
128 case ConditionName::ASPECT_RATIO: // fall through
129 case ConditionName::MIN_ASPECT_RATIO: // fall through
130 case ConditionName::MAX_ASPECT_RATIO: {
131 return JudgeConditionByNumberValue(conditionId, trimedTargetValue);
132 }
133 default: {
134 HILOG_ERROR(HILOG_MODULE_ACE, "not supported condition feature %{public}d", conditionId);
135 return false;
136 }
137 }
138 }
139
JudgeConditionByStrValue(ConditionName conditionId,const char * trimedTargetValue) const140 bool ConditionArbitrator::JudgeConditionByStrValue(ConditionName conditionId, const char *trimedTargetValue) const
141 {
142 bool result = false;
143 switch (conditionId) {
144 case ConditionName::DEVICE_TYPE: {
145 result = (strcmp(trimedTargetValue, SystemInfo::GetInstance().GetDeviceType()) == 0);
146 break;
147 }
148 case ConditionName::ROUND_SCREEN: {
149 if (!strcmp(trimedTargetValue, "TRUE") || !strcmp(trimedTargetValue, "true") ||
150 !strcmp(trimedTargetValue, "1")) {
151 result = (SystemInfo::GetInstance().IsRoundScreen() == true);
152 } else if (!strcmp(trimedTargetValue, "FALSE") || !strcmp(trimedTargetValue, "false") ||
153 !(strcmp(trimedTargetValue, "0"))) {
154 result = (SystemInfo::GetInstance().IsRoundScreen() == false);
155 } else {
156 result = false;
157 }
158 break;
159 }
160 default: {
161 return false;
162 }
163 }
164 return result;
165 }
166
JudgeConditionByNumberValue(ConditionName conditionId,const char * targetValue) const167 bool ConditionArbitrator::JudgeConditionByNumberValue(ConditionName conditionId, const char *targetValue) const
168 {
169 // must be started with number character
170 if (!(targetValue[0] >= ASSICLL_DIGIT_START && targetValue[0] <= ASSICLL_DIGIT_END)) {
171 return false;
172 }
173 switch (conditionId) {
174 case ConditionName::WIDTH: // fall through
175 case ConditionName::MIN_WIDTH: // fall through
176 case ConditionName::MAX_WIDTH: // fall through
177 case ConditionName::HEIGHT: // fall through
178 case ConditionName::MIN_HEIGHT: // fall through
179 case ConditionName::MAX_HEIGHT: {
180 return CompareIntDimension(conditionId, targetValue);
181 }
182 case ConditionName::ASPECT_RATIO: // fall through
183 case ConditionName::MIN_ASPECT_RATIO: // fall through
184 case ConditionName::MAX_ASPECT_RATIO: {
185 return CompareFloatDimension(conditionId, targetValue);
186 }
187 default: {
188 return false;
189 }
190 }
191 }
192
CompareIntDimension(ConditionName conditionId,const char * targetValue) const193 bool ConditionArbitrator::CompareIntDimension(ConditionName conditionId, const char *targetValue) const
194 {
195 int dimensionValue = atoi(targetValue);
196 if (dimensionValue <= 0 || dimensionValue >= UINT16_MAX) {
197 return false;
198 }
199 switch (conditionId) {
200 case ConditionName::WIDTH: {
201 return SystemInfo::GetInstance().GetScreenWidth() == dimensionValue;
202 }
203 case ConditionName::MIN_WIDTH: {
204 // the device screen width must be larger than the requirement
205 return SystemInfo::GetInstance().GetScreenWidth() >= dimensionValue;
206 }
207 case ConditionName::MAX_WIDTH: {
208 // the device screen width must be smaller than the requirement
209 return SystemInfo::GetInstance().GetScreenWidth() <= dimensionValue;
210 }
211 case ConditionName::HEIGHT: {
212 return SystemInfo::GetInstance().GetScreenHeight() == dimensionValue;
213 }
214 case ConditionName::MIN_HEIGHT: {
215 // the device screen height must be larger than the requirement
216 return SystemInfo::GetInstance().GetScreenHeight() >= dimensionValue;
217 }
218 case ConditionName::MAX_HEIGHT: {
219 // the device screen height must be smaller than the requirement
220 return SystemInfo::GetInstance().GetScreenHeight() <= dimensionValue;
221 }
222 default:
223 return false;
224 }
225 }
226
IsFloatValueEqual(float left,float right,float precision) const227 bool ConditionArbitrator::IsFloatValueEqual(float left, float right, float precision) const
228 {
229 return (ABS_VALUE(left - right) < precision);
230 }
231
CompareFloatDimension(ConditionName conditionId,const char * targetValue) const232 bool ConditionArbitrator::CompareFloatDimension(ConditionName conditionId, const char *targetValue) const
233 {
234 float floatValue = atof(targetValue);
235 switch (conditionId) {
236 case ConditionName::ASPECT_RATIO: // fall through
237 case ConditionName::MIN_ASPECT_RATIO: // fall through
238 case ConditionName::MAX_ASPECT_RATIO: {
239 return CompareAspectRatio(conditionId, floatValue);
240 }
241 default: {
242 return false;
243 }
244 }
245 }
246
CompareAspectRatio(ConditionName conditionId,float targetRatioValue) const247 bool ConditionArbitrator::CompareAspectRatio(ConditionName conditionId, float targetRatioValue) const
248 {
249 float currentAspectRatio = SystemInfo::GetInstance().GetAspectRatio();
250 bool isEqual = IsFloatValueEqual(targetRatioValue, currentAspectRatio, CONDITION_FLOAT_VALUE_EPRECISION);
251 if (isEqual) {
252 // the equal case matches for all ASPECT_RATIO media feature types
253 return true;
254 }
255 switch (conditionId) {
256 case ConditionName::ASPECT_RATIO: {
257 return isEqual;
258 }
259 case ConditionName::MIN_ASPECT_RATIO: {
260 return currentAspectRatio > targetRatioValue;
261 }
262 case ConditionName::MAX_ASPECT_RATIO: {
263 return currentAspectRatio < targetRatioValue;
264 }
265 default: {
266 return false;
267 }
268 }
269 }
270
GetConditionName(const char * conditionName) const271 ConditionName ConditionArbitrator::GetConditionName(const char *conditionName) const
272 {
273 if (conditionName == nullptr || strlen(conditionName) == 0) {
274 return ConditionName::UNKOWN;
275 }
276 static const struct {
277 const char *nameStr;
278 ConditionName name;
279 } conditionNamePair[ConditionName::MAX_COUNT] = {
280 {"width", ConditionName::WIDTH},
281 {"height", ConditionName::HEIGHT},
282 {"min-width", ConditionName::MIN_WIDTH},
283 {"max-width", ConditionName::MAX_WIDTH},
284 {"min-height", ConditionName::MIN_HEIGHT},
285 {"max-height", ConditionName::MAX_HEIGHT},
286 {"aspect-ratio", ConditionName::ASPECT_RATIO},
287 {"min-aspect-ratio", ConditionName::MIN_ASPECT_RATIO},
288 {"max-aspect-ratio", ConditionName::MAX_ASPECT_RATIO},
289 {"device-type", ConditionName::DEVICE_TYPE},
290 {"round-screen", ConditionName::ROUND_SCREEN}
291 };
292 ConditionName targetName = ConditionName::UNKOWN;
293 uint8_t index = 0;
294 for (; index < ConditionName::UNKOWN; index++) {
295 if (strcmp(conditionName, conditionNamePair[index].nameStr) == 0) {
296 targetName = conditionNamePair[index].name;
297 break;
298 }
299 }
300 if (index == ConditionName::UNKOWN) {
301 HILOG_ERROR(HILOG_MODULE_ACE, "the condition name is not supported [%{public}s]", conditionName);
302 }
303 return targetName;
304 }
305
IsValid(const char * conditions) const306 bool ConditionArbitrator::IsValid(const char *conditions) const
307 {
308 if (conditions == nullptr || strlen(conditions) >= LIMIT_CONDITION_LEN) {
309 return false;
310 }
311
312 LinkStack stack;
313 while (*conditions != '\0') {
314 if (*conditions == '(') {
315 stack.Push("(");
316 } else if (*conditions == ')') {
317 if (stack.IsEmpty() || strcmp("(", stack.Peak()) != 0) {
318 return false;
319 }
320 stack.Pop(nullptr);
321 }
322 conditions++;
323 }
324 return stack.IsEmpty() ? true : false;
325 }
326
FindFirstPos(const char * conditions,uint8_t * size) const327 const char *ConditionArbitrator::FindFirstPos(const char *conditions, uint8_t *size) const
328 {
329 if (conditions == nullptr || size == nullptr) {
330 return nullptr;
331 }
332
333 const char *recordPos = conditions;
334 while (*recordPos != '\0') {
335 if (*recordPos == '(' || *recordPos == ')' || IsOperator(recordPos)) {
336 return recordPos;
337 }
338 recordPos++;
339 (*size)++;
340 }
341 return nullptr;
342 }
343
FindNoSpacePos(const char * conditions) const344 const char *ConditionArbitrator::FindNoSpacePos(const char *conditions) const
345 {
346 if (conditions == nullptr) {
347 return nullptr;
348 }
349
350 while (*conditions != '\0' && ((*conditions) == ' ' ||
351 static_cast<uint32_t>(*conditions) - '\t' < SPACE_CHAR_LIMIT_SCOPE)) {
352 conditions++;
353 }
354 return conditions;
355 }
356
DecomPositionConditions(const char * conditions,LinkQueue * queue) const357 bool ConditionArbitrator::DecomPositionConditions(const char *conditions, LinkQueue* queue) const
358 {
359 conditions = FindNoSpacePos(conditions);
360 if (conditions == nullptr || strlen(conditions) == 0 || queue == nullptr || !queue->IsEmpty()) {
361 return false;
362 }
363 const char *recordPos = nullptr;
364 char *buff = nullptr;
365 uint8_t len = 0;
366 bool result = false;
367 while ((recordPos = FindFirstPos(conditions, &len)) != nullptr) {
368 if (len == 0) {
369 switch (*recordPos) {
370 case '(' :
371 case ')' :
372 buff = StringUtil::Slice(recordPos, SLICE_FROM_CURRENT_POS, OPERATOR_BRACKETS);
373 recordPos++;
374 break;
375 case 'a' :
376 buff = StringUtil::Slice(recordPos, SLICE_FROM_CURRENT_POS, OPERATOR_AND);
377 recordPos = recordPos + OPERATOR_AND;
378 break;
379 case 'o' :
380 buff = StringUtil::Slice(recordPos, SLICE_FROM_CURRENT_POS, OPERATOR_OR);
381 recordPos = recordPos + OPERATOR_OR;
382 break;
383 default :
384 break;
385 }
386 recordPos = FindNoSpacePos(recordPos);
387 } else {
388 buff = StringUtil::Slice(conditions, SLICE_FROM_CURRENT_POS, len * sizeof(char));
389 }
390
391 if (buff != nullptr) {
392 result = queue->Enqueue(buff);
393 if (!result) {
394 return false;
395 }
396 }
397 conditions = recordPos;
398 len = 0;
399 }
400 if (*conditions != '\0' && strlen(conditions) > 0) {
401 buff = StringUtil::Slice(conditions, SLICE_FROM_CURRENT_POS);
402 if (buff != nullptr) {
403 result = queue->Enqueue(buff);
404 }
405 }
406 return result;
407 }
408
IsOperator(const char * str) const409 bool ConditionArbitrator::IsOperator(const char *str) const
410 {
411 if (str != nullptr) {
412 if (!strncmp("and", str, OPERATOR_AND) || !strncmp("or", str, OPERATOR_OR)) {
413 return true;
414 }
415 }
416 return false;
417 }
418
TransformExpression(const LinkQueue * queue,LinkQueue * expressionQueue) const419 void ConditionArbitrator::TransformExpression(const LinkQueue *queue, LinkQueue *expressionQueue) const
420 {
421 if (queue == nullptr || expressionQueue == nullptr) {
422 return;
423 }
424 if (queue->IsEmpty()) {
425 return;
426 }
427 const char *getvalue = nullptr;
428 LinkStack stack;
429 QueueNode *tmp = queue->GetNext();
430 while (tmp) {
431 if (strcmp(tmp->GetNodeData(), "(") == 0) {
432 stack.Push(tmp->GetNodeData());
433 } else if (strcmp(tmp->GetNodeData(), ")") == 0) {
434 while (!stack.IsEmpty() && strcmp(stack.Peak(), "(") != 0) {
435 stack.Pop(&getvalue);
436 expressionQueue->Enqueue(getvalue);
437 }
438 stack.Pop(nullptr);
439 } else if (IsOperator(tmp->GetNodeData())) {
440 if (!stack.IsEmpty() && strcmp("(", stack.Peak()) != 0) {
441 stack.Pop(&getvalue);
442 expressionQueue->Enqueue(getvalue);
443 }
444 stack.Push(tmp->GetNodeData());
445 } else {
446 expressionQueue->Enqueue(tmp->GetNodeData());
447 }
448 tmp = tmp->GetNodeNext();
449 }
450
451 while (!stack.IsEmpty()) {
452 stack.Pop(&getvalue);
453 expressionQueue->Enqueue(getvalue);
454 }
455 }
456
Parse(const char * condition) const457 bool ConditionArbitrator::Parse(const char *condition) const
458 {
459 if (strcmp(condition, "screen") == 0 || strcmp(condition, "true") == 0) {
460 return true;
461 } else if (strcmp(condition, "false") == 0) {
462 return false;
463 } else if (strlen(condition) > MAX_LENGTH_PER_CONDITION) {
464 return false;
465 }
466
467 char conditionWithBrackets[MAX_LENGTH_PER_CONDITION_ADD_BRACKETS] = {0};
468 if (strcat_s(conditionWithBrackets, MAX_LENGTH_PER_CONDITION_ADD_BRACKETS, "(") != 0) {
469 return false;
470 }
471 if (strcat_s(conditionWithBrackets, MAX_LENGTH_PER_CONDITION_ADD_BRACKETS, condition) != 0) {
472 return false;
473 }
474 if (strcat_s(conditionWithBrackets, MAX_LENGTH_PER_CONDITION_ADD_BRACKETS, ")") != 0) {
475 return false;
476 }
477 return JudgeCondition(conditionWithBrackets);
478 }
479
Calculate(LinkQueue * expressionQueue) const480 bool ConditionArbitrator::Calculate(LinkQueue *expressionQueue) const
481 {
482 if (expressionQueue == nullptr || expressionQueue->IsEmpty()) {
483 return false;
484 }
485
486 bool result = false;
487 LinkStack stack;
488 const char *value = nullptr;
489 const char resultTrue[] = "true";
490 const char resultFalse[] = "false";
491 while (!expressionQueue->IsEmpty()) {
492 expressionQueue->Dequeue(&value);
493 if (!IsOperator(value)) {
494 stack.Push(value);
495 } else {
496 const char *condition1 = nullptr;
497 const char *condition2 = nullptr;
498 stack.Pop(&condition1);
499 stack.Pop(&condition2);
500 if (condition1 == nullptr || condition2 == nullptr) {
501 return false;
502 }
503 char *trimStr1 = StringUtil::Trim(const_cast<char*>(condition1));
504 char *trimStr2 = StringUtil::Trim(const_cast<char*>(condition2));
505 if (trimStr1 == nullptr || trimStr2 == nullptr) {
506 return false;
507 }
508 switch (*value) {
509 case 'a':
510 result = Parse(trimStr1) && Parse(trimStr2);
511 break;
512 case 'o':
513 result = Parse(trimStr1) || Parse(trimStr2);
514 break;
515 default:
516 break;
517 }
518 result ? stack.Push(resultTrue) : stack.Push(resultFalse);
519 }
520 }
521 if (stack.StackSize() != 1) {
522 return false;
523 }
524 stack.Pop(&value);
525 return strcmp(value, "true") == 0 ? true : false;
526 }
527
FreeMallocData(const LinkQueue * queue) const528 void ConditionArbitrator::FreeMallocData(const LinkQueue *queue) const
529 {
530 if (queue == nullptr) {
531 return;
532 }
533 QueueNode *tmp = queue->GetNext();
534 while (tmp) {
535 char *nodeValue = const_cast<char *>(tmp->GetNodeData());
536 ACE_FREE(nodeValue);
537 tmp->SetNodeData(nullptr);
538 tmp = tmp->GetNodeNext();
539 }
540 }
541 } // namespace ACELite
542 } // namespace OHOS
543