• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 package ohos;
17 
18 import java.math.BigDecimal;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29 
30 /**
31  * check hap is verify.
32  */
33 class HapVerify {
34     private static final String INCLUDE = "include";
35     private static final String EXCLUDE = "exclude";
36     private static final Log LOG = new Log(HapVerify.class.toString());
37     private static final int SERVICE_DEPTH = 2;
38     private static final int APPLICATION_DEPTH = 5;
39     private static final String EMPTY_STRING = "";
40     private static final String ENTRY = "entry";
41     private static final String FEATURE = "feature";
42     private static final String SHARED_LIBRARY = "shared";
43     private static final String HAR = "har";
44     private static final String REFERENCE_LINK =
45             "https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/verification_rule-0000001406748378";
46     private static final String ATOMIC_SERVICE = "atomicService";
47     private static final String TYPE_SHARED = "shared";
48     private static final long FILE_LENGTH_1M = 1024 * 1024L;
49     private static final double FILE_SIZE_OFFSET_DOUBLE = 0.01d;
50     private static final int FILE_SIZE_DECIMAL_PRECISION = 2;
51 
52     /**
53      * check hap is verify.
54      *
55      * @param hapVerifyInfos is the collection of hap infos
56      * @return the result
57      * @throws BundleException Throws this exception if the json is not standard
58      */
checkHapIsValid(List<HapVerifyInfo> hapVerifyInfos)59     public static boolean checkHapIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
60         if (hapVerifyInfos == null || hapVerifyInfos.isEmpty()) {
61             LOG.error("HapVerify::checkHapIsValid hapVerifyInfos is empty");
62             return false;
63         }
64         // check app variable is same
65         if (!checkAppFieldsIsSame(hapVerifyInfos)) {
66             LOG.error("some app variable is different.");
67             return false;
68         }
69         // check moduleName is valid
70         if (!checkModuleNameIsValid(hapVerifyInfos)) {
71             return false;
72         }
73         // check package is valid
74         if (!checkPackageNameIsValid(hapVerifyInfos)) {
75             LOG.error("packageName duplicated.");
76             return false;
77         }
78         // check entry is valid
79         if (!checkEntryIsValid(hapVerifyInfos)) {
80             return false;
81         }
82         // check dependency is valid
83         if (!checkDependencyIsValid(hapVerifyInfos)) {
84             LOG.error("module dependency is invalid.");
85             return false;
86         }
87         // check atomic service is valid
88         if (!checkAtomicServiceIsValid(hapVerifyInfos)) {
89             LOG.error("checkAtomicServiceIsValid failed.");
90             return false;
91         }
92         // check ability is valid
93         if (!checkAbilityNameIsValid(hapVerifyInfos)) {
94             LOG.info("Ability name is duplicated.");
95         }
96         // check targetModuleName
97         if (!checkTargetModuleNameIsExisted(hapVerifyInfos)) {
98             LOG.error("target module is not found.");
99             return false;
100         }
101         if (!checkCompileSdkIsValid(hapVerifyInfos)) {
102             LOG.error("compile sdk config is not same.");
103             return false;
104         }
105         if (!checkProxyDataUriIsUnique(hapVerifyInfos)) {
106             LOG.error("uris in proxy data are not unique.");
107             return false;
108         }
109         return true;
110     }
111 
112     /**
113      * check inter-app hsp is valid.
114      *
115      * @param hapVerifyInfos is the collection of hap infos
116      * @return the result
117      * @throws BundleException Throws this exception if the json is not standard
118      */
checkSharedApppIsValid(List<HapVerifyInfo> hapVerifyInfos)119     public static boolean checkSharedApppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
120         if (hapVerifyInfos == null || hapVerifyInfos.isEmpty()) {
121             LOG.error("HapVerify::checkSharedApppIsValid hapVerifyInfos is empty.");
122             return false;
123         }
124         String moduleName = hapVerifyInfos.get(0).getModuleName();
125         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
126             if (!moduleName.equals(hapVerifyInfo.getModuleName())) {
127                 LOG.error("HapVerify::checkSharedApppIsValid module name is different.");
128                 return false;
129             }
130             if (!hapVerifyInfo.getDependencyItemList().isEmpty()) {
131                 LOG.error("HapVerify::checkSharedApppIsValid shared hsp cannot depend on other modules.");
132                 return false;
133             }
134             if (!TYPE_SHARED.equals(hapVerifyInfo.getModuleType())) {
135                 LOG.error("HapVerify::checkSharedApppIsValid module type is not shared app.");
136                 return false;
137             }
138         }
139         for (int i = 0; i < hapVerifyInfos.size(); i++) {
140             for (int j = i + 1; j < hapVerifyInfos.size(); j++) {
141                 if (!checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
142                     LOG.error("HapVerify::checkSharedApppIsValid duplicated module.");
143                     return false;
144                 }
145             }
146         }
147         return true;
148     }
149 
150 
151     /**
152      * check whether the app fields in the hap are the same.
153      *
154      * @param hapVerifyInfos is the collection of hap infos
155      * @return true if app fields is same
156      */
checkAppFieldsIsSame(List<HapVerifyInfo> hapVerifyInfos)157     private static boolean checkAppFieldsIsSame(List<HapVerifyInfo> hapVerifyInfos) {
158         if (hapVerifyInfos.isEmpty()) {
159             LOG.error("HapVerify::checkAppVariableIsSame failed, hapVerifyInfos is empty.");
160             return false;
161         }
162         VerifyCollection verifyCollection = new VerifyCollection();
163         verifyCollection.bundleName = hapVerifyInfos.get(0).getBundleName();
164         verifyCollection.setBundleType(hapVerifyInfos.get(0).getBundleType());
165         verifyCollection.vendor = hapVerifyInfos.get(0).getVendor();
166         verifyCollection.versionCode = hapVerifyInfos.get(0).getVersion().versionCode;
167         verifyCollection.versionName = hapVerifyInfos.get(0).getVersion().versionName;
168         verifyCollection.minCompatibleVersionCode = hapVerifyInfos.get(0).getVersion().minCompatibleVersionCode;
169         verifyCollection.compatibleApiVersion = hapVerifyInfos.get(0).getApiVersion().getCompatibleApiVersion();
170         verifyCollection.targetApiVersion = hapVerifyInfos.get(0).getApiVersion().getTargetApiVersion();
171         verifyCollection.releaseType = hapVerifyInfos.get(0).getApiVersion().getReleaseType();
172         verifyCollection.targetBundleName = hapVerifyInfos.get(0).getTargetBundleName();
173         verifyCollection.targetPriority = hapVerifyInfos.get(0).getTargetPriority();
174         verifyCollection.debug = hapVerifyInfos.get(0).isDebug();
175         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
176             if (hapVerifyInfo.getBundleName().isEmpty() ||
177                     !verifyCollection.bundleName.equals(hapVerifyInfo.getBundleName())) {
178                 LOG.error("input module bundleName is different.");
179                 return false;
180             }
181             if (!verifyCollection.getBundleType().equals(hapVerifyInfo.getBundleType())) {
182                 LOG.error("input module bundleType is different.");
183                 return false;
184             }
185             if (hapVerifyInfo.getVendor().isEmpty() || !verifyCollection.vendor.equals(hapVerifyInfo.getVendor())) {
186                 LOG.error("input module vendor is different.");
187                 return false;
188             }
189             if (verifyCollection.versionCode != hapVerifyInfo.getVersion().versionCode) {
190                 LOG.error("input module versionCode is different.");
191                 return false;
192             }
193             if (!verifyCollection.versionName.equals(hapVerifyInfo.getVersion().versionName)) {
194                 LOG.error("input module versionName is different.");
195                 return false;
196             }
197             if (verifyCollection.minCompatibleVersionCode != hapVerifyInfo.getVersion().minCompatibleVersionCode) {
198                 LOG.error("input module minCompatibleVersionCode is different.");
199                 return false;
200             }
201             if (verifyCollection.compatibleApiVersion != hapVerifyInfo.getApiVersion().getCompatibleApiVersion()) {
202                 LOG.error("input module minApiVersion is different.");
203                 return false;
204             }
205             if (verifyCollection.targetApiVersion != hapVerifyInfo.getApiVersion().getTargetApiVersion()) {
206                 LOG.error("input module targetApiVersion is different.");
207                 return false;
208             }
209             if (!verifyCollection.releaseType.equals(hapVerifyInfo.getApiVersion().getReleaseType())) {
210                 LOG.error("input module releaseType is different.");
211                 return false;
212             }
213             if (!verifyCollection.targetBundleName.equals(hapVerifyInfo.getTargetBundleName())) {
214                 LOG.error("targetBundleName is different.");
215                 return false;
216             }
217             if (verifyCollection.targetPriority != hapVerifyInfo.getTargetPriority()) {
218                 LOG.error("targetPriority is different.");
219                 return false;
220             }
221             if (verifyCollection.debug != hapVerifyInfo.isDebug()) {
222                 LOG.error("debug is different.");
223                 return false;
224             }
225         }
226         return true;
227     }
228 
229     /**
230      * check moduleName is valid.
231      *
232      * @param hapVerifyInfos is the collection of hap infos
233      * @return true if moduleName is valid
234      * @throws BundleException Throws this exception if the json is not standard.
235      */
checkModuleNameIsValid(List<HapVerifyInfo> hapVerifyInfos)236     private static boolean checkModuleNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
237         for (int i = 0; i < hapVerifyInfos.size() - 1; ++i) {
238             if (hapVerifyInfos.get(i).getModuleName().isEmpty()) {
239                 LOG.error("HapVerify::checkModuleNameIsValid should not be empty.");
240                 throw new BundleException("HapVerify::checkModuleNameIsValid should not be empty.");
241             }
242             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
243                 if (hapVerifyInfos.get(i).getModuleName().equals(hapVerifyInfos.get(j).getModuleName()) &&
244                     !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
245                     LOG.error("Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
246                         hapVerifyInfos.get(j).getModuleName() + ") have the same moduleName, " +
247                             "please check deviceType or distroFilter of the module.");
248                     LOG.error("Module: " + hapVerifyInfos.get(i).getModuleName() + " has deviceType "
249                         + hapVerifyInfos.get(i).getDeviceType() + ".");
250                     LOG.error("Another Module: " + hapVerifyInfos.get(j).getModuleName() + " has deviceType "
251                         + hapVerifyInfos.get(j).getDeviceType() + ".");
252                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(i).getDistroFilter().dump())) {
253                         LOG.error("Module: " + hapVerifyInfos.get(i).getModuleName() + " DistroFilter is : "
254                                 + hapVerifyInfos.get(i).getDistroFilter().dump() + ".");
255                     }
256                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(j).getDistroFilter().dump())) {
257                         LOG.error("Another Module: " + hapVerifyInfos.get(j).getModuleName() + " DistroFilter is "
258                                 + hapVerifyInfos.get(j).getDistroFilter().dump() + ".");
259                     }
260                     LOG.error("Solution: Make sure the module name is valid and unique.");
261                     LOG.error("Reference: " + REFERENCE_LINK + ".");
262                     return false;
263                 }
264             }
265         }
266         return true;
267     }
268 
269     /**
270      * check packageName is valid.
271      *
272      * @param hapVerifyInfos is the collection of hap infos
273      * @return true if moduleName is valid
274      * @throws BundleException Throws this exception if the json is not standard
275      */
checkPackageNameIsValid(List<HapVerifyInfo> hapVerifyInfos)276     private static boolean checkPackageNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
277         for (int i = 0; i < hapVerifyInfos.size() - 1; ++i) {
278             if (hapVerifyInfos.get(i).getPackageName().isEmpty()) {
279                 continue;
280             }
281             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
282                 if (hapVerifyInfos.get(i).getPackageName().equals(hapVerifyInfos.get(j).getPackageName()) &&
283                         !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
284                     LOG.error("Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
285                             hapVerifyInfos.get(j).getModuleName() + ") have the same packageName, " +
286                             "please check deviceType or distroFilter of the module.");
287                     LOG.error("Module: " + hapVerifyInfos.get(i).getModuleName() + " has deviceType "
288                             + hapVerifyInfos.get(i).getDeviceType() + ".");
289                     LOG.error("Another Module: " + hapVerifyInfos.get(j).getModuleName() + " has deviceType "
290                             + hapVerifyInfos.get(j).getDeviceType() + ".");
291                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(i).getDistroFilter().dump())) {
292                         LOG.error("Module: " + hapVerifyInfos.get(i).getModuleName() + " DistroFilter is : " +
293                                 hapVerifyInfos.get(i).getDistroFilter().dump() + ".");
294                     }
295                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(j).getDistroFilter().dump())) {
296                         LOG.error("Another Module: " + hapVerifyInfos.get(j).getModuleName() + " DistroFilter is " +
297                                 hapVerifyInfos.get(j).getDistroFilter().dump() + ".");
298                     }
299                     LOG.error("Solution: Make sure package name is valid and unique.");
300                     LOG.error("Reference: " + REFERENCE_LINK + ".");
301                     return false;
302                 }
303             }
304         }
305         return true;
306     }
307 
308     /**
309      * check abilityName is valid.
310      *
311      * @param hapVerifyInfos is the collection of hap infos
312      * @return true if abilityName is valid
313      * @throws BundleException Throws this exception if the json is not standard.
314      */
checkAbilityNameIsValid(List<HapVerifyInfo> hapVerifyInfos)315     private static boolean checkAbilityNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
316         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
317             long noDuplicatedCount = hapVerifyInfo.getAbilityNames().stream().distinct().count();
318             if (noDuplicatedCount != hapVerifyInfo.getAbilityNames().size()) {
319                 LOG.warning(
320                         hapVerifyInfo.getModuleName() + " ability duplicated, please rename ability name.");
321                 return false;
322             }
323         }
324         for (int i = 0; i < hapVerifyInfos.size(); ++i) {
325             if (hapVerifyInfos.get(i).getAbilityNames().isEmpty()) {
326                 continue;
327             }
328             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
329                 if (!Collections.disjoint(hapVerifyInfos.get(i).getAbilityNames(),
330                         hapVerifyInfos.get(j).getAbilityNames()) &&
331                         !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
332                     LOG.warning("Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
333                             hapVerifyInfos.get(j).getModuleName() + ") have the same ability name.");
334                     LOG.warning("Module: " + hapVerifyInfos.get(i).getModuleName() + " has ability "
335                         + hapVerifyInfos.get(i).getAbilityNames() + ".");
336                     LOG.warning("Module: " + hapVerifyInfos.get(j).getModuleName() + " has ability "
337                         + hapVerifyInfos.get(j).getAbilityNames() + ".");
338                     LOG.warning("Solution: Make sure ability name is valid and unique.");
339                     LOG.warning("Reference: " + REFERENCE_LINK + ".");
340                     return false;
341                 }
342             }
343         }
344         return true;
345     }
346 
347     /**
348      * check targetModuleName is existed.
349      *
350      * @param hapVerifyInfos is the collection of hap infos
351      * @return true if targetModuleName is erxisted
352      * @throws BundleException Throws this exception if the json is not standard.
353      */
checkTargetModuleNameIsExisted(List<HapVerifyInfo> hapVerifyInfos)354     private static boolean checkTargetModuleNameIsExisted(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
355         List<HapVerifyInfo> internalOverlayHap = new ArrayList<>();
356         List<HapVerifyInfo> nonOverlayHap = new ArrayList<>();
357         List<String> targetModuleList = new ArrayList<>();
358         List<String> moduleList = new ArrayList<>();
359         for (HapVerifyInfo hapInfo : hapVerifyInfos) {
360             if (!hapInfo.getTargetBundleName().isEmpty()) {
361                 return true;
362             }
363             if (!hapInfo.getTargetModuleName().isEmpty()) {
364                 internalOverlayHap.add(hapInfo);
365                 targetModuleList.add(hapInfo.getTargetModuleName());
366                 continue;
367             }
368             nonOverlayHap.add(hapInfo);
369             if (!SHARED_LIBRARY.equals(hapInfo.getModuleType())) {
370                 moduleList.add(hapInfo.getModuleName());
371             }
372         }
373         if (internalOverlayHap.isEmpty()) {
374             return true;
375         }
376         if (nonOverlayHap.isEmpty()) {
377             LOG.error("target modules are needed to pack with overlay module.");
378             return false;
379         }
380         if (!moduleList.containsAll(targetModuleList)) {
381             LOG.error("target modules are needed to pack with overlay module.");
382             return false;
383         }
384 
385 
386         return true;
387     }
388 
checkCompileSdkIsValid(List<HapVerifyInfo> hapVerifyInfos)389     private static boolean checkCompileSdkIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
390         if (hapVerifyInfos.isEmpty()) {
391             LOG.error("hapVerifyInfos is empty");
392             return false;
393         }
394         String compileSdkVersion = hapVerifyInfos.get(0).getCompileSdkVersion();
395         String compileSdkType = hapVerifyInfos.get(0).getCompileSdkType();
396         for (HapVerifyInfo info : hapVerifyInfos) {
397             if (!compileSdkType.equals(info.getCompileSdkType())) {
398                 LOG.error("compile sdk type is not same.");
399                 return false;
400             }
401             if (!compileSdkVersion.equals(info.getCompileSdkVersion())) {
402                 LOG.error("compile sdk version is not same.");
403                 return false;
404             }
405         }
406         return true;
407     }
408 
checkProxyDataUriIsUnique(List<HapVerifyInfo> hapVerifyInfos)409     private static boolean checkProxyDataUriIsUnique(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
410         if (hapVerifyInfos.isEmpty()) {
411             LOG.error("hapVerifyInfos is empty");
412             return false;
413         }
414         Set<String> uriSet = new HashSet<>();
415         for (HapVerifyInfo info : hapVerifyInfos) {
416             for (String uri : info.getProxyDataUris()) {
417                 if (uriSet.contains(uri)) {
418                     LOG.error("uri " + uri + " in proxy data is duplicated");
419                     return false;
420                 } else {
421                     uriSet.add(uri);
422                 }
423             }
424         }
425         return true;
426     }
427 
428     /**
429      * check entry is valid.
430      *
431      * @param hapVerifyInfos is the collection of hap infos
432      * @return true if entry is valid
433      * @throws BundleException Throws this exception if the json is not standard.
434      */
checkEntryIsValid(List<HapVerifyInfo> hapVerifyInfos)435     private static boolean checkEntryIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
436         List<HapVerifyInfo> entryHapVerifyInfos = new ArrayList<>();
437         List<HapVerifyInfo> featureHapVerifyInfos = new ArrayList<>();
438         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
439             if (ENTRY.equals(hapVerifyInfo.getModuleType())) {
440                 entryHapVerifyInfos.add(hapVerifyInfo);
441             } else if (FEATURE.equals(hapVerifyInfo.getModuleType())) {
442                 featureHapVerifyInfos.add(hapVerifyInfo);
443             } else if (!SHARED_LIBRARY.equals(hapVerifyInfo.getModuleType())) {
444                 LOG.warning("Input wrong type module.");
445             }
446         }
447         if (hapVerifyInfos.isEmpty()
448                 || (entryHapVerifyInfos.isEmpty() && (!SHARED_LIBRARY.equals(hapVerifyInfos.get(0).getBundleType())))) {
449             LOG.warning("Warning: has no entry module.");
450         }
451 
452         for (int i = 0; i < entryHapVerifyInfos.size() - 1; ++i) {
453             for (int j = i + 1; j < entryHapVerifyInfos.size(); ++j) {
454                 if (!checkDuplicatedIsValid(entryHapVerifyInfos.get(i), entryHapVerifyInfos.get(j))) {
455                     LOG.error("Module: (" + entryHapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
456                             entryHapVerifyInfos.get(j).getModuleName() + ") are entry, " +
457                             "please check deviceType or distroFilter of the module.");
458                     LOG.error("Module: " + entryHapVerifyInfos.get(i).getModuleName() + " has deviceType "
459                             + entryHapVerifyInfos.get(i).getDeviceType() + ".");
460                     LOG.error("Another Module: " + entryHapVerifyInfos.get(j).getModuleName() + " has deviceType "
461                             + entryHapVerifyInfos.get(j).getDeviceType() + ".");
462                     if (!EMPTY_STRING.equals(entryHapVerifyInfos.get(i).getDistroFilter().dump())) {
463                         LOG.error("Module: " + entryHapVerifyInfos.get(i).getModuleName() + " DistroFilter is : " +
464                                 entryHapVerifyInfos.get(i).getDistroFilter().dump() + ".");
465                     }
466                     if (!EMPTY_STRING.equals(entryHapVerifyInfos.get(j).getDistroFilter().dump())) {
467                         LOG.error("Another Module: " + entryHapVerifyInfos.get(j).getModuleName() +
468                                 " DistroFilter is " + entryHapVerifyInfos.get(j).getDistroFilter().dump() + ".");
469                     }
470                     LOG.error("Solution: Make sure entry name is valid and unique.");
471                     LOG.error("Reference: " + REFERENCE_LINK + ".");
472                     return false;
473                 }
474             }
475         }
476 
477         Map<String, List<HapVerifyInfo>> deviceHap = classifyEntry(entryHapVerifyInfos);
478         for (HapVerifyInfo hapVerifyInfo : featureHapVerifyInfos) {
479             if (!checkFeature(hapVerifyInfo, deviceHap)) {
480                 LOG.warning(hapVerifyInfo.getModuleName() + " can not be covered by entry.");
481             }
482         }
483 
484         return true;
485     }
486 
487     /**
488      * check if name duplicated, name is valid.
489      *
490      * @param hapVerifyInfoLeft is one hapVerifyInfo
491      * @param hapVerifyInfoRight is another hapVerifyInfo that name is duplicated with hapVerifyInfoLeft
492      * @return true if moduleName is valid
493      * @throws BundleException Throws this exception if the json is not standard.
494      */
checkDuplicatedIsValid(HapVerifyInfo hapVerifyInfoLeft, HapVerifyInfo hapVerifyInfoRight)495     private static boolean checkDuplicatedIsValid(HapVerifyInfo hapVerifyInfoLeft, HapVerifyInfo hapVerifyInfoRight)
496             throws BundleException {
497         // check deviceType
498         if (Collections.disjoint(hapVerifyInfoLeft.getDeviceType(), hapVerifyInfoRight.getDeviceType())) {
499             return true;
500         }
501         // check distroFilter
502         if (checkDistroFilterDisjoint(hapVerifyInfoLeft.getDistroFilter(), hapVerifyInfoRight.getDistroFilter())) {
503             return true;
504         }
505 
506         return false;
507     }
508 
509     /**
510      * check two distroFilter is disjoint.
511      *
512      * @param distroFilterLeft is one distroFilter
513      * @param distroFilterRight is another distroFilter will be checked
514      * @throws BundleException Throws this exception if the json is not standard.
515      * @return true if two distroFilter is disjoint
516      */
checkDistroFilterDisjoint(DistroFilter distroFilterLeft, DistroFilter distroFilterRight)517     private static boolean checkDistroFilterDisjoint(DistroFilter distroFilterLeft, DistroFilter distroFilterRight)
518             throws BundleException {
519         if (distroFilterLeft == null || distroFilterRight == null) {
520             return false;
521         }
522         if (distroFilterLeft.apiVersion != null && distroFilterRight.apiVersion != null) {
523             if (checkPolicyValueDisjoint(distroFilterLeft.apiVersion.policy, distroFilterLeft.apiVersion.value,
524                     distroFilterRight.apiVersion.policy, distroFilterRight.apiVersion.value)) {
525                 return true;
526             }
527         }
528         if (distroFilterLeft.screenShape != null && distroFilterRight.screenShape != null) {
529             if (checkPolicyValueDisjoint(distroFilterLeft.screenShape.policy, distroFilterLeft.screenShape.value,
530                     distroFilterRight.screenShape.policy, distroFilterRight.screenShape.value)) {
531                 return true;
532             }
533         }
534         if (distroFilterLeft.screenDensity != null && distroFilterRight.screenDensity != null) {
535             if (checkPolicyValueDisjoint(distroFilterLeft.screenDensity.policy, distroFilterLeft.screenDensity.value,
536                     distroFilterRight.screenDensity.policy, distroFilterRight.screenDensity.value)) {
537                 return true;
538             }
539         }
540         if (distroFilterLeft.screenWindow != null && distroFilterRight.screenWindow != null) {
541             if (checkPolicyValueDisjoint(distroFilterLeft.screenWindow.policy, distroFilterLeft.screenWindow.value,
542                     distroFilterRight.screenWindow.policy, distroFilterRight.screenWindow.value)) {
543                 return true;
544             }
545         }
546         if (distroFilterLeft.countryCode != null && distroFilterRight.countryCode != null) {
547             if (checkPolicyValueDisjoint(distroFilterLeft.countryCode.policy, distroFilterLeft.countryCode.value,
548                     distroFilterRight.countryCode.policy, distroFilterRight.countryCode.value)) {
549                 return true;
550             }
551         }
552         return false;
553     }
554 
555     /**
556      * check two distroFilter variable is disjoint.
557      *
558      * @param policyLeft is one distroFilter variable policy
559      * @param valueLeft is one distroFilter variable value
560      * @param policyRight is another distroFilter variable policy
561      * @param valueRight is another distroFilter variable value
562      * @return true if two variable is disjoint
563      * @throws BundleException Throws this exception if the json is not standard.
564      */
checkPolicyValueDisjoint(String policyLeft, List<String> valueLeft, String policyRight, List<String> valueRight)565     private static boolean checkPolicyValueDisjoint(String policyLeft, List<String> valueLeft, String policyRight,
566                                                     List<String> valueRight) throws BundleException {
567         if (valueLeft == null || valueRight == null) {
568             LOG.error("HapVerify::checkPolicyValueDisjoint value should not empty.");
569             throw new BundleException("HapVerify::checkPolicyValueDisjoint value should not empty.");
570         }
571         if (EXCLUDE.equals(policyLeft) && INCLUDE.equals(policyRight)) {
572             if (valueRight.isEmpty() || valueLeft.containsAll(valueRight)) {
573                 return true;
574             }
575         } else if (INCLUDE.equals(policyLeft) && INCLUDE.equals(policyRight)) {
576             if (Collections.disjoint(valueLeft, valueRight)) {
577                 return true;
578             }
579         } else if (INCLUDE.equals(policyLeft) && EXCLUDE.equals(policyRight)) {
580             if (valueLeft.isEmpty() || valueRight.containsAll(valueLeft)) {
581                 return true;
582             }
583         } else if (EXCLUDE.equals(policyLeft) && EXCLUDE.equals(policyRight)) {
584             return false;
585         } else {
586             LOG.error("HapVerify::checkPolicyValueDisjoint input policy is invalid.");
587             throw new BundleException("HapVerify::checkPolicyValueDisjoint input policy is invalid.");
588         }
589         return false;
590     }
591 
592     /**
593      * classify entry haps by deviceType.
594      *
595      * @param entryHapVerifyInfos is the list od entry hapVerifyInfos
596      * @return deviceHap that is classfied
597      */
classifyEntry(List<HapVerifyInfo> entryHapVerifyInfos)598     private static Map<String, List<HapVerifyInfo>> classifyEntry(List<HapVerifyInfo> entryHapVerifyInfos) {
599         Map<String, List<HapVerifyInfo>> deviceHaps = new HashMap<>();
600         for (HapVerifyInfo hapVerifyInfo : entryHapVerifyInfos) {
601             for (String device : hapVerifyInfo.getDeviceType()) {
602                 if (deviceHaps.containsKey(device)) {
603                     deviceHaps.get(device).add(hapVerifyInfo);
604                 } else {
605                     deviceHaps.put(device, new ArrayList<HapVerifyInfo>());
606                     deviceHaps.get(device).add(hapVerifyInfo);
607                 }
608             }
609         }
610         return deviceHaps;
611     }
612 
613     /**
614      * check feature is valid, deviceType is subset of entry, distroFilter is subset of entry
615      *
616      * @param featureHap the feature hap will be checked
617      * @param deviceHap is the haps that feature matched
618      * @return feature is valid
619      * @throws BundleException when input distroFilter is invalid
620      */
checkFeature(HapVerifyInfo featureHap, Map<String, List<HapVerifyInfo>> deviceHap)621     private static boolean checkFeature(HapVerifyInfo featureHap, Map<String, List<HapVerifyInfo>> deviceHap)
622             throws BundleException {
623         // check deviceType and distroFilter
624         for (String device : featureHap.getDeviceType()) {
625             if (!deviceHap.containsKey(device)) {
626                 LOG.warning("Warning: device " + device + " has feature but has no entry.");
627                 return false;
628             }
629             List<HapVerifyInfo> entryHaps = deviceHap.get(device);
630             if (!checkFeatureDistroFilter(featureHap, entryHaps)) {
631                 LOG.warning(featureHap.getModuleName() +
632                         "'s distroFilter has not covered by entry.");
633                 if (!EMPTY_STRING.equals(featureHap.getDistroFilter().dump())) {
634                     LOG.warning(featureHap.getModuleName() + " has " +
635                             featureHap.getDistroFilter().dump() + ".");
636                 }
637                 return false;
638             }
639         }
640         return true;
641     }
642 
643     /**
644      * check feature is valid, deviceType is subset of entry, distroFilter is subset of entry
645      *
646      * @param featureHap the feature hap will be checked
647      * @param entryHaps is the haps that feature matched
648      * @return feature is valid
649      * @throws BundleException when input policy in invalid
650      */
checkFeatureDistroFilter(HapVerifyInfo featureHap, List<HapVerifyInfo> entryHaps)651     private static boolean checkFeatureDistroFilter(HapVerifyInfo featureHap, List<HapVerifyInfo> entryHaps)
652             throws BundleException {
653         if (featureHap.getDistroFilter() == null) {
654             if (checkApiVersionCovered(null, entryHaps)
655                     && checkScreenShapeCovered(null, entryHaps)
656                     && checkScreenWindowCovered(null, entryHaps)
657                     && checkScreenDensityCovered(null, entryHaps)
658                     && checkCountryCodeCovered(null, entryHaps)) {
659                 return true;
660             } else {
661                 return false;
662             }
663         }
664         if (!checkApiVersionCovered(featureHap.getDistroFilter().apiVersion, entryHaps)) {
665             LOG.warning("HapVerify::checkFeatureDistroFilter failed, apiVersion is not covered.");
666             return false;
667         }
668         if (!checkScreenShapeCovered(featureHap.getDistroFilter().screenShape, entryHaps)) {
669             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenShape is not covered.");
670             return false;
671         }
672         if (!checkScreenWindowCovered(featureHap.getDistroFilter().screenWindow, entryHaps)) {
673             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenWindow is not covered.");
674             return false;
675         }
676         if (!checkScreenDensityCovered(featureHap.getDistroFilter().screenDensity, entryHaps)) {
677             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenDensity is not covered.");
678             return false;
679         }
680         if (!checkCountryCodeCovered(featureHap.getDistroFilter().countryCode, entryHaps)) {
681             LOG.warning("HapVerify::checkFeatureDistroFilter failed, countryCode is not covered.");
682             return false;
683         }
684         return true;
685     }
686 
687     /**
688      * check feature apiVersion is subset of entry apiVersion
689      *
690      * @param apiVersion is the apiVersion of feature hap
691      * @param entryHaps is the haps that feature matched
692      * @return apiVersion is valid
693      * @throws BundleException when input policy is invalid
694      */
checkApiVersionCovered(ApiVersion apiVersion, List<HapVerifyInfo> entryHaps)695     private static boolean checkApiVersionCovered(ApiVersion apiVersion, List<HapVerifyInfo> entryHaps)
696             throws BundleException {
697         List<String> include = null;
698         List<String> exclude = null;
699         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
700             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().apiVersion == null) {
701                 return true;
702             }
703             if (hapVerifyInfo.getDistroFilter().apiVersion.policy == null) {
704                 LOG.error("HapVerify::checkApiVersionCovered input none policy.");
705                 return false;
706             }
707             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().apiVersion.policy)) {
708                 if (include == null) {
709                     include = new ArrayList<>();
710                 }
711                 // take collection of two include value
712                 include.addAll(hapVerifyInfo.getDistroFilter().apiVersion.value);
713             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().apiVersion.policy)) {
714                 if (exclude == null) {
715                     exclude = new ArrayList<>();
716                 }
717                 // take intersection of two exclude value
718                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().apiVersion.value).
719                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
720             } else {
721                 LOG.error("HapVerify::checkApiVersionCovered input policy is invalid.");
722                 throw new BundleException("HapVerify::checkApiVersionCovered input policy is invalid.");
723             }
724         }
725         if (include != null) {
726             include = include.stream().distinct().collect(Collectors.toList());
727         }
728         if (exclude != null) {
729             exclude = exclude.stream().distinct().collect(Collectors.toList());
730         }
731         if (apiVersion == null) {
732             return checkEntryPolicyValueCoverAll(include, exclude);
733         }
734         return checkPolicyValueCovered(apiVersion.policy, apiVersion.value, include, exclude);
735     }
736 
737     /**
738      * check feature screenShape is subset of entry screenShape
739      *
740      * @param screenShape is the screenShape of feature hap
741      * @param entryHaps is the haps that feature matched
742      * @return screenShape is valid
743      * @throws BundleException when input policy is invalid
744      */
checkScreenShapeCovered(ScreenShape screenShape, List<HapVerifyInfo> entryHaps)745     private static boolean checkScreenShapeCovered(ScreenShape screenShape, List<HapVerifyInfo> entryHaps)
746             throws BundleException {
747         List<String> include = null;
748         List<String> exclude = null;
749         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
750             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenShape == null) {
751                 return true;
752             }
753             if (hapVerifyInfo.getDistroFilter().screenShape.policy == null) {
754                 LOG.error("HapVerify::checkScreenShapeCovered input none policy.");
755                 return false;
756             }
757             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenShape.policy)) {
758                 if (include == null) {
759                     include = new ArrayList<>();
760                 }
761                 include.addAll(hapVerifyInfo.getDistroFilter().screenShape.value);
762             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenShape.policy)) {
763                 if (exclude == null) {
764                     exclude = new ArrayList<>();
765                 }
766                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenShape.value).
767                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
768             } else {
769                 LOG.error("HapVerify::checkScreenShapeCovered input policy is invalid.");
770                 throw new BundleException("HapVerify::checkScreenShapeCovered input policy is invalid.");
771             }
772         }
773         if (include != null) {
774             include = include.stream().distinct().collect(Collectors.toList());
775         }
776         if (exclude != null) {
777             exclude = exclude.stream().distinct().collect(Collectors.toList());
778         }
779         if (screenShape == null) {
780             return checkEntryPolicyValueCoverAll(include, exclude);
781         }
782         return checkPolicyValueCovered(screenShape.policy, screenShape.value, include, exclude);
783     }
784 
785     /**
786      * check feature screenWindow is subset of entry screenWindow
787      *
788      * @param screenWindow is the screenWindow of feature hap
789      * @param entryHaps is the haps that feature matched
790      * @return screenWindow is valid
791      */
checkScreenWindowCovered(ScreenWindow screenWindow, List<HapVerifyInfo> entryHaps)792     private static boolean checkScreenWindowCovered(ScreenWindow screenWindow, List<HapVerifyInfo> entryHaps)
793             throws BundleException {
794         List<String> include = null;
795         List<String> exclude = null;
796         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
797             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenWindow == null) {
798                 return true;
799             }
800             if (hapVerifyInfo.getDistroFilter().screenWindow.policy == null) {
801                 LOG.error("HapVerify::checkScreenWindowCovered input none policy.");
802                 return false;
803             }
804             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenWindow.policy)) {
805                 if (include == null) {
806                     include = new ArrayList<>();
807                 }
808                 include.addAll(hapVerifyInfo.getDistroFilter().screenWindow.value);
809             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenWindow.policy)) {
810                 if (exclude == null) {
811                     exclude = new ArrayList<>();
812                 }
813                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenWindow.value).
814                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
815             } else {
816                 LOG.error("HapVerify::checkScreenWindowCovered input policy is invalid.");
817                 throw new BundleException("HapVerify::checkScreenWindowCovered input policy is invalid.");
818             }
819         }
820         if (include != null) {
821             include = include.stream().distinct().collect(Collectors.toList());
822         }
823         if (exclude != null) {
824             exclude = exclude.stream().distinct().collect(Collectors.toList());
825         }
826         if (screenWindow == null) {
827             return checkEntryPolicyValueCoverAll(include, exclude);
828         }
829         return checkPolicyValueCovered(screenWindow.policy, screenWindow.value, include, exclude);
830     }
831 
832     /**
833      * check feature screenDensity is subset of entry screenDensity
834      *
835      * @param screenDensity is the screenDensity of feature hap
836      * @param entryHaps is the haps that feature matched
837      * @return screenDensity is valid
838      */
checkScreenDensityCovered(ScreenDensity screenDensity, List<HapVerifyInfo> entryHaps)839     private static boolean checkScreenDensityCovered(ScreenDensity screenDensity, List<HapVerifyInfo> entryHaps)
840             throws BundleException {
841         List<String> include = null;
842         List<String> exclude = null;
843         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
844             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenDensity == null) {
845                 return true;
846             }
847             if (hapVerifyInfo.getDistroFilter().screenDensity.policy == null) {
848                 LOG.error("HapVerify::checkScreenDensityCovered input none policy.");
849                 return false;
850             }
851             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenDensity.policy)) {
852                 if (include == null) {
853                     include = new ArrayList<>();
854                 }
855                 include.addAll(hapVerifyInfo.getDistroFilter().screenDensity.value);
856             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenDensity.policy)) {
857                 if (exclude == null) {
858                     exclude = new ArrayList<>();
859                 }
860                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenDensity.value).
861                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
862             } else {
863                 LOG.error("HapVerify::checkScreenDensityCovered input policy is invalid.");
864                 throw new BundleException("HapVerify::checkScreenDensityCovered input policy is invalid.");
865             }
866         }
867         if (include != null) {
868             include = include.stream().distinct().collect(Collectors.toList());
869         }
870         if (exclude != null) {
871             exclude = exclude.stream().distinct().collect(Collectors.toList());
872         }
873         if (screenDensity == null) {
874             return checkEntryPolicyValueCoverAll(include, exclude);
875         }
876         return checkPolicyValueCovered(screenDensity.policy, screenDensity.value, include, exclude);
877     }
878 
879     /**
880      * check feature countryCode is subset of entry countryCode
881      *
882      * @param countryCode is the countryCode of feature hap
883      * @param entryHaps is the haps that feature matched
884      * @return countryCode is valid
885      */
checkCountryCodeCovered(CountryCode countryCode, List<HapVerifyInfo> entryHaps)886     private static boolean checkCountryCodeCovered(CountryCode countryCode, List<HapVerifyInfo> entryHaps)
887             throws BundleException {
888         List<String> include = null;
889         List<String> exclude = null;
890         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
891             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().countryCode == null) {
892                 return true;
893             }
894             if (hapVerifyInfo.getDistroFilter().countryCode.policy == null) {
895                 LOG.error("HapVerify::checkCountryCodeCovered input none policy.");
896                 return false;
897             }
898             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().countryCode.policy)) {
899                 if (include == null) {
900                     include = new ArrayList<>();
901                 }
902                 include.addAll(hapVerifyInfo.getDistroFilter().countryCode.value);
903             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().countryCode.policy)) {
904                 if (exclude == null) {
905                     exclude = new ArrayList<>();
906                 }
907                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().countryCode.value).
908                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
909             } else {
910                 LOG.error("HapVerify::checkCountryCodeCovered input policy is invalid.");
911                 throw new BundleException("HapVerify::checkCountryCodeCovered input policy is invalid.");
912             }
913         }
914         if (include != null) {
915             include = include.stream().distinct().collect(Collectors.toList());
916         }
917         if (exclude != null) {
918             exclude = exclude.stream().distinct().collect(Collectors.toList());
919         }
920         if (countryCode == null) {
921             return checkEntryPolicyValueCoverAll(include, exclude);
922         }
923         return checkPolicyValueCovered(countryCode.policy, countryCode.value, include, exclude);
924     }
925 
926     /**
927      * check entry policy value covered all value
928      *
929      * @param include is the collection of included value
930      * @param exclude is the collection of excluded value
931      * @return entry policy value covered all value
932      */
checkEntryPolicyValueCoverAll(List<String> include, List<String> exclude)933     private static boolean checkEntryPolicyValueCoverAll(List<String> include, List<String> exclude) {
934         if (include == null) {
935             return exclude == null || exclude.isEmpty();
936         }
937         return exclude != null && include.containsAll(exclude);
938     }
939 
940     /**
941      * check entry policy value covered all value
942      *
943      * @param include is the collection of included value
944      * @param exclude is the collection of excluded value
945      * @return entry policy value covered all value
946      */
checkPolicyValueCovered( String policy, List<String> value, List<String> include, List<String> exclude)947     private static boolean checkPolicyValueCovered(
948         String policy, List<String> value, List<String> include, List<String> exclude) {
949         if (value == null || policy == null) {
950             LOG.error("checkPolicyValueCovered::failed value is null.");
951             return false;
952         }
953         if (EXCLUDE.equals(policy)) {
954             return checkCoveredExcludePolicyValue(value, include, exclude);
955         } else if (INCLUDE.equals(policy)) {
956             return checkCoveredIncludePolicyValue(value, include, exclude);
957         } else {
958             return false;
959         }
960     }
961 
962     /**
963      * check entry covered feature value when feature policy is exclude
964      *
965      * @param value is the feature value
966      * @param include is the included value of entry
967      * @param exclude is the excluded value of entry
968      * @return entry policy value covered feature value
969      */
checkCoveredExcludePolicyValue( List<String> value, List<String> include, List<String> exclude)970     private static boolean checkCoveredExcludePolicyValue(
971         List<String> value, List<String> include, List<String> exclude) {
972         if (include == null) {
973             return exclude == null || value.containsAll(exclude);
974         }
975         if (exclude == null) {
976             return false;
977         }
978         exclude.removeAll(include);
979         return value.containsAll(exclude);
980     }
981 
982     /**
983      * check entry covered feature value when feature policy is include
984      *
985      * @param value is the feature value
986      * @param include is the included value of entry
987      * @param exclude is the excluded value of entry
988      * @return entry policy value covered feature value
989      */
checkCoveredIncludePolicyValue( List<String> value, List<String> include, List<String> exclude)990     private static boolean checkCoveredIncludePolicyValue(
991         List<String> value, List<String> include, List<String> exclude) {
992         if (include == null) {
993             return exclude == null || Collections.disjoint(exclude, value);
994         }
995         if (exclude == null) {
996             return include.containsAll(value);
997         }
998         exclude.removeAll(include);
999         return Collections.disjoint(exclude, value);
1000     }
1001 
1002     /**
1003      * check dependency is valid
1004      *
1005      * @param allHapVerifyInfo is all input hap module
1006      * @return true if dependency is valid
1007      * @throws BundleException when input hapVerify is invalid
1008      */
checkDependencyIsValid(List<HapVerifyInfo> allHapVerifyInfo)1009     private static boolean checkDependencyIsValid(List<HapVerifyInfo> allHapVerifyInfo) throws BundleException {
1010         if (allHapVerifyInfo.isEmpty()) {
1011             LOG.error("HapVerify::checkDependencyIsValid failed, input none hap.");
1012             throw new BundleException("HapVerify::checkDependencyIsValid failed, input none hap.");
1013         }
1014         boolean isInstallationFree = allHapVerifyInfo.get(0).isInstallationFree();
1015         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1016             if (isInstallationFree != hapVerifyInfo.isInstallationFree()) {
1017                 LOG.error("installationFree is different in input hap.");
1018                 return false;
1019             }
1020         }
1021         int depth = isInstallationFree ? SERVICE_DEPTH : APPLICATION_DEPTH;
1022         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1023             List<HapVerifyInfo> dependencyList = new ArrayList<>();
1024             dependencyList.add(hapVerifyInfo);
1025             if (!dfsTraverseDependency(hapVerifyInfo, allHapVerifyInfo, dependencyList, depth)) {
1026                 return false;
1027             }
1028             dependencyList.remove(dependencyList.size() - 1);
1029         }
1030         return true;
1031     }
1032 
1033     /**
1034      * DFS traverse dependency, and check dependency list ia valid
1035      *
1036      * @param hapVerifyInfo the first node of dependency list
1037      * @param allHapVerifyInfo is all input hap module
1038      * @param dependencyList is the current dependency list
1039      * @param depth is th limit of depth
1040      * @return true if dependency list is valid
1041      * @throws BundleException when input hapVerifyInfo is invalid
1042      */
dfsTraverseDependency( HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo, List<HapVerifyInfo> dependencyList, int depth)1043     private static boolean dfsTraverseDependency(
1044         HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo,
1045         List<HapVerifyInfo> dependencyList, int depth) throws BundleException {
1046         // check dependencyList is valid
1047         if (checkDependencyListCirculate(dependencyList)) {
1048             return false;
1049         }
1050         if (dependencyList.size() > depth + 1) {
1051             LOG.error("dependency list depth exceed, dependencyList is "
1052                     + getHapVerifyInfoListNames(dependencyList) + ".");
1053             return false;
1054         }
1055         for (DependencyItem dependency : hapVerifyInfo.getDependencyItemList()) {
1056             if (!dependency.getBundleName().equals(hapVerifyInfo.getBundleName())) {
1057                 continue;
1058             }
1059             if (!checkDependencyInFileList(dependency, allHapVerifyInfo)) {
1060                 LOG.warning("Dependent module " + dependency.getModuleName() + " missing, check the HSP-Path.");
1061                 continue;
1062             }
1063             List<HapVerifyInfo> layerDependencyList = getLayerDependency(
1064                     dependency.getModuleName(), hapVerifyInfo, allHapVerifyInfo);
1065             for (HapVerifyInfo item : layerDependencyList) {
1066                 if (FEATURE.equals(item.getModuleType()) || ENTRY.equals(item.getModuleType())) {
1067                     LOG.error("HAP or HSP cannot depend on HAP" + item.getModuleName() + ".");
1068                     return false;
1069                 }
1070                 dependencyList.add(item);
1071                 if (!dfsTraverseDependency(item, allHapVerifyInfo, dependencyList, depth)) {
1072                     return false;
1073                 }
1074                 dependencyList.remove(dependencyList.size() - 1);
1075             }
1076         }
1077         return true;
1078     }
1079 
checkDependencyInFileList( DependencyItem dependencyItem, List<HapVerifyInfo> allHapVerifyInfo)1080     private static boolean checkDependencyInFileList(
1081             DependencyItem dependencyItem, List<HapVerifyInfo> allHapVerifyInfo) {
1082         String moduleName = dependencyItem.getModuleName();
1083         String bundleName = dependencyItem.getBundleName();
1084         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1085             if (moduleName.equals(hapVerifyInfo.getModuleName()) && bundleName.equals(hapVerifyInfo.getBundleName())) {
1086                 return true;
1087             }
1088         }
1089         return false;
1090     }
1091 
1092     /**
1093      * get one layer dependency module by moduleName
1094      *
1095      * @param moduleName is the dependency moduleName of module
1096      * @param hapVerifyInfo the first node of dependency list
1097      * @param allHapVerifyInfo is all input hap module
1098      * @return a layer dependency list
1099      */
getLayerDependency( String moduleName, HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo)1100     private static List<HapVerifyInfo> getLayerDependency(
1101         String moduleName, HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo) throws BundleException {
1102         List<HapVerifyInfo> layerHapVerifyInfoList = new ArrayList<>();
1103         for (HapVerifyInfo item : allHapVerifyInfo) {
1104             if (item.getModuleName().equals(moduleName) && checkModuleJoint(hapVerifyInfo, item)) {
1105                 layerHapVerifyInfoList.add(item);
1106             }
1107         }
1108         return layerHapVerifyInfoList;
1109     }
1110 
1111     /**
1112      * check two module is joint
1113      *
1114      * @param infoLeft is one hapVerifyInfo
1115      * @param infoRight is another hapVerifyInfo
1116      * @return true if dependency list is valid
1117      */
checkModuleJoint(HapVerifyInfo infoLeft, HapVerifyInfo infoRight)1118     private static boolean checkModuleJoint(HapVerifyInfo infoLeft, HapVerifyInfo infoRight) throws BundleException {
1119         return !checkDuplicatedIsValid(infoLeft, infoRight);
1120     }
1121 
1122     /**
1123      * check dependency list is circulate
1124      *
1125      * @param dependencyList is current dependency list
1126      * @return true if dependency list is circulate
1127      */
checkDependencyListCirculate(List<HapVerifyInfo> dependencyList)1128     private static boolean checkDependencyListCirculate(List<HapVerifyInfo> dependencyList) throws BundleException {
1129         for (int i = 0; i < dependencyList.size() - 1; ++i) {
1130             for (int j = i + 1; j < dependencyList.size(); ++j) {
1131                 if (isSameHapVerifyInfo(dependencyList.get(i), dependencyList.get(j))) {
1132                     LOG.error("circular dependency, dependencyList is "
1133                             + getHapVerifyInfoListNames(dependencyList) + ".");
1134                     return true;
1135                 }
1136             }
1137         }
1138         return false;
1139     }
1140 
1141     /**
1142      * check two hapVerifyInfo is same.If two module has same moduleName and joint, they are the same hapVerifyInfo
1143      *
1144      * @param infoLeft is one hapVerifyInfo
1145      * @param infoRight is another hapVerifyInfo
1146      * @return true two hapVerifyInfo is same
1147      */
isSameHapVerifyInfo(HapVerifyInfo infoLeft, HapVerifyInfo infoRight)1148     private static boolean isSameHapVerifyInfo(HapVerifyInfo infoLeft, HapVerifyInfo infoRight) throws BundleException {
1149         if (!infoLeft.getModuleName().equals(infoRight.getModuleName())) {
1150             return false;
1151         }
1152         return checkModuleJoint(infoLeft, infoRight);
1153     }
1154 
1155     /**
1156      * get moduleNames from List<HapVerifyInfo>
1157      *
1158      * @param hapVerifyInfoList is hapVerifyInfo list
1159      * @return true two hapVerifyInfo is same
1160      */
getHapVerifyInfoListNames(List<HapVerifyInfo> hapVerifyInfoList)1161     private static List<String> getHapVerifyInfoListNames(List<HapVerifyInfo> hapVerifyInfoList) {
1162         List<String> moduleNames = new ArrayList<>();
1163         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1164             moduleNames.add((hapVerifyInfo.getModuleName()));
1165         }
1166         return moduleNames;
1167     }
1168 
checkAtomicServiceModuleSize(List<HapVerifyInfo> hapVerifyInfoList)1169     private static boolean checkAtomicServiceModuleSize(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1170         if (hapVerifyInfoList.isEmpty()) {
1171             LOG.error("checkAtomicServiceIsValid failed, hapVerifyInfoList is empty.");
1172             return false;
1173         }
1174         int entryLimit = hapVerifyInfoList.get(0).getEntrySizeLimit();
1175         int notEntryLimit = hapVerifyInfoList.get(0).getNotEntrySizeLimit();
1176         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1177             List<String> dependencies = getModuleDependency(hapVerifyInfo, hapVerifyInfoList);
1178             List<HapVerifyInfo> dependenciesInfos = new ArrayList<>();
1179             for (String module : dependencies) {
1180                 HapVerifyInfo info = findAtomicServiceHapVerifyInfo(module, hapVerifyInfoList);
1181                 dependenciesInfos.add(info);
1182             }
1183             long fileSize = hapVerifyInfo.getFileLength();
1184             for (HapVerifyInfo dependency : dependenciesInfos) {
1185                 if (dependency == null) {
1186                     continue;
1187                 }
1188                 fileSize += dependency.getFileLength();
1189             }
1190             if (hapVerifyInfo.getModuleType().equals(ENTRY) && (fileSize >= entryLimit * FILE_LENGTH_1M)) {
1191                 LOG.error("module " + hapVerifyInfo.getModuleName() + " and it's dependencies size is " +
1192                         getCeilFileSize(fileSize, entryLimit) + "MB, which is overlarge than " + entryLimit + "MB.");
1193                 return false;
1194             }
1195             if (!hapVerifyInfo.getModuleType().equals(ENTRY) && (fileSize >= notEntryLimit * FILE_LENGTH_1M)) {
1196                 LOG.error("module " + hapVerifyInfo.getModuleName() + " and it's dependencies size is " +
1197                         getCeilFileSize(fileSize, notEntryLimit) +
1198                         "MB, which is overlarge than " + notEntryLimit + "MB.");
1199                 return false;
1200             }
1201         }
1202         return true;
1203     }
1204 
getCeilFileSize(long fileSize, int sizeLimit)1205     private static double getCeilFileSize(long fileSize, int sizeLimit) {
1206         double threshold = Double.valueOf(sizeLimit) + FILE_SIZE_OFFSET_DOUBLE;
1207         double size = new BigDecimal((float) fileSize
1208                 / FILE_LENGTH_1M).setScale(FILE_SIZE_DECIMAL_PRECISION, BigDecimal.ROUND_HALF_UP).doubleValue();
1209         if (size < threshold && size >= sizeLimit) {
1210             size = threshold;
1211         }
1212         return size;
1213     }
1214 
getDeviceHapVerifyInfoMap(List<HapVerifyInfo> hapVerifyInfoList)1215     private static Map<String, List<HapVerifyInfo>> getDeviceHapVerifyInfoMap(List<HapVerifyInfo> hapVerifyInfoList)
1216             throws BundleException {
1217         if (hapVerifyInfoList.isEmpty()) {
1218             LOG.error("getDeviceHapVerifyInfoMap failed, hapVerifyInfoList is empty.");
1219             throw new BundleException("getDeviceHapVerifyInfoMap failed, hapVerifyInfoList is empty.");
1220         }
1221         Map<String, List<HapVerifyInfo>> deviceInfoMap = new HashMap<String, List<HapVerifyInfo>>();
1222         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1223             List<String> deviceTypes = hapVerifyInfo.getDeviceType();
1224             for (String device : deviceTypes) {
1225                 if (!deviceInfoMap.containsKey(device)) {
1226                     List<HapVerifyInfo> infos = new ArrayList<>();
1227                     infos.add(hapVerifyInfo);
1228                     deviceInfoMap.put(device, infos);
1229                 } else {
1230                     deviceInfoMap.get(device).add(hapVerifyInfo);
1231                 }
1232             }
1233         }
1234         return deviceInfoMap;
1235     }
1236 
checkAtomicServiceIsValid(List<HapVerifyInfo> hapVerifyInfoList)1237     private static boolean checkAtomicServiceIsValid(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1238         if (hapVerifyInfoList.isEmpty()) {
1239             LOG.error("checkAtomicServiceIsValid failed, hapVerifyInfoList is empty.");
1240             return false;
1241         }
1242         String bundleType = hapVerifyInfoList.get(0).getBundleType();
1243         if (!bundleType.equals(ATOMIC_SERVICE)) {
1244             return true;
1245         }
1246         boolean isStage = hapVerifyInfoList.get(0).isStageModule();
1247         if (!isStage) {
1248             return true;
1249         }
1250         // check preloads is valid
1251         Map<String, List<HapVerifyInfo>> deviceInfoMap = getDeviceHapVerifyInfoMap(hapVerifyInfoList);
1252         for (String device : deviceInfoMap.keySet()) {
1253             List<HapVerifyInfo> hapVerifyInfos = deviceInfoMap.get(device);
1254             if (!checkAtomicServiceSumLimit(hapVerifyInfos)) {
1255                 LOG.error("checkAtomicServiceSumLimit failed on device: " + device);
1256                 return false;
1257             }
1258             if (!checkAtomicServicePreloadsIsValid(hapVerifyInfos)) {
1259                 LOG.error("checkAtomicServicePreloadsIsValid failed on device " + device + ".");
1260                 return false;
1261             }
1262         }
1263         // check file size is valid
1264         if (!checkFileSizeIsValid(hapVerifyInfoList)) {
1265             LOG.error("checkFileSizeIsValid failed.");
1266             return false;
1267         }
1268         return true;
1269     }
1270 
checkAtomicServiceSumLimit(List<HapVerifyInfo>hapVerifyInfos)1271     private static boolean checkAtomicServiceSumLimit(List<HapVerifyInfo>hapVerifyInfos) {
1272         int sumLimit = hapVerifyInfos.get(0).getSumSizeLimit();
1273         if (!hapVerifyInfos.get(0).isStageModule()) {
1274             return true;
1275         }
1276         long fileSize = 0L;
1277         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
1278             fileSize += hapVerifyInfo.getFileLength();
1279             if (fileSize >= sumLimit * FILE_LENGTH_1M) {
1280                 LOG.error("The total file size is " + getCeilFileSize(fileSize, sumLimit) +
1281                         "MB, greater than " + sumLimit + "MB.");
1282                 return false;
1283             }
1284         }
1285         return true;
1286     }
1287 
checkAtomicServicePreloadsIsValid(List<HapVerifyInfo> hapVerifyInfoList)1288     private static boolean checkAtomicServicePreloadsIsValid(List<HapVerifyInfo> hapVerifyInfoList)
1289             throws BundleException {
1290         if (hapVerifyInfoList.isEmpty()) {
1291             LOG.error("checkAtomicServicePreloadsIsValid failed, hapVerifyInfoList is empty.");
1292             throw new BundleException("checkAtomicServicePreloadsIsValid failed, hapVerifyInfoList is empty.");
1293         }
1294         List<String> moduleNames = new ArrayList<>();
1295         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1296             moduleNames.add(hapVerifyInfo.getModuleName());
1297         }
1298         // check preload module is existed and not self
1299         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1300             List<String> preloadModuleName = new ArrayList<>();
1301             List<PreloadItem> preloadItems = hapVerifyInfo.getPreloadItems();
1302             for (PreloadItem preloadItem : preloadItems) {
1303                 String moduleName = preloadItem.getModuleName();
1304                 if (preloadModuleName.contains(moduleName)) {
1305                     LOG.error("preloads config a duplicate module " + moduleName +
1306                             " in " + hapVerifyInfo.getModuleName() + ".");
1307                     return false;
1308                 }
1309                 preloadModuleName.add(moduleName);
1310                 if (!moduleNames.contains(moduleName)) {
1311                     LOG.error("preloads config a invalid module " + moduleName +
1312                             " in " + hapVerifyInfo.getModuleName() + ".");
1313                     return false;
1314                 }
1315                 if (moduleName.equals(hapVerifyInfo.getModuleName())) {
1316                     LOG.error("can not preload self, " + hapVerifyInfo.getModuleName() + " preload self.");
1317                     return false;
1318                 }
1319             }
1320         }
1321         // check feature preload is valid
1322         Map<String, String> moduleNameWithType = new HashMap<>();
1323         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1324             moduleNameWithType.put(hapVerifyInfo.getModuleName(), hapVerifyInfo.getModuleType());
1325         }
1326         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1327             List<PreloadItem> preloadItems = hapVerifyInfo.getPreloadItems();
1328             for (PreloadItem preloadItem : preloadItems) {
1329                 String moduleName = preloadItem.getModuleName();
1330                 if (moduleNameWithType.get(moduleName).equals(ENTRY)
1331                         || moduleNameWithType.get(moduleName).equals(HAR)) {
1332                     LOG.error("feature or shared can not preload entry or har, "
1333                             + hapVerifyInfo.getModuleName() + " preloads a "
1334                     + moduleNameWithType.get(moduleName) + " module.");
1335                     return false;
1336                 }
1337             }
1338         }
1339 
1340         return true;
1341     }
1342 
checkFileSizeIsValid(List<HapVerifyInfo> hapVerifyInfoList)1343     private static boolean checkFileSizeIsValid(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1344         if (hapVerifyInfoList.isEmpty()) {
1345             LOG.error("checkFileSizeIsValid failed, hapVerifyInfoList is empty.");
1346             throw new BundleException("checkFileSizeIsValid failed, hapVerifyInfoList is empty.");
1347         }
1348         if (!checkFileSize(hapVerifyInfoList)) {
1349             LOG.error("checkFileSize failed.");
1350             return false;
1351         }
1352         return true;
1353     }
1354 
checkFileSize(List<HapVerifyInfo> hapVerifyInfoList)1355     private static boolean checkFileSize(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1356         if (hapVerifyInfoList.isEmpty()) {
1357             LOG.error("checkFileSizeWhenSplit failed, hapVerifyInfoList is empty.");
1358             throw new BundleException("checkFileSizeWhenSplit failed, hapVerifyInfoList is empty.");
1359         }
1360         // check single file length
1361         int entryLimit = hapVerifyInfoList.get(0).getEntrySizeLimit();
1362         int notEntryLimit = hapVerifyInfoList.get(0).getNotEntrySizeLimit();
1363         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1364             if (hapVerifyInfo.getModuleType().equals(ENTRY) &&
1365                     (hapVerifyInfo.getFileLength() >= entryLimit * FILE_LENGTH_1M)) {
1366                 LOG.error("module " + hapVerifyInfo.getModuleName() + "'s size is " +
1367                         getCeilFileSize(hapVerifyInfo.getFileLength(), entryLimit) +
1368                         "MB, which is overlarge than " + entryLimit + "MB.");
1369                 return false;
1370             }
1371             if (!hapVerifyInfo.getModuleType().equals(ENTRY) &&
1372                     (hapVerifyInfo.getFileLength() >= notEntryLimit * FILE_LENGTH_1M)) {
1373                 LOG.error("module " + hapVerifyInfo.getModuleName() + "'s size is " +
1374                         getCeilFileSize(hapVerifyInfo.getFileLength(), notEntryLimit) +
1375                         "MB, which is overlarge than " + notEntryLimit + "MB.");
1376                 return false;
1377             }
1378         }
1379 
1380         Map<String, List<HapVerifyInfo>> deviceInfoMap = getDeviceHapVerifyInfoMap(hapVerifyInfoList);
1381         for (String device : deviceInfoMap.keySet()) {
1382             List<HapVerifyInfo>hapVerifyInfoList1 = deviceInfoMap.get(device);
1383             if (!checkAtomicServiceModuleSize(hapVerifyInfoList1)) {
1384                 LOG.error("checkAtomicServiceModuleSize failed on device " + device + ".");
1385                 return false;
1386             }
1387         }
1388         return true;
1389     }
1390 
getModuleDependency(HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> hapVerifyInfoList)1391     private static List<String> getModuleDependency(HapVerifyInfo hapVerifyInfo,
1392                                                     List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1393         List<String> dependencyModules = new ArrayList<>();
1394         dependencyModules.addAll(hapVerifyInfo.getDependencies());
1395         List<String> dependencyItems = hapVerifyInfo.getDependencies();
1396         for (String dependency : dependencyItems) {
1397             HapVerifyInfo dependencyHapVerifyInfo = findAtomicServiceHapVerifyInfo(dependency, hapVerifyInfoList);
1398             if (dependencyHapVerifyInfo == null) {
1399                 continue;
1400             }
1401             List<String> childDependencies = getModuleDependency(dependencyHapVerifyInfo, hapVerifyInfoList);
1402             for (String childDependency : childDependencies) {
1403                 if (!dependencyModules.contains(childDependency)) {
1404                     dependencyModules.add(childDependency);
1405                 }
1406             }
1407         }
1408         return dependencyModules;
1409     }
1410 
findAtomicServiceHapVerifyInfo(String moduleName, List<HapVerifyInfo> hapVerifyInfoList)1411     private static HapVerifyInfo findAtomicServiceHapVerifyInfo(String moduleName,
1412                                                                 List<HapVerifyInfo> hapVerifyInfoList) {
1413         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1414             if (hapVerifyInfo.getModuleName().equals(moduleName)) {
1415                 return hapVerifyInfo;
1416             }
1417         }
1418         return null;
1419     }
1420 }
1421