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