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