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