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