• 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.Optional;
27 import java.util.Set;
28 import java.util.Objects;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31 
32 /**
33  * check hap is verify.
34  */
35 class HapVerify {
36     private static final String INCLUDE = "include";
37     private static final String EXCLUDE = "exclude";
38     private static final Log LOG = new Log(HapVerify.class.toString());
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 = "FAQ";
45     private static final String ATOMIC_SERVICE = "atomicService";
46     private static final String TYPE_SHARED = "shared";
47     private static final long FILE_LENGTH_1M = 1024 * 1024L;
48     private static final long FILE_LENGTH_1KB = 1024L;
49     private static final double FILE_SIZE_OFFSET_DOUBLE = 0.01d;
50     private static final int FILE_SIZE_DECIMAL_PRECISION = 2;
51     private static final String HAP_SUFFIX = ".hap";
52     private static final String HSP_SUFFIX = ".hsp";
53     private static final String APP_PLUGIN = "appPlugin";
54 
55     /**
56      * check hap is verify.
57      *
58      * @param hapVerifyInfos is the collection of hap infos
59      * @return the result
60      * @throws BundleException Throws this exception if the json is not standard
61      */
checkHapIsValid(List<HapVerifyInfo> hapVerifyInfos)62     public static boolean checkHapIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
63         if (hapVerifyInfos == null || hapVerifyInfos.isEmpty()) {
64             String errMsg = "Hap verify infos is null or empty.";
65             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(errMsg));
66             return false;
67         }
68         // check app is plugin app
69         if (!checkIsPluginApp(hapVerifyInfos)) {
70             LOG.error("checkIsPluginApp failed");
71             return false;
72         }
73         // check app variable is same
74         if (!checkAppFieldsIsSame(hapVerifyInfos)) {
75             String errMsg = "Some app variable is different.";
76             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
77             return false;
78         }
79         // check moduleName is valid
80         if (!checkModuleNameIsValid(hapVerifyInfos)) {
81             return false;
82         }
83         // check package is valid
84         if (!checkPackageNameIsValid(hapVerifyInfos)) {
85             String errMsg = "Check packageName is duplicated.";
86             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
87             return false;
88         }
89         // check entry is valid
90         if (!checkEntryIsValid(hapVerifyInfos)) {
91             return false;
92         }
93         // check dependency is valid
94         if (!checkDependencyIsValid(hapVerifyInfos)) {
95             String errMsg = "Check module dependency is invalid.";
96             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
97             return false;
98         }
99         // check atomic service is valid
100         if (!checkAtomicServiceIsValid(hapVerifyInfos)) {
101             String errMsg = "Check atomicService is invalid.";
102             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
103             return false;
104         }
105         // check ability is valid
106         if (!checkAbilityNameIsValid(hapVerifyInfos)) {
107             LOG.info("Ability name is duplicated.");
108         }
109         // check targetModuleName
110         if (!checkTargetModuleNameIsExisted(hapVerifyInfos)) {
111             String errMsg = "Target module cannot found.";
112             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
113             return false;
114         }
115         if (!checkCompileSdkIsValid(hapVerifyInfos)) {
116             String errMsg = "The compileSdkType of each module is different.";
117             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
118             return false;
119         }
120         if (!checkProxyDataUriIsUnique(hapVerifyInfos)) {
121             String errMsg = "The values of uri in proxyData of module.json are not unique.";
122             LOG.error(PackingToolErrMsg.CHECK_HAP_INVALID.toString(errMsg));
123             return false;
124         }
125         if (!checkContinueTypeIsValid(hapVerifyInfos)) {
126             return false;
127         }
128         return true;
129     }
130 
checkIsPluginApp(List<HapVerifyInfo> hapVerifyInfos)131     private static boolean checkIsPluginApp(List<HapVerifyInfo> hapVerifyInfos) {
132         boolean hasPluginBundle = hapVerifyInfos.stream()
133             .anyMatch(hapVerifyInfo -> hapVerifyInfo.getBundleType().equals(APP_PLUGIN));
134         if (!hasPluginBundle) {
135             return true;
136         }
137         if (hapVerifyInfos.size() != 1) {
138             LOG.error("plugin App must contain only one element");
139             return false;
140         }
141         if (hapVerifyInfos.get(0).getFileType() != HSP_SUFFIX) {
142             LOG.error("plugin App must be of type hsp");
143             return false;
144         }
145         return true;
146     }
147 
checkContinueTypeIsValid(List<HapVerifyInfo> hapVerifyInfos)148     private static boolean checkContinueTypeIsValid(List<HapVerifyInfo> hapVerifyInfos) {
149         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
150             if (!checkContinueTypeIsValid(hapVerifyInfo)) {
151                 return false;
152             }
153         }
154         for (int i = 0; i < hapVerifyInfos.size(); i++) {
155             for (int j = i + 1; j < hapVerifyInfos.size(); j++) {
156                 if (!checkContinueTypeIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
157                     return false;
158                 }
159             }
160         }
161         return true;
162     }
163 
checkContinueTypeIsValid(HapVerifyInfo hapVerifyInfo)164     private static boolean checkContinueTypeIsValid(HapVerifyInfo hapVerifyInfo) {
165         List<String> abilityNames = hapVerifyInfo.getAbilityNames();
166         if (abilityNames.size() < 2) {
167             return true;
168         }
169         for (int i = 0; i < abilityNames.size(); i++) {
170             List<String> typeList = hapVerifyInfo.getContinueTypeMap().get(abilityNames.get(i));
171             if (typeList == null) {
172                 continue;
173             }
174             for (int j = i + 1; j < abilityNames.size(); j++) {
175                 List<String> typeList2 = hapVerifyInfo.getContinueTypeMap().get(abilityNames.get(j));
176                 if (typeList2 == null) {
177                     continue;
178                 }
179                 if (!Collections.disjoint(typeList, typeList2)) {
180                     String cause = "Module(" + hapVerifyInfo.getModuleName() + "), Ability(" +
181                             abilityNames.get(i) + ") and Ability(" +
182                             abilityNames.get(j) + ") have same continueType.\n";
183                     cause += "Ability(" + abilityNames.get(i) + ") have continueType: " + typeList + ", ";
184                     cause += "Another Ability(" + abilityNames.get(j) + ") have continueType: " + typeList2 + ".";
185                     String solution = "Please ensure that the continueType for different abilities does not overlap.";
186                     LOG.error(PackingToolErrMsg.CONTINUE_TYPE_INVALID.toString(cause, solution));
187                     return false;
188                 }
189             }
190         }
191         return true;
192     }
193 
checkContinueTypeIsValid(HapVerifyInfo hapVerifyInfo, HapVerifyInfo hapVerifyInfo2)194     private static boolean checkContinueTypeIsValid(HapVerifyInfo hapVerifyInfo, HapVerifyInfo hapVerifyInfo2) {
195         if (Collections.disjoint(hapVerifyInfo.getDeviceType(), hapVerifyInfo2.getDeviceType())) {
196             return true;
197         }
198         List<String> typeList = hapVerifyInfo.getContinueTypeMap().values().stream()
199                 .flatMap(Collection::stream).collect(Collectors.toList());
200         List<String> typeList2 = hapVerifyInfo2.getContinueTypeMap().values().stream()
201                 .flatMap(Collection::stream).collect(Collectors.toList());
202         if (!Collections.disjoint(typeList, typeList2)) {
203             String cause = "Conflict detected between modules due to overlapping deviceType and continueType:\n" +
204                     "- Module(" + hapVerifyInfo.getModuleName() + ") with deviceType: " +
205                     hapVerifyInfo.getDeviceType() + " and continueType: " + typeList + "\n" +
206                     "- Module(" + hapVerifyInfo2.getModuleName() + ") with deviceType: " +
207                     hapVerifyInfo2.getDeviceType() + " and continueType: " + typeList2;
208 
209             String solution = "Ensure that the continueType fields in these modules are different. " +
210                     "Update either (" + hapVerifyInfo.getModuleName() + ") or (" + hapVerifyInfo2.getModuleName() +
211                     ") to avoid continueType or deviceType overlap.";
212 
213             LOG.error(PackingToolErrMsg.CONTINUE_TYPE_INVALID.toString(cause, solution));
214             return false;
215         }
216         return true;
217     }
218 
219     /**
220      * check inter-app hsp is valid.
221      *
222      * @param hapVerifyInfos is the collection of hap infos
223      * @return the result
224      * @throws BundleException Throws this exception if the json is not standard
225      */
checkSharedApppIsValid(List<HapVerifyInfo> hapVerifyInfos)226     public static boolean checkSharedApppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
227         if (hapVerifyInfos == null || hapVerifyInfos.isEmpty()) {
228             String cause = "Hap verify infos is null or empty";
229             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
230             return false;
231         }
232         String moduleName = hapVerifyInfos.get(0).getModuleName();
233         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
234             if (!moduleName.equals(hapVerifyInfo.getModuleName())) {
235                 String cause = "The module name is different.";
236                 String solution = "When the bundleType is shared, ensure that all modules have the same module name.";
237                 LOG.error(PackingToolErrMsg.CHECK_SHARED_APP_INVALID.toString(cause, solution));
238                 return false;
239             }
240             if (!hapVerifyInfo.getDependencyItemList().isEmpty()) {
241                 String cause = "The shared App cannot depend on other modules.";
242                 String solution = "Remove dependencies settings in 'module.json5' and ensure module does " +
243                         "not contain dependencies.";
244                 LOG.error(PackingToolErrMsg.CHECK_SHARED_APP_INVALID.toString(cause, solution));
245                 return false;
246             }
247             if (!TYPE_SHARED.equals(hapVerifyInfo.getModuleType())) {
248                 String cause = "The module type is not shared.";
249                 String solution = "Ensure module type is shared for all modules.";
250                 LOG.error(PackingToolErrMsg.CHECK_SHARED_APP_INVALID.toString(cause, solution));
251                 return false;
252             }
253         }
254         for (int i = 0; i < hapVerifyInfos.size(); i++) {
255             for (int j = i + 1; j < hapVerifyInfos.size(); j++) {
256                 if (!checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
257                     String cause = "There are duplicated modules in the packing file.";
258                     String solution = "Ensure that there are no duplicated modules.";
259                     LOG.error(PackingToolErrMsg.CHECK_SHARED_APP_INVALID.toString(cause, solution));
260                     return false;
261                 }
262             }
263         }
264         return true;
265     }
266 
267 
268     /**
269      * check whether the app fields in the hap are the same.
270      *
271      * @param hapVerifyInfos is the collection of hap infos
272      * @return true if app fields is same
273      */
checkAppFieldsIsSame(List<HapVerifyInfo> hapVerifyInfos)274     private static boolean checkAppFieldsIsSame(List<HapVerifyInfo> hapVerifyInfos) {
275         if (hapVerifyInfos.isEmpty()) {
276             String cause = "Hap verify infos is empty.";
277             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
278             return false;
279         }
280         HapVerifyInfo verifyInfo = hapVerifyInfos.get(0);
281         Optional<HapVerifyInfo> optional = hapVerifyInfos.stream()
282                 .filter(hapVerifyInfo -> !hapVerifyInfo.getModuleType().equals(TYPE_SHARED))
283                 .findFirst();
284         if (optional.isPresent()) {
285             verifyInfo = optional.get();
286         }
287         List<HapVerifyInfo> hapList = new ArrayList<>();
288         List<HapVerifyInfo> hspList = new ArrayList<>();
289         int hspMinCompatibleVersionCodeMax = -1;
290         int hspTargetApiVersionMax = -1;
291         int hspMinApiVersionMax = -1;
292         VerifyCollection verifyCollection = new VerifyCollection();
293         verifyCollection.bundleName = verifyInfo.getBundleName();
294         verifyCollection.setBundleType(verifyInfo.getBundleType());
295         verifyCollection.vendor = verifyInfo.getVendor();
296         verifyCollection.versionCode = verifyInfo.getVersion().versionCode;
297         verifyCollection.versionName = verifyInfo.getVersion().versionName;
298         verifyCollection.compatibleApiVersion = verifyInfo.getApiVersion().getCompatibleApiVersion();
299         verifyCollection.releaseType = verifyInfo.getApiVersion().getReleaseType();
300         verifyCollection.targetBundleName = verifyInfo.getTargetBundleName();
301         verifyCollection.targetPriority = verifyInfo.getTargetPriority();
302         verifyCollection.debug = verifyInfo.isDebug();
303         verifyCollection.setModuleName(verifyInfo.getModuleName());
304         verifyCollection.setModuleType(verifyInfo.getModuleType());
305         verifyCollection.setMultiAppMode(verifyInfo.getMultiAppMode());
306 
307         List<String> assetAccessGroups = verifyInfo.getAssetAccessGroups();
308         String moduleName = verifyInfo.getModuleName();
309         Optional<HapVerifyInfo> entryOptional = hapVerifyInfos.stream()
310                 .filter(hapVerifyInfo -> ENTRY.equals(hapVerifyInfo.getModuleType()))
311                 .findFirst();
312         if (entryOptional.isPresent()) {
313             moduleName = entryOptional.get().getModuleName();
314             assetAccessGroups = entryOptional.get().getAssetAccessGroups();
315         }
316         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
317             if (!appFieldsIsSame(verifyCollection, hapVerifyInfo)) {
318                 LOG.warning("Module: (" + verifyCollection.getModuleName() + ") and Module: (" +
319                         hapVerifyInfo.getModuleName() + ") has different values.");
320                 return false;
321             }
322             if (!appAssetAccessGroupsIsSame(assetAccessGroups, hapVerifyInfo)) {
323                 LOG.warning("Module: (" + moduleName + ") and Module: (" +
324                         hapVerifyInfo.getModuleName() + ") has different values.");
325             }
326             if (Objects.equals(hapVerifyInfo.getFileType(), HAP_SUFFIX)) {
327                 hapList.add(hapVerifyInfo);
328             } else if (Objects.equals(hapVerifyInfo.getFileType(), HSP_SUFFIX)) {
329                 hspList.add(hapVerifyInfo);
330                 if (hapVerifyInfo.getVersion().minCompatibleVersionCode > hspMinCompatibleVersionCodeMax) {
331                     hspMinCompatibleVersionCodeMax = hapVerifyInfo.getVersion().minCompatibleVersionCode;
332                 }
333                 if (hapVerifyInfo.getApiVersion().getTargetApiVersion() > hspTargetApiVersionMax) {
334                     hspTargetApiVersionMax = hapVerifyInfo.getApiVersion().getTargetApiVersion();
335                 }
336                 if (hapVerifyInfo.getApiVersion().getCompatibleApiVersion() > hspMinApiVersionMax) {
337                     hspMinApiVersionMax = hapVerifyInfo.getApiVersion().getCompatibleApiVersion();
338                 }
339             }
340         }
341         if (!appFieldsIsValid(hapList, hspMinCompatibleVersionCodeMax, hspTargetApiVersionMax, hspMinApiVersionMax)) {
342             return false;
343         }
344         if (!moduleDebugValidation(hapList, hspList)) {
345             return false;
346         }
347         return true;
348     }
349 
appFieldsIsValid(List<HapVerifyInfo> hapVerifyInfos, int minCompatibleVersionCode, int targetApiVersion, int minApiVersion)350     private static boolean appFieldsIsValid(List<HapVerifyInfo> hapVerifyInfos, int minCompatibleVersionCode,
351         int targetApiVersion, int minApiVersion) {
352         if (hapVerifyInfos.isEmpty()) {
353             LOG.warning("Hap verify infos is empty");
354             return true;
355         }
356         HapVerifyInfo hap = hapVerifyInfos.get(0);
357         for (HapVerifyInfo hapInfo : hapVerifyInfos) {
358             if (hap.getVersion().minCompatibleVersionCode != hapInfo.getVersion().minCompatibleVersionCode ||
359                 hap.getApiVersion().getTargetApiVersion() != hapInfo.getApiVersion().getTargetApiVersion() ||
360                 hap.getApiVersion().getCompatibleApiVersion() != hapInfo.getApiVersion().getCompatibleApiVersion()) {
361                 String errMsg = "Hap minCompatibleVersionCode or targetApiVersion or minApiVersion different.";
362                 String solution = "Ensure that the values of 'minCompatibleVersionCode' and 'targetApiVersion' " +
363                         "and 'minApiVersion' in the module.json file of each HAP module are the same.";
364                 LOG.error(PackingToolErrMsg.CHECK_APP_FIELDS_INVALID.toString(errMsg, solution));
365                 return false;
366             }
367         }
368         if (hap.getVersion().minCompatibleVersionCode < minCompatibleVersionCode ||
369             hap.getApiVersion().getTargetApiVersion() < targetApiVersion ||
370             hap.getApiVersion().getCompatibleApiVersion() < minApiVersion) {
371             String cause = "The values of minCompatibleVersionCode or targetApiVersion or minApiVersion in the" +
372                     " module.json file of the HAP module are smaller than those of the HSP module.";
373             String solution = "Ensure that the values of aminCompatibleVersionCode and targetApiVersion" +
374                     " and minApiVersion in the module.json file of the HAP module" +
375                     " are greater than or equal to those of the HSP module.";
376             LOG.error(PackingToolErrMsg.CHECK_APP_FIELDS_INVALID.toString(cause, solution));
377             return false;
378         }
379         return true;
380     }
381 
moduleDebugValidation(List<HapVerifyInfo> hapVerifyInfos, List<HapVerifyInfo> hspVerifyInfos)382     private static boolean moduleDebugValidation(List<HapVerifyInfo> hapVerifyInfos,
383                                                  List<HapVerifyInfo> hspVerifyInfos) {
384         if (hapVerifyInfos.isEmpty()) {
385             LOG.warning("Hap verify infos is empty");
386             return true;
387         }
388         HapVerifyInfo hap = hapVerifyInfos.get(0);
389         for (HapVerifyInfo hapInfo : hapVerifyInfos) {
390             if (hap.isDebug() != hapInfo.isDebug()) {
391                 String errMsg = "The debug fields of multiple Hap are different.";
392                 String solution = "Ensure that the values of 'debug' in the module.json file of each HAP module" +
393                         " are the same.";
394                 LOG.error(PackingToolErrMsg.CHECK_APP_FIELDS_INVALID.toString(errMsg, solution));
395                 return false;
396             }
397         }
398         if (hap.isDebug() || hspVerifyInfos.isEmpty()) {
399             LOG.warning("Hap debug is true or Hsp verify infos is empty");
400             return true;
401         }
402         if (hspVerifyInfos.stream().anyMatch(HapVerifyInfo::isDebug)) {
403             String errMsg = "Detected HAP(s) with debug=false, but some HSP(s) are debug=true.";
404             String solution = "When the debug value of Hap is false,the debug value of Hsp should also be false.";
405             LOG.error(PackingToolErrMsg.CHECK_APP_FIELDS_INVALID.toString(errMsg, solution));
406             return false;
407         }
408         return true;
409     }
410 
appFieldsIsSame(VerifyCollection verifyCollection, HapVerifyInfo hapVerifyInfo)411     private static boolean appFieldsIsSame(VerifyCollection verifyCollection, HapVerifyInfo hapVerifyInfo) {
412         if (hapVerifyInfo.getBundleName().isEmpty() ||
413                 !verifyCollection.bundleName.equals(hapVerifyInfo.getBundleName())) {
414             String errMsg = "The bundleName parameter values are different.";
415             String solution = "Check if the bundleName is the same in different modules.";
416             LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
417             return false;
418         }
419         if (!verifyCollection.getBundleType().equals(hapVerifyInfo.getBundleType())) {
420             String errMsg = "The bundleType parameter values are different.";
421             String solution = "Check if the bundleType is the same in different modules.";
422             LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
423             return false;
424         }
425         if (verifyCollection.versionCode != hapVerifyInfo.getVersion().versionCode) {
426             String errMsg = "The versionCode parameter values are different.";
427             String solution = "Check if the versionCode is the same in different modules.";
428             LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
429             return false;
430         }
431         if (!verifyCollection.releaseType.equals(hapVerifyInfo.getApiVersion().getReleaseType())) {
432             if (verifyCollection.getModuleType().equals(TYPE_SHARED) ||
433                     hapVerifyInfo.getModuleType().equals(TYPE_SHARED)) {
434                 LOG.warning("Module: (" + verifyCollection.getModuleName() + ") and Module: (" +
435                         hapVerifyInfo.getModuleName() + ") has different releaseType.");
436             } else {
437                 String errMsg = "The module releaseType parameter values are different.";
438                 String solution = "Check if the releaseType is the same in different modules.";
439                 LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
440                 return false;
441             }
442         }
443         if (!verifyCollection.targetBundleName.equals(hapVerifyInfo.getTargetBundleName())) {
444             String errMsg = "The targetBundleName parameter values are different.";
445             String solution = "Check if the targetBundleName is the same in different modules.";
446             LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
447             return false;
448         }
449         if (verifyCollection.targetPriority != hapVerifyInfo.getTargetPriority()) {
450             String errMsg = "The targetPriority parameter values are different.";
451             String solution = "Check if the targetPriority is the same in different modules.";
452             LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
453             return false;
454         }
455         if (isEntryOrFeature(verifyCollection.getModuleType()) && isEntryOrFeature(hapVerifyInfo.getModuleType())) {
456             if (!verifyCollection.getMultiAppMode().equals(hapVerifyInfo.getMultiAppMode())) {
457                 String errMsg = "The multiAppMode parameter values are different.";
458                 String solution = "Check if the multiAppMode is the same in different modules.";
459                 LOG.error(PackingToolErrMsg.APP_FIELDS_DIFFERENT_ERROR.toString(errMsg, solution));
460                 return false;
461             }
462         }
463         return true;
464     }
465 
appAssetAccessGroupsIsSame(List<String> assetAccessGroups, HapVerifyInfo hapVerifyInfo)466     private static boolean appAssetAccessGroupsIsSame(List<String> assetAccessGroups, HapVerifyInfo hapVerifyInfo) {
467         if (!new HashSet<>(assetAccessGroups).
468                 equals(new HashSet<>(hapVerifyInfo.getAssetAccessGroups()))){
469             LOG.warning("input module assetAccessGroups is different.");
470             return false;
471         }
472         return true;
473     }
474 
isEntryOrFeature(String moduleType)475     private static boolean isEntryOrFeature(String moduleType) {
476         return ENTRY.equals(moduleType) || FEATURE.equals(moduleType);
477     }
478 
479     /**
480      * check moduleName is valid.
481      *
482      * @param hapVerifyInfos is the collection of hap infos
483      * @return true if moduleName is valid
484      * @throws BundleException Throws this exception if the json is not standard.
485      */
checkModuleNameIsValid(List<HapVerifyInfo> hapVerifyInfos)486     private static boolean checkModuleNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
487         for (int i = 0; i < hapVerifyInfos.size() - 1; ++i) {
488             if (hapVerifyInfos.get(i).getModuleName().isEmpty()) {
489                 String cause = "The module name in the HAP infos is empty.";
490                 String solution = "Ensure that each HAP file contains a valid module name field before verification.";
491                 LOG.error(PackingToolErrMsg.CHECK_MODULE_NAME_INVALID.toString(cause, solution));
492                 throw new BundleException("Check moduleName is valid should not be empty.");
493             }
494             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
495                 if (hapVerifyInfos.get(i).getModuleName().equals(hapVerifyInfos.get(j).getModuleName()) &&
496                     !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
497                     String cause = "Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
498                             hapVerifyInfos.get(j).getModuleName() + ") have the same moduleName, " +
499                             "please check deviceType or distroFilter/distributionFilter of the module.\n" + "Module: " +
500                             hapVerifyInfos.get(i).getModuleName() + " has deviceType " +
501                             hapVerifyInfos.get(i).getDeviceType() + ".\n" + "Another Module: " +
502                             hapVerifyInfos.get(j).getModuleName() + " has deviceType " +
503                             hapVerifyInfos.get(j).getDeviceType() + ".";
504                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(i).getDistroFilter().dump())) {
505                         cause += "\n" + "Module: " + hapVerifyInfos.get(i).getModuleName() + " DistroFilter/DistributionFilter is " +
506                                 hapVerifyInfos.get(i).getDistroFilter().dump() + ".";
507                     }
508                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(j).getDistroFilter().dump())) {
509                         cause += "\n" + "Another Module: " + hapVerifyInfos.get(j).getModuleName() +
510                                 " DistroFilter/DistributionFilter is " + hapVerifyInfos.get(j).getDistroFilter().dump() + ".";
511                     }
512                     String solution = "Make sure the module name is valid and unique.\n" +
513                             "Reference: " + REFERENCE_LINK + ".";
514                     LOG.error(PackingToolErrMsg.CHECK_MODULE_NAME_INVALID.toString(cause, solution));
515                     return false;
516                 }
517             }
518         }
519         return true;
520     }
521 
522     /**
523      * check packageName is valid.
524      *
525      * @param hapVerifyInfos is the collection of hap infos
526      * @return true if moduleName is valid
527      * @throws BundleException Throws this exception if the json is not standard
528      */
checkPackageNameIsValid(List<HapVerifyInfo> hapVerifyInfos)529     private static boolean checkPackageNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
530         for (int i = 0; i < hapVerifyInfos.size() - 1; ++i) {
531             if (hapVerifyInfos.get(i).getPackageName().isEmpty()) {
532                 continue;
533             }
534             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
535                 if (hapVerifyInfos.get(i).getPackageName().equals(hapVerifyInfos.get(j).getPackageName()) &&
536                         !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
537                     String cause = "Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
538                         hapVerifyInfos.get(j).getModuleName() + ") have the same packageName, " +
539                         "please check deviceType or distroFilter of the module.\n" + "Module: " +
540                         hapVerifyInfos.get(i).getModuleName() + " has deviceType " +
541                         hapVerifyInfos.get(i).getDeviceType() + ".\n" + "Another Module: " +
542                         hapVerifyInfos.get(j).getModuleName() + " has deviceType " +
543                         hapVerifyInfos.get(j).getDeviceType() + ".";
544                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(i).getDistroFilter().dump())) {
545                         cause += "\n" + "Module: " + hapVerifyInfos.get(i).getModuleName() + " DistroFilter is " +
546                                 hapVerifyInfos.get(i).getDistroFilter().dump() + ".";
547                     }
548                     if (!EMPTY_STRING.equals(hapVerifyInfos.get(j).getDistroFilter().dump())) {
549                         cause += "\n" + "Another Module: " + hapVerifyInfos.get(j).getModuleName() +
550                                 " DistroFilter is " + hapVerifyInfos.get(j).getDistroFilter().dump() + ".";
551                     }
552                     String solution = "Make sure package name is valid and unique.\n" +
553                             "Reference: " + REFERENCE_LINK + ".";
554                     LOG.error(PackingToolErrMsg.CHECK_PACKAGE_NAME_INVALID.toString(cause, solution));
555                     return false;
556                 }
557             }
558         }
559         return true;
560     }
561 
562     /**
563      * check abilityName is valid.
564      *
565      * @param hapVerifyInfos is the collection of hap infos
566      * @return true if abilityName is valid
567      * @throws BundleException Throws this exception if the json is not standard.
568      */
checkAbilityNameIsValid(List<HapVerifyInfo> hapVerifyInfos)569     private static boolean checkAbilityNameIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
570         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
571             long noDuplicatedCount = hapVerifyInfo.getAbilityNames().stream().distinct().count();
572             if (noDuplicatedCount != hapVerifyInfo.getAbilityNames().size()) {
573                 LOG.warning(
574                         hapVerifyInfo.getModuleName() + " ability duplicated, please rename ability name.");
575                 return false;
576             }
577         }
578         for (int i = 0; i < hapVerifyInfos.size(); ++i) {
579             if (hapVerifyInfos.get(i).getAbilityNames().isEmpty()) {
580                 continue;
581             }
582             for (int j = i + 1; j < hapVerifyInfos.size(); ++j) {
583                 if (!Collections.disjoint(hapVerifyInfos.get(i).getAbilityNames(),
584                         hapVerifyInfos.get(j).getAbilityNames()) &&
585                         !checkDuplicatedIsValid(hapVerifyInfos.get(i), hapVerifyInfos.get(j))) {
586                     LOG.warning("Module: (" + hapVerifyInfos.get(i).getModuleName() + ") and Module: (" +
587                             hapVerifyInfos.get(j).getModuleName() + ") have the same ability name.");
588                     LOG.warning("Module: " + hapVerifyInfos.get(i).getModuleName() + " has ability "
589                         + hapVerifyInfos.get(i).getAbilityNames() + ".");
590                     LOG.warning("Module: " + hapVerifyInfos.get(j).getModuleName() + " has ability "
591                         + hapVerifyInfos.get(j).getAbilityNames() + ".");
592                     LOG.warning("Solution: Make sure ability name is valid and unique.");
593                     LOG.warning("Reference: " + REFERENCE_LINK + ".");
594                     return false;
595                 }
596             }
597         }
598         return true;
599     }
600 
601     /**
602      * check targetModuleName is existed.
603      *
604      * @param hapVerifyInfos is the collection of hap infos
605      * @return true if targetModuleName is erxisted
606      * @throws BundleException Throws this exception if the json is not standard.
607      */
checkTargetModuleNameIsExisted(List<HapVerifyInfo> hapVerifyInfos)608     private static boolean checkTargetModuleNameIsExisted(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
609         List<HapVerifyInfo> internalOverlayHap = new ArrayList<>();
610         List<HapVerifyInfo> nonOverlayHap = new ArrayList<>();
611         List<String> targetModuleList = new ArrayList<>();
612         List<String> moduleList = new ArrayList<>();
613         for (HapVerifyInfo hapInfo : hapVerifyInfos) {
614             if (!hapInfo.getTargetBundleName().isEmpty()) {
615                 return true;
616             }
617             if (!hapInfo.getTargetModuleName().isEmpty()) {
618                 internalOverlayHap.add(hapInfo);
619                 targetModuleList.add(hapInfo.getTargetModuleName());
620                 continue;
621             }
622             nonOverlayHap.add(hapInfo);
623             if (!SHARED_LIBRARY.equals(hapInfo.getModuleType())) {
624                 moduleList.add(hapInfo.getModuleName());
625             }
626         }
627         if (internalOverlayHap.isEmpty()) {
628             return true;
629         }
630         if (nonOverlayHap.isEmpty()) {
631             LOG.error(PackingToolErrMsg.TARGET_MODULE_NAME_NOT_EXIST.toString(
632                     "The target modules are needed to pack with the overlay module."));
633             return false;
634         }
635         if (!moduleList.containsAll(targetModuleList)) {
636             List<String> missingModules = new ArrayList<>(targetModuleList);
637             missingModules.removeAll(moduleList);
638             LOG.error(PackingToolErrMsg.TARGET_MODULE_NAME_NOT_EXIST.toString(
639                     "The following target overlay modules are missing: " + missingModules));
640             return false;
641         }
642 
643 
644         return true;
645     }
646 
checkCompileSdkIsValid(List<HapVerifyInfo> hapVerifyInfos)647     private static boolean checkCompileSdkIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
648         if (hapVerifyInfos.isEmpty()) {
649             String cause = "Hap verify infos is empty";
650             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
651             return false;
652         }
653         String compileSdkType = hapVerifyInfos.get(0).getCompileSdkType();
654         for (HapVerifyInfo info : hapVerifyInfos) {
655             if (!compileSdkType.equals(info.getCompileSdkType())) {
656                 String cause = "CompileSdkType is not the same for all modules.";
657                 String solution = "Ensure that all modules has same compileSdkType.";
658                 LOG.error(PackingToolErrMsg.COMPILE_SDK_TYPE_DIFFERENT.toString(cause, solution));
659                 return false;
660             }
661         }
662         return true;
663     }
664 
checkProxyDataUriIsUnique(List<HapVerifyInfo> hapVerifyInfos)665     private static boolean checkProxyDataUriIsUnique(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
666         if (hapVerifyInfos.isEmpty()) {
667             String cause = "Hap verify infos is empty";
668             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
669             return false;
670         }
671         Set<String> uriSet = new HashSet<>();
672         for (HapVerifyInfo info : hapVerifyInfos) {
673             for (String uri : info.getProxyDataUris()) {
674                 if (uriSet.contains(uri)) {
675                     String moduleName = info.getModuleName();
676                     String cause = "The uri(" + uri + ") in proxyData settings of Module(" + moduleName +
677                             ") is duplicated.";
678                     String solution = "Ensure that the uri in proxyData is unique across different modules.";
679                     LOG.error(PackingToolErrMsg.PROXY_DATA_URI_NOT_UNIQUE.toString(cause, solution));
680                     return false;
681                 } else {
682                     uriSet.add(uri);
683                 }
684             }
685         }
686         return true;
687     }
688 
689     /**
690      * check entry is valid.
691      *
692      * @param hapVerifyInfos is the collection of hap infos
693      * @return true if entry is valid
694      * @throws BundleException Throws this exception if the json is not standard.
695      */
checkEntryIsValid(List<HapVerifyInfo> hapVerifyInfos)696     private static boolean checkEntryIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
697         List<HapVerifyInfo> entryHapVerifyInfos = new ArrayList<>();
698         List<HapVerifyInfo> featureHapVerifyInfos = new ArrayList<>();
699         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
700             if (ENTRY.equals(hapVerifyInfo.getModuleType())) {
701                 entryHapVerifyInfos.add(hapVerifyInfo);
702             } else if (FEATURE.equals(hapVerifyInfo.getModuleType())) {
703                 featureHapVerifyInfos.add(hapVerifyInfo);
704             } else if (!SHARED_LIBRARY.equals(hapVerifyInfo.getModuleType())) {
705                 LOG.warning("Input wrong type module.");
706             }
707         }
708         if (hapVerifyInfos.isEmpty()
709                 || (entryHapVerifyInfos.isEmpty() && (!SHARED_LIBRARY.equals(hapVerifyInfos.get(0).getBundleType())))) {
710             LOG.warning("Warning: has no entry module.");
711         }
712 
713         for (int i = 0; i < entryHapVerifyInfos.size() - 1; ++i) {
714             for (int j = i + 1; j < entryHapVerifyInfos.size(); ++j) {
715                 if (!checkDuplicatedIsValid(entryHapVerifyInfos.get(i), entryHapVerifyInfos.get(j))) {
716                     String cause = "Module(" + entryHapVerifyInfos.get(i).getModuleName() + ") and Module(" +
717                             entryHapVerifyInfos.get(j).getModuleName() + ") are entry, " +
718                             "please check deviceType or distroFilter of the module.\n" + "Module: " +
719                             entryHapVerifyInfos.get(i).getModuleName() + " has deviceType " +
720                             entryHapVerifyInfos.get(i).getDeviceType() + ".\n" + "Another Module: " +
721                             entryHapVerifyInfos.get(j).getModuleName() + " has deviceType " +
722                             entryHapVerifyInfos.get(j).getDeviceType() + ".";
723 
724                     if (!EMPTY_STRING.equals(entryHapVerifyInfos.get(i).getDistroFilter().dump())) {
725                         cause += "\n" + "Module: " + entryHapVerifyInfos.get(i).getModuleName() + " DistroFilter is " +
726                                 entryHapVerifyInfos.get(i).getDistroFilter().dump() + ".";
727                     }
728                     if (!EMPTY_STRING.equals(entryHapVerifyInfos.get(j).getDistroFilter().dump())) {
729                         cause += "\n" + "Another Module: " + entryHapVerifyInfos.get(j).getModuleName() +
730                                 " DistroFilter is " + entryHapVerifyInfos.get(j).getDistroFilter().dump() + ".";
731                     }
732 
733                     String solution = "Make sure the entry module is valid and unique, and the HAP uniqueness " +
734                             "check logic passes.\nReference: " + REFERENCE_LINK + ".";
735                     LOG.error(PackingToolErrMsg.CHECK_ENTRY_INVALID.toString(cause, solution));
736                     return false;
737                 }
738             }
739         }
740 
741         Map<String, List<HapVerifyInfo>> deviceHap = classifyEntry(entryHapVerifyInfos);
742         for (HapVerifyInfo hapVerifyInfo : featureHapVerifyInfos) {
743             if (!checkFeature(hapVerifyInfo, deviceHap)) {
744                 LOG.warning(hapVerifyInfo.getModuleName() + " can not be covered by entry.");
745             }
746         }
747 
748         return true;
749     }
750 
751     /**
752      * check if name duplicated, name is valid.
753      *
754      * @param hapVerifyInfoLeft is one hapVerifyInfo
755      * @param hapVerifyInfoRight is another hapVerifyInfo that name is duplicated with hapVerifyInfoLeft
756      * @return true if moduleName is valid
757      * @throws BundleException Throws this exception if the json is not standard.
758      */
checkDuplicatedIsValid(HapVerifyInfo hapVerifyInfoLeft, HapVerifyInfo hapVerifyInfoRight)759     private static boolean checkDuplicatedIsValid(HapVerifyInfo hapVerifyInfoLeft, HapVerifyInfo hapVerifyInfoRight)
760             throws BundleException {
761         // check deviceType
762         if (Collections.disjoint(hapVerifyInfoLeft.getDeviceType(), hapVerifyInfoRight.getDeviceType())) {
763             return true;
764         }
765         // check distroFilter
766         if (checkDistroFilterDisjoint(hapVerifyInfoLeft.getDistroFilter(), hapVerifyInfoRight.getDistroFilter(),
767                     hapVerifyInfoLeft.getModuleName(), hapVerifyInfoRight.getModuleName())) {
768             return true;
769         }
770 
771         return false;
772     }
773 
774     /**
775      * check two distroFilter is disjoint.
776      *
777      * @param distroFilterLeft is one distroFilter
778      * @param distroFilterRight is another distroFilter will be checked
779      * @param moduleNameLeft is the moduleName associated with distroFilterLeft
780      * @param moduleNameRight is the moduleName associated with distroFilterRight
781      * @return true if two distroFilter is disjoint
782      * @throws BundleException Throws this exception if the json is not standard.
783      */
checkDistroFilterDisjoint(DistroFilter distroFilterLeft, DistroFilter distroFilterRight, String moduleNameLeft, String moduleNameRight)784     private static boolean checkDistroFilterDisjoint(DistroFilter distroFilterLeft, DistroFilter distroFilterRight,
785                 String moduleNameLeft, String moduleNameRight) throws BundleException {
786         if (distroFilterLeft == null || distroFilterRight == null) {
787             return false;
788         }
789         if (distroFilterLeft.apiVersion != null && distroFilterRight.apiVersion != null) {
790             if (checkPolicyValueDisjoint(distroFilterLeft.apiVersion.policy, distroFilterLeft.apiVersion.value,
791                     distroFilterRight.apiVersion.policy, distroFilterRight.apiVersion.value,
792                     moduleNameLeft, moduleNameRight)) {
793                 return true;
794             }
795         }
796         if (distroFilterLeft.screenShape != null && distroFilterRight.screenShape != null) {
797             if (checkPolicyValueDisjoint(distroFilterLeft.screenShape.policy, distroFilterLeft.screenShape.value,
798                     distroFilterRight.screenShape.policy, distroFilterRight.screenShape.value,
799                     moduleNameLeft, moduleNameRight)) {
800                 return true;
801             }
802         }
803         if (distroFilterLeft.screenDensity != null && distroFilterRight.screenDensity != null) {
804             if (checkPolicyValueDisjoint(distroFilterLeft.screenDensity.policy, distroFilterLeft.screenDensity.value,
805                     distroFilterRight.screenDensity.policy, distroFilterRight.screenDensity.value,
806                     moduleNameLeft, moduleNameRight)) {
807                 return true;
808             }
809         }
810         if (distroFilterLeft.screenWindow != null && distroFilterRight.screenWindow != null) {
811             if (checkPolicyValueDisjoint(distroFilterLeft.screenWindow.policy, distroFilterLeft.screenWindow.value,
812                     distroFilterRight.screenWindow.policy, distroFilterRight.screenWindow.value,
813                     moduleNameLeft, moduleNameRight)) {
814                 return true;
815             }
816         }
817         if (distroFilterLeft.countryCode != null && distroFilterRight.countryCode != null) {
818             if (checkPolicyValueDisjoint(distroFilterLeft.countryCode.policy, distroFilterLeft.countryCode.value,
819                     distroFilterRight.countryCode.policy, distroFilterRight.countryCode.value,
820                     moduleNameLeft, moduleNameRight)) {
821                 return true;
822             }
823         }
824         return false;
825     }
826 
827     /**
828      * check two distroFilter variable is disjoint.
829      *
830      * @param policyLeft is one distroFilter variable policy
831      * @param valueLeft is one distroFilter variable value
832      * @param policyRight is another distroFilter variable policy
833      * @param valueRight is another distroFilter variable value
834      * @return true if two variable is disjoint
835      * @throws BundleException Throws this exception if the json is not standard.
836      */
checkPolicyValueDisjoint(String policyLeft, List<String> valueLeft, String policyRight, List<String> valueRight, String moduleNameLeft, String moduleNameRight)837     private static boolean checkPolicyValueDisjoint(String policyLeft, List<String> valueLeft, String policyRight,
838                 List<String> valueRight, String moduleNameLeft, String moduleNameRight) throws BundleException {
839         if (valueLeft == null || valueRight == null) {
840             String errMsg = "The variable 'value' in the distributionFilter setting is empty.";
841             String solution = "Ensure that all distributionFilter file and filter settings has 'value' setting.";
842             solution += "Module " + moduleNameLeft + " and " + moduleNameRight + " can be checked in priority.";
843             LOG.error(PackingToolErrMsg.CHECK_POLICY_DISJOINT_ERROR.toString(errMsg, solution));
844             throw new BundleException("Check policy value disjoint value should not empty.");
845         }
846         if (EXCLUDE.equals(policyLeft) && INCLUDE.equals(policyRight)) {
847             if (valueRight.isEmpty() || valueLeft.containsAll(valueRight)) {
848                 return true;
849             }
850         } else if (INCLUDE.equals(policyLeft) && INCLUDE.equals(policyRight)) {
851             if (Collections.disjoint(valueLeft, valueRight)) {
852                 return true;
853             }
854         } else if (INCLUDE.equals(policyLeft) && EXCLUDE.equals(policyRight)) {
855             if (valueLeft.isEmpty() || valueRight.containsAll(valueLeft)) {
856                 return true;
857             }
858         } else if (EXCLUDE.equals(policyLeft) && EXCLUDE.equals(policyRight)) {
859             return false;
860         } else {
861             String errMsg = "Check distributionFilter 'policy' setting is invalid.";
862             String solution = "Ensure all distributionFilter file and filter settings 'policy' value must " +
863                     "'include' or 'exclude'.\n";
864             solution += "Module " + moduleNameLeft + " and " + moduleNameRight + " can be checked in priority.";
865             LOG.error(PackingToolErrMsg.CHECK_POLICY_DISJOINT_ERROR.toString(errMsg, solution));
866             throw new BundleException("Check policy value disjoint input policy is invalid.");
867         }
868         return false;
869     }
870 
871     /**
872      * classify entry haps by deviceType.
873      *
874      * @param entryHapVerifyInfos is the list od entry hapVerifyInfos
875      * @return deviceHap that is classfied
876      */
classifyEntry(List<HapVerifyInfo> entryHapVerifyInfos)877     private static Map<String, List<HapVerifyInfo>> classifyEntry(List<HapVerifyInfo> entryHapVerifyInfos) {
878         Map<String, List<HapVerifyInfo>> deviceHaps = new HashMap<>();
879         for (HapVerifyInfo hapVerifyInfo : entryHapVerifyInfos) {
880             for (String device : hapVerifyInfo.getDeviceType()) {
881                 if (deviceHaps.containsKey(device)) {
882                     deviceHaps.get(device).add(hapVerifyInfo);
883                 } else {
884                     deviceHaps.put(device, new ArrayList<HapVerifyInfo>());
885                     deviceHaps.get(device).add(hapVerifyInfo);
886                 }
887             }
888         }
889         return deviceHaps;
890     }
891 
892     /**
893      * check feature is valid, deviceType is subset of entry, distroFilter is subset of entry
894      *
895      * @param featureHap the feature hap will be checked
896      * @param deviceHap is the haps that feature matched
897      * @return feature is valid
898      * @throws BundleException when input distroFilter is invalid
899      */
checkFeature(HapVerifyInfo featureHap, Map<String, List<HapVerifyInfo>> deviceHap)900     private static boolean checkFeature(HapVerifyInfo featureHap, Map<String, List<HapVerifyInfo>> deviceHap)
901             throws BundleException {
902         // check deviceType and distroFilter
903         for (String device : featureHap.getDeviceType()) {
904             if (!deviceHap.containsKey(device)) {
905                 LOG.warning("Warning: device " + device + " has feature but has no entry.");
906                 return false;
907             }
908             List<HapVerifyInfo> entryHaps = deviceHap.get(device);
909             if (!checkFeatureDistroFilter(featureHap, entryHaps)) {
910                 LOG.warning(featureHap.getModuleName() +
911                         "'s distroFilter has not covered by entry.");
912                 if (!EMPTY_STRING.equals(featureHap.getDistroFilter().dump())) {
913                     LOG.warning(featureHap.getModuleName() + " has " +
914                             featureHap.getDistroFilter().dump() + ".");
915                 }
916                 return false;
917             }
918         }
919         return true;
920     }
921 
922     /**
923      * check feature is valid, deviceType is subset of entry, distroFilter is subset of entry
924      *
925      * @param featureHap the feature hap will be checked
926      * @param entryHaps is the haps that feature matched
927      * @return feature is valid
928      * @throws BundleException when input policy in invalid
929      */
checkFeatureDistroFilter(HapVerifyInfo featureHap, List<HapVerifyInfo> entryHaps)930     private static boolean checkFeatureDistroFilter(HapVerifyInfo featureHap, List<HapVerifyInfo> entryHaps)
931             throws BundleException {
932         if (featureHap.getDistroFilter() == null) {
933             if (checkApiVersionCovered(null, entryHaps)
934                     && checkScreenShapeCovered(null, entryHaps)
935                     && checkScreenWindowCovered(null, entryHaps)
936                     && checkScreenDensityCovered(null, entryHaps)
937                     && checkCountryCodeCovered(null, entryHaps)) {
938                 return true;
939             } else {
940                 return false;
941             }
942         }
943         if (!checkApiVersionCovered(featureHap.getDistroFilter().apiVersion, entryHaps)) {
944             LOG.warning("HapVerify::checkFeatureDistroFilter failed, apiVersion is not covered.");
945             return false;
946         }
947         if (!checkScreenShapeCovered(featureHap.getDistroFilter().screenShape, entryHaps)) {
948             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenShape is not covered.");
949             return false;
950         }
951         if (!checkScreenWindowCovered(featureHap.getDistroFilter().screenWindow, entryHaps)) {
952             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenWindow is not covered.");
953             return false;
954         }
955         if (!checkScreenDensityCovered(featureHap.getDistroFilter().screenDensity, entryHaps)) {
956             LOG.warning("HapVerify::checkFeatureDistroFilter failed, screenDensity is not covered.");
957             return false;
958         }
959         if (!checkCountryCodeCovered(featureHap.getDistroFilter().countryCode, entryHaps)) {
960             LOG.warning("HapVerify::checkFeatureDistroFilter failed, countryCode is not covered.");
961             return false;
962         }
963         return true;
964     }
965 
966     /**
967      * check feature apiVersion is subset of entry apiVersion
968      *
969      * @param apiVersion is the apiVersion of feature hap
970      * @param entryHaps is the haps that feature matched
971      * @return apiVersion is valid
972      * @throws BundleException when input policy is invalid
973      */
checkApiVersionCovered(ApiVersion apiVersion, List<HapVerifyInfo> entryHaps)974     private static boolean checkApiVersionCovered(ApiVersion apiVersion, List<HapVerifyInfo> entryHaps)
975             throws BundleException {
976         List<String> include = null;
977         List<String> exclude = null;
978         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
979             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().apiVersion == null) {
980                 return true;
981             }
982             if (hapVerifyInfo.getDistroFilter().apiVersion.policy == null) {
983                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
984                     "Entry module(" + hapVerifyInfo.getModuleName() + ") apiVersion policy is null."));
985                 return false;
986             }
987             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().apiVersion.policy)) {
988                 if (include == null) {
989                     include = new ArrayList<>();
990                 }
991                 // take collection of two include value
992                 include.addAll(hapVerifyInfo.getDistroFilter().apiVersion.value);
993             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().apiVersion.policy)) {
994                 if (exclude == null) {
995                     exclude = new ArrayList<>();
996                 }
997                 // take intersection of two exclude value
998                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().apiVersion.value).
999                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
1000             } else {
1001                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1002                     "Entry module(" + hapVerifyInfo.getModuleName() + ") apiVersion policy '" +
1003                     hapVerifyInfo.getDistroFilter().apiVersion.policy + "' is invalid."));
1004                 throw new BundleException("Check apiVersion covered input policy is invalid.");
1005             }
1006         }
1007         if (include != null) {
1008             include = include.stream().distinct().collect(Collectors.toList());
1009         }
1010         if (exclude != null) {
1011             exclude = exclude.stream().distinct().collect(Collectors.toList());
1012         }
1013         if (apiVersion == null) {
1014             return checkEntryPolicyValueCoverAll(include, exclude);
1015         }
1016         return checkPolicyValueCovered(apiVersion.policy, apiVersion.value, include, exclude);
1017     }
1018 
1019     /**
1020      * check feature screenShape is subset of entry screenShape
1021      *
1022      * @param screenShape is the screenShape of feature hap
1023      * @param entryHaps is the haps that feature matched
1024      * @return screenShape is valid
1025      * @throws BundleException when input policy is invalid
1026      */
checkScreenShapeCovered(ScreenShape screenShape, List<HapVerifyInfo> entryHaps)1027     private static boolean checkScreenShapeCovered(ScreenShape screenShape, List<HapVerifyInfo> entryHaps)
1028             throws BundleException {
1029         List<String> include = null;
1030         List<String> exclude = null;
1031         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
1032             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenShape == null) {
1033                 return true;
1034             }
1035             if (hapVerifyInfo.getDistroFilter().screenShape.policy == null) {
1036                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1037                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenShape policy is null."));
1038                 return false;
1039             }
1040             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenShape.policy)) {
1041                 if (include == null) {
1042                     include = new ArrayList<>();
1043                 }
1044                 include.addAll(hapVerifyInfo.getDistroFilter().screenShape.value);
1045             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenShape.policy)) {
1046                 if (exclude == null) {
1047                     exclude = new ArrayList<>();
1048                 }
1049                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenShape.value).
1050                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
1051             } else {
1052                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1053                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenShape policy '" +
1054                     hapVerifyInfo.getDistroFilter().screenShape.policy + "' is invalid."));
1055                 throw new BundleException("Check screenShape covered input policy is invalid.");
1056             }
1057         }
1058         if (include != null) {
1059             include = include.stream().distinct().collect(Collectors.toList());
1060         }
1061         if (exclude != null) {
1062             exclude = exclude.stream().distinct().collect(Collectors.toList());
1063         }
1064         if (screenShape == null) {
1065             return checkEntryPolicyValueCoverAll(include, exclude);
1066         }
1067         return checkPolicyValueCovered(screenShape.policy, screenShape.value, include, exclude);
1068     }
1069 
1070     /**
1071      * check feature screenWindow is subset of entry screenWindow
1072      *
1073      * @param screenWindow is the screenWindow of feature hap
1074      * @param entryHaps is the haps that feature matched
1075      * @return screenWindow is valid
1076      * @throws BundleException when input policy is invalid
1077      */
checkScreenWindowCovered(ScreenWindow screenWindow, List<HapVerifyInfo> entryHaps)1078     private static boolean checkScreenWindowCovered(ScreenWindow screenWindow, List<HapVerifyInfo> entryHaps)
1079             throws BundleException {
1080         List<String> include = null;
1081         List<String> exclude = null;
1082         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
1083             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenWindow == null) {
1084                 return true;
1085             }
1086             if (hapVerifyInfo.getDistroFilter().screenWindow.policy == null) {
1087                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1088                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenWindow policy is null."));
1089                 return false;
1090             }
1091             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenWindow.policy)) {
1092                 if (include == null) {
1093                     include = new ArrayList<>();
1094                 }
1095                 include.addAll(hapVerifyInfo.getDistroFilter().screenWindow.value);
1096             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenWindow.policy)) {
1097                 if (exclude == null) {
1098                     exclude = new ArrayList<>();
1099                 }
1100                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenWindow.value).
1101                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
1102             } else {
1103                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1104                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenWindow policy '" +
1105                     hapVerifyInfo.getDistroFilter().screenWindow.policy + "' is invalid."));
1106                 throw new BundleException("Check screenWindow covered input policy is invalid.");
1107             }
1108         }
1109         if (include != null) {
1110             include = include.stream().distinct().collect(Collectors.toList());
1111         }
1112         if (exclude != null) {
1113             exclude = exclude.stream().distinct().collect(Collectors.toList());
1114         }
1115         if (screenWindow == null) {
1116             return checkEntryPolicyValueCoverAll(include, exclude);
1117         }
1118         return checkPolicyValueCovered(screenWindow.policy, screenWindow.value, include, exclude);
1119     }
1120 
1121     /**
1122      * check feature screenDensity is subset of entry screenDensity
1123      *
1124      * @param screenDensity is the screenDensity of feature hap
1125      * @param entryHaps is the haps that feature matched
1126      * @return screenDensity is valid
1127      * @throws BundleException when input policy is invalid
1128      */
checkScreenDensityCovered(ScreenDensity screenDensity, List<HapVerifyInfo> entryHaps)1129     private static boolean checkScreenDensityCovered(ScreenDensity screenDensity, List<HapVerifyInfo> entryHaps)
1130             throws BundleException {
1131         List<String> include = null;
1132         List<String> exclude = null;
1133         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
1134             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().screenDensity == null) {
1135                 return true;
1136             }
1137             if (hapVerifyInfo.getDistroFilter().screenDensity.policy == null) {
1138                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1139                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenDensity policy is null."));
1140                 return false;
1141             }
1142             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().screenDensity.policy)) {
1143                 if (include == null) {
1144                     include = new ArrayList<>();
1145                 }
1146                 include.addAll(hapVerifyInfo.getDistroFilter().screenDensity.value);
1147             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().screenDensity.policy)) {
1148                 if (exclude == null) {
1149                     exclude = new ArrayList<>();
1150                 }
1151                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().screenDensity.value).
1152                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
1153             } else {
1154                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1155                     "Entry module(" + hapVerifyInfo.getModuleName() + ") screenDensity policy '" +
1156                     hapVerifyInfo.getDistroFilter().screenDensity.policy + "' is invalid."));
1157                 throw new BundleException("Check screenDensity covered input policy is invalid.");
1158             }
1159         }
1160         if (include != null) {
1161             include = include.stream().distinct().collect(Collectors.toList());
1162         }
1163         if (exclude != null) {
1164             exclude = exclude.stream().distinct().collect(Collectors.toList());
1165         }
1166         if (screenDensity == null) {
1167             return checkEntryPolicyValueCoverAll(include, exclude);
1168         }
1169         return checkPolicyValueCovered(screenDensity.policy, screenDensity.value, include, exclude);
1170     }
1171 
1172     /**
1173      * check feature countryCode is subset of entry countryCode
1174      *
1175      * @param countryCode is the countryCode of feature hap
1176      * @param entryHaps is the haps that feature matched
1177      * @return countryCode is valid
1178      * @throws BundleException when input policy is invalid
1179      */
checkCountryCodeCovered(CountryCode countryCode, List<HapVerifyInfo> entryHaps)1180     private static boolean checkCountryCodeCovered(CountryCode countryCode, List<HapVerifyInfo> entryHaps)
1181             throws BundleException {
1182         List<String> include = null;
1183         List<String> exclude = null;
1184         for (HapVerifyInfo hapVerifyInfo : entryHaps) {
1185             if (hapVerifyInfo.getDistroFilter() == null || hapVerifyInfo.getDistroFilter().countryCode == null) {
1186                 return true;
1187             }
1188             if (hapVerifyInfo.getDistroFilter().countryCode.policy == null) {
1189                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1190                     "Entry module(" + hapVerifyInfo.getModuleName() + ") countryCode policy is null."));
1191                 return false;
1192             }
1193             if (INCLUDE.equals(hapVerifyInfo.getDistroFilter().countryCode.policy)) {
1194                 if (include == null) {
1195                     include = new ArrayList<>();
1196                 }
1197                 include.addAll(hapVerifyInfo.getDistroFilter().countryCode.value);
1198             } else if (EXCLUDE.equals(hapVerifyInfo.getDistroFilter().countryCode.policy)) {
1199                 if (exclude == null) {
1200                     exclude = new ArrayList<>();
1201                 }
1202                 exclude = Stream.of(exclude, hapVerifyInfo.getDistroFilter().countryCode.value).
1203                         flatMap(Collection::stream).distinct().collect(Collectors.toList());
1204             } else {
1205                 LOG.error(PackingToolErrMsg.CHECK_FEATURE_DISTRO_FILTER_INVALID.toString(
1206                     "Entry module(" + hapVerifyInfo.getModuleName() + ") countryCode policy '" +
1207                     hapVerifyInfo.getDistroFilter().countryCode.policy + "' is invalid."));
1208                 throw new BundleException("Check countryCode covered input policy is invalid.");
1209             }
1210         }
1211         if (include != null) {
1212             include = include.stream().distinct().collect(Collectors.toList());
1213         }
1214         if (exclude != null) {
1215             exclude = exclude.stream().distinct().collect(Collectors.toList());
1216         }
1217         if (countryCode == null) {
1218             return checkEntryPolicyValueCoverAll(include, exclude);
1219         }
1220         return checkPolicyValueCovered(countryCode.policy, countryCode.value, include, exclude);
1221     }
1222 
1223     /**
1224      * check entry policy value covered all value
1225      *
1226      * @param include is the collection of included value
1227      * @param exclude is the collection of excluded value
1228      * @return entry policy value covered all value
1229      */
checkEntryPolicyValueCoverAll(List<String> include, List<String> exclude)1230     private static boolean checkEntryPolicyValueCoverAll(List<String> include, List<String> exclude) {
1231         if (include == null) {
1232             return exclude == null || exclude.isEmpty();
1233         }
1234         return exclude != null && include.containsAll(exclude);
1235     }
1236 
1237     /**
1238      * check entry policy value covered all value
1239      *
1240      * @param include is the collection of included value
1241      * @param exclude is the collection of excluded value
1242      * @return entry policy value covered all value
1243      */
checkPolicyValueCovered( String policy, List<String> value, List<String> include, List<String> exclude)1244     private static boolean checkPolicyValueCovered(
1245         String policy, List<String> value, List<String> include, List<String> exclude) {
1246         if (value == null || policy == null) {
1247             LOG.warning("checkPolicyValueCovered::failed covered value or policy is null.");
1248             return false;
1249         }
1250         if (EXCLUDE.equals(policy)) {
1251             return checkCoveredExcludePolicyValue(value, include, exclude);
1252         } else if (INCLUDE.equals(policy)) {
1253             return checkCoveredIncludePolicyValue(value, include, exclude);
1254         } else {
1255             return false;
1256         }
1257     }
1258 
1259     /**
1260      * check entry covered feature value when feature policy is exclude
1261      *
1262      * @param value is the feature value
1263      * @param include is the included value of entry
1264      * @param exclude is the excluded value of entry
1265      * @return entry policy value covered feature value
1266      */
checkCoveredExcludePolicyValue( List<String> value, List<String> include, List<String> exclude)1267     private static boolean checkCoveredExcludePolicyValue(
1268         List<String> value, List<String> include, List<String> exclude) {
1269         if (include == null) {
1270             return exclude == null || value.containsAll(exclude);
1271         }
1272         if (exclude == null) {
1273             return false;
1274         }
1275         exclude.removeAll(include);
1276         return value.containsAll(exclude);
1277     }
1278 
1279     /**
1280      * check entry covered feature value when feature policy is include
1281      *
1282      * @param value is the feature value
1283      * @param include is the included value of entry
1284      * @param exclude is the excluded value of entry
1285      * @return entry policy value covered feature value
1286      */
checkCoveredIncludePolicyValue( List<String> value, List<String> include, List<String> exclude)1287     private static boolean checkCoveredIncludePolicyValue(
1288         List<String> value, List<String> include, List<String> exclude) {
1289         if (include == null) {
1290             return exclude == null || Collections.disjoint(exclude, value);
1291         }
1292         if (exclude == null) {
1293             return include.containsAll(value);
1294         }
1295         exclude.removeAll(include);
1296         return Collections.disjoint(exclude, value);
1297     }
1298 
1299     /**
1300      * check dependency is valid
1301      *
1302      * @param allHapVerifyInfo is all input hap module
1303      * @return true if dependency is valid
1304      * @throws BundleException when input hapVerify is invalid
1305      */
checkDependencyIsValid(List<HapVerifyInfo> allHapVerifyInfo)1306     private static boolean checkDependencyIsValid(List<HapVerifyInfo> allHapVerifyInfo) throws BundleException {
1307         if (allHapVerifyInfo.isEmpty()) {
1308             String cause = "Hap verify infos is empty";
1309             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
1310             throw new BundleException("HapVerify::checkDependencyIsValid failed, input none hap.");
1311         }
1312         boolean isInstallationFree = allHapVerifyInfo.get(0).isInstallationFree();
1313         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1314             if (isInstallationFree != hapVerifyInfo.isInstallationFree()) {
1315                 String cause = "The installationFree value is different in input hap.";
1316                 String solution = "Ensure that the installationFree field is same for all hap.";
1317                 LOG.error(PackingToolErrMsg.CHECK_DEPENDENCY_INVALID.toString(cause, solution));
1318                 return false;
1319             }
1320         }
1321         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1322             List<HapVerifyInfo> dependencyList = new ArrayList<>();
1323             dependencyList.add(hapVerifyInfo);
1324             if (!dfsTraverseDependency(hapVerifyInfo, allHapVerifyInfo, dependencyList)) {
1325                 return false;
1326             }
1327             dependencyList.remove(dependencyList.size() - 1);
1328         }
1329         return true;
1330     }
1331 
1332     /**
1333      * DFS traverse dependency, and check dependency list is valid
1334      *
1335      * @param hapVerifyInfo the first node of dependency list
1336      * @param allHapVerifyInfo is all input hap module
1337      * @param dependencyList is the current dependency list
1338      * @return true if dependency list is valid
1339      * @throws BundleException when input hapVerifyInfo is invalid
1340      */
dfsTraverseDependency( HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo, List<HapVerifyInfo> dependencyList)1341     private static boolean dfsTraverseDependency(
1342         HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo,
1343         List<HapVerifyInfo> dependencyList) throws BundleException {
1344         // check dependencyList is valid
1345         if (checkDependencyListCirculate(dependencyList)) {
1346             return false;
1347         }
1348         for (DependencyItem dependency : hapVerifyInfo.getDependencyItemList()) {
1349             if (!dependency.getBundleName().equals(hapVerifyInfo.getBundleName())) {
1350                 continue;
1351             }
1352             if (!checkDependencyInFileList(dependency, allHapVerifyInfo)) {
1353                 LOG.warning("Dependent module " + dependency.getModuleName() + " missing, check the HSP-Path.");
1354                 continue;
1355             }
1356             List<HapVerifyInfo> layerDependencyList = getLayerDependency(
1357                     dependency.getModuleName(), hapVerifyInfo, allHapVerifyInfo);
1358             for (HapVerifyInfo item : layerDependencyList) {
1359                 if (FEATURE.equals(item.getModuleType()) || ENTRY.equals(item.getModuleType())) {
1360                     String cause =
1361                             "HAP or HSP cannot depend on the feature or entry module. The dependeny module(" +
1362                             item.getModuleName() + ") type is feature or entry.";
1363                     String solution = "Remove module dependencies on module (" + item.getModuleName() +
1364                             ") to ensure the dependency list is valid.";
1365                     LOG.error(PackingToolErrMsg.DEPENDENCY_LIST_INVALID.toString(cause, solution));
1366                     return false;
1367                 }
1368                 dependencyList.add(item);
1369                 if (!dfsTraverseDependency(item, allHapVerifyInfo, dependencyList)) {
1370                     return false;
1371                 }
1372                 dependencyList.remove(dependencyList.size() - 1);
1373             }
1374         }
1375         return true;
1376     }
1377 
checkDependencyInFileList( DependencyItem dependencyItem, List<HapVerifyInfo> allHapVerifyInfo)1378     private static boolean checkDependencyInFileList(
1379             DependencyItem dependencyItem, List<HapVerifyInfo> allHapVerifyInfo) {
1380         String moduleName = dependencyItem.getModuleName();
1381         String bundleName = dependencyItem.getBundleName();
1382         for (HapVerifyInfo hapVerifyInfo : allHapVerifyInfo) {
1383             if (moduleName.equals(hapVerifyInfo.getModuleName()) && bundleName.equals(hapVerifyInfo.getBundleName())) {
1384                 return true;
1385             }
1386         }
1387         return false;
1388     }
1389 
1390     /**
1391      * get one layer dependency module by moduleName
1392      *
1393      * @param moduleName is the dependency moduleName of module
1394      * @param hapVerifyInfo the first node of dependency list
1395      * @param allHapVerifyInfo is all input hap module
1396      * @return a layer dependency list
1397      */
getLayerDependency( String moduleName, HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo)1398     private static List<HapVerifyInfo> getLayerDependency(
1399         String moduleName, HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> allHapVerifyInfo) throws BundleException {
1400         List<HapVerifyInfo> layerHapVerifyInfoList = new ArrayList<>();
1401         for (HapVerifyInfo item : allHapVerifyInfo) {
1402             if (item.getModuleName().equals(moduleName) && checkModuleJoint(hapVerifyInfo, item)) {
1403                 layerHapVerifyInfoList.add(item);
1404             }
1405         }
1406         return layerHapVerifyInfoList;
1407     }
1408 
1409     /**
1410      * check two module is joint
1411      *
1412      * @param infoLeft is one hapVerifyInfo
1413      * @param infoRight is another hapVerifyInfo
1414      * @return true if dependency list is valid
1415      */
checkModuleJoint(HapVerifyInfo infoLeft, HapVerifyInfo infoRight)1416     private static boolean checkModuleJoint(HapVerifyInfo infoLeft, HapVerifyInfo infoRight) throws BundleException {
1417         return !checkDuplicatedIsValid(infoLeft, infoRight);
1418     }
1419 
1420     /**
1421      * check dependency list is circulate
1422      *
1423      * @param dependencyList is current dependency list
1424      * @return true if dependency list is circulate
1425      */
checkDependencyListCirculate(List<HapVerifyInfo> dependencyList)1426     private static boolean checkDependencyListCirculate(List<HapVerifyInfo> dependencyList) throws BundleException {
1427         for (int i = 0; i < dependencyList.size() - 1; ++i) {
1428             for (int j = i + 1; j < dependencyList.size(); ++j) {
1429                 if (isSameHapVerifyInfo(dependencyList.get(i), dependencyList.get(j))) {
1430                     String cause = "Circular dependency, dependencyList is " +
1431                             getHapVerifyInfoListNames(dependencyList) + ".";
1432                     String solution = "Please check the dependecy against the module name in the list.\n";
1433                     solution += "Remove circulate dependency module to ensure the dependency list is valid.";
1434                     LOG.error(PackingToolErrMsg.DEPENDENCY_LIST_INVALID.toString(cause, solution));
1435                     return true;
1436                 }
1437             }
1438         }
1439         return false;
1440     }
1441 
1442     /**
1443      * check two hapVerifyInfo is same.If two module has same moduleName and joint, they are the same hapVerifyInfo
1444      *
1445      * @param infoLeft is one hapVerifyInfo
1446      * @param infoRight is another hapVerifyInfo
1447      * @return true two hapVerifyInfo is same
1448      */
isSameHapVerifyInfo(HapVerifyInfo infoLeft, HapVerifyInfo infoRight)1449     private static boolean isSameHapVerifyInfo(HapVerifyInfo infoLeft, HapVerifyInfo infoRight) throws BundleException {
1450         if (!infoLeft.getModuleName().equals(infoRight.getModuleName())) {
1451             return false;
1452         }
1453         return checkModuleJoint(infoLeft, infoRight);
1454     }
1455 
1456     /**
1457      * get moduleNames from List<HapVerifyInfo>
1458      *
1459      * @param hapVerifyInfoList is hapVerifyInfo list
1460      * @return true two hapVerifyInfo is same
1461      */
getHapVerifyInfoListNames(List<HapVerifyInfo> hapVerifyInfoList)1462     private static List<String> getHapVerifyInfoListNames(List<HapVerifyInfo> hapVerifyInfoList) {
1463         List<String> moduleNames = new ArrayList<>();
1464         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1465             moduleNames.add((hapVerifyInfo.getModuleName()));
1466         }
1467         return moduleNames;
1468     }
1469 
checkAtomicServiceModuleAndDependenciesSize(List<HapVerifyInfo> hapVerifyInfoList)1470     private static boolean checkAtomicServiceModuleAndDependenciesSize(List<HapVerifyInfo> hapVerifyInfoList)
1471             throws BundleException {
1472         if (hapVerifyInfoList.isEmpty()) {
1473             String cause = "Hap verify infos is empty";
1474             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
1475             return false;
1476         }
1477         int entryLimit = hapVerifyInfoList.get(0).getEntrySizeLimit();
1478         int notEntryLimit = hapVerifyInfoList.get(0).getNotEntrySizeLimit();
1479         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1480             List<String> dependencies = getModuleDependency(hapVerifyInfo, hapVerifyInfoList);
1481             List<HapVerifyInfo> dependenciesInfos = new ArrayList<>();
1482             for (String module : dependencies) {
1483                 HapVerifyInfo info = findAtomicServiceHapVerifyInfo(module, hapVerifyInfoList);
1484                 dependenciesInfos.add(info);
1485             }
1486             long fileSize = hapVerifyInfo.getFileLength();
1487             for (HapVerifyInfo dependency : dependenciesInfos) {
1488                 if (dependency == null) {
1489                     continue;
1490                 }
1491                 fileSize += dependency.getFileLength();
1492             }
1493             if (hapVerifyInfo.getModuleType().equals(ENTRY) &&
1494                     entryLimit != 0 &&
1495                     (fileSize >= entryLimit * FILE_LENGTH_1KB)) {
1496                 String errMsg = "Module " + hapVerifyInfo.getModuleName() + " and it's dependencies size sum is " +
1497                         getCeilFileSize(fileSize, entryLimit) + "KB, which is overlarge than " + entryLimit + "KB.";
1498                 LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_MODULE_SIZE.toString(errMsg));
1499                 return false;
1500             }
1501             if (!hapVerifyInfo.getModuleType().equals(ENTRY) &&
1502                     notEntryLimit != 0 &&
1503                     (fileSize >= notEntryLimit * FILE_LENGTH_1KB)) {
1504                 String errMsg = "Module " + hapVerifyInfo.getModuleName() + " and it's dependencies size sum is " +
1505                         getCeilFileSize(fileSize, notEntryLimit) + "KB, which is overlarge than " + notEntryLimit +
1506                         "KB.";
1507                 LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_MODULE_SIZE.toString(errMsg));
1508                 return false;
1509             }
1510         }
1511         return true;
1512     }
1513 
getCeilFileSize(long fileSize, int sizeLimit)1514     private static double getCeilFileSize(long fileSize, int sizeLimit) {
1515         double threshold = Double.valueOf(sizeLimit) + FILE_SIZE_OFFSET_DOUBLE;
1516         double size = new BigDecimal((float) fileSize
1517                 / FILE_LENGTH_1KB).setScale(FILE_SIZE_DECIMAL_PRECISION, BigDecimal.ROUND_HALF_UP).doubleValue();
1518         if (size < threshold && size >= sizeLimit) {
1519             size = threshold;
1520         }
1521         return size;
1522     }
1523 
getDeviceHapVerifyInfoMap(List<HapVerifyInfo> hapVerifyInfoList)1524     private static Map<String, List<HapVerifyInfo>> getDeviceHapVerifyInfoMap(List<HapVerifyInfo> hapVerifyInfoList)
1525             throws BundleException {
1526         if (hapVerifyInfoList.isEmpty()) {
1527             String cause = "Hap verify infos is empty";
1528             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
1529             throw new BundleException("getDeviceHapVerifyInfoMap failed, hapVerifyInfoList is empty.");
1530         }
1531         Map<String, List<HapVerifyInfo>> deviceInfoMap = new HashMap<String, List<HapVerifyInfo>>();
1532         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1533             List<String> deviceTypes = hapVerifyInfo.getDeviceType();
1534             for (String device : deviceTypes) {
1535                 if (!deviceInfoMap.containsKey(device)) {
1536                     List<HapVerifyInfo> infos = new ArrayList<>();
1537                     infos.add(hapVerifyInfo);
1538                     deviceInfoMap.put(device, infos);
1539                 } else {
1540                     deviceInfoMap.get(device).add(hapVerifyInfo);
1541                 }
1542             }
1543         }
1544         return deviceInfoMap;
1545     }
1546 
checkAtomicServiceIsValid(List<HapVerifyInfo> hapVerifyInfoList)1547     private static boolean checkAtomicServiceIsValid(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1548         if (hapVerifyInfoList.isEmpty()) {
1549             String cause = "Hap verify infos is empty";
1550             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
1551             return false;
1552         }
1553         String bundleType = hapVerifyInfoList.get(0).getBundleType();
1554         if (!bundleType.equals(ATOMIC_SERVICE)) {
1555             return true;
1556         }
1557         boolean isStage = hapVerifyInfoList.get(0).isStageModule();
1558         if (!isStage) {
1559             return true;
1560         }
1561         // check preloads is valid
1562         Map<String, List<HapVerifyInfo>> deviceInfoMap = getDeviceHapVerifyInfoMap(hapVerifyInfoList);
1563         for (String device : deviceInfoMap.keySet()) {
1564             List<HapVerifyInfo> hapVerifyInfos = deviceInfoMap.get(device);
1565             if (!checkAtomicServicePreloadsIsValid(hapVerifyInfos)) {
1566                 LOG.error(PackingToolErrMsg.CHECK_ATOMICSERVICE_INVALID.toString(
1567                         "Check whether atomicService preloads are valid failed on device " + device + "."));
1568                 return false;
1569             }
1570         }
1571 
1572         return true;
1573     }
1574 
checkAtomicServiceSumLimit(List<HapVerifyInfo>hapVerifyInfos)1575     private static boolean checkAtomicServiceSumLimit(List<HapVerifyInfo>hapVerifyInfos) {
1576         int sumLimit = hapVerifyInfos.get(0).getSumSizeLimit();
1577         if (!hapVerifyInfos.get(0).isStageModule()) {
1578             return true;
1579         }
1580         long fileSize = 0L;
1581         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
1582             fileSize += hapVerifyInfo.getFileLength();
1583             if (fileSize >= sumLimit * FILE_LENGTH_1M) {
1584                 LOG.error("The total file size is " + getCeilFileSize(fileSize, sumLimit) +
1585                         "MB, greater than " + sumLimit + "MB.");
1586                 return false;
1587             }
1588         }
1589         return true;
1590     }
1591 
checkAtomicServicePreloadsIsValid(List<HapVerifyInfo> hapVerifyInfoList)1592     private static boolean checkAtomicServicePreloadsIsValid(List<HapVerifyInfo> hapVerifyInfoList)
1593             throws BundleException {
1594         if (hapVerifyInfoList.isEmpty()) {
1595             String cause = "Hap verify infos is empty.";
1596             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
1597             throw new BundleException("checkAtomicServicePreloadsIsValid failed, hapVerifyInfoList is empty.");
1598         }
1599         List<String> moduleNames = new ArrayList<>();
1600         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1601             moduleNames.add(hapVerifyInfo.getModuleName());
1602         }
1603         // check preload module is existed and not self
1604         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1605             List<String> preloadModuleName = new ArrayList<>();
1606             List<PreloadItem> preloadItems = hapVerifyInfo.getPreloadItems();
1607             for (PreloadItem preloadItem : preloadItems) {
1608                 String moduleName = preloadItem.getModuleName();
1609                 if (preloadModuleName.contains(moduleName)) {
1610                     LOG.error(PackingToolErrMsg.ATOMICSERVICE_PRELOADS_INVALID.toString(
1611                             "Preloads a duplicate module, module(" + hapVerifyInfo.getModuleName() +
1612                             ") cannot preloads module (" + moduleName + ")."));
1613                     return false;
1614                 }
1615                 preloadModuleName.add(moduleName);
1616                 if (!moduleNames.contains(moduleName)) {
1617                     LOG.error(PackingToolErrMsg.ATOMICSERVICE_PRELOADS_INVALID.toString(
1618                             "Preloads a not exist module, module(" + hapVerifyInfo.getModuleName() +
1619                             ") cannot preloads module(" + moduleName + ")."));
1620                     return false;
1621                 }
1622                 if (moduleName.equals(hapVerifyInfo.getModuleName())) {
1623                     LOG.error(PackingToolErrMsg.ATOMICSERVICE_PRELOADS_INVALID.toString("Cannot preload self, module " +
1624                             hapVerifyInfo.getModuleName() + " cannot preloads self."));
1625                     return false;
1626                 }
1627             }
1628         }
1629         // check feature preload is valid
1630         Map<String, String> moduleNameWithType = new HashMap<>();
1631         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1632             moduleNameWithType.put(hapVerifyInfo.getModuleName(), hapVerifyInfo.getModuleType());
1633         }
1634         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1635             List<PreloadItem> preloadItems = hapVerifyInfo.getPreloadItems();
1636             for (PreloadItem preloadItem : preloadItems) {
1637                 String moduleName = preloadItem.getModuleName();
1638                 if (moduleNameWithType.get(moduleName).equals(ENTRY)
1639                         || moduleNameWithType.get(moduleName).equals(HAR)) {
1640                     LOG.error(PackingToolErrMsg.ATOMICSERVICE_PRELOADS_INVALID.toString(
1641                         "feature or shared cannot preload entry or har, module(" + hapVerifyInfo.getModuleName() +
1642                         ") cannot preloads module(" + moduleName + ")."));
1643                     return false;
1644                 }
1645             }
1646         }
1647 
1648         return true;
1649     }
1650 
1651     /**
1652      * check file size is valid from List<HapVerifyInfo>
1653      *
1654      * @param hapVerifyInfoList is hapVerifyInfo list
1655      * @return true file size is under limit
1656      */
checkFileSizeIsValid(List<HapVerifyInfo> hapVerifyInfoList)1657     public static boolean checkFileSizeIsValid(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1658         if (hapVerifyInfoList.isEmpty()) {
1659             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString("Hap verify infos is empty."));
1660             throw new BundleException("Check file size is valid failed, hap verify infos is empty.");
1661         }
1662         if (!checkFileSize(hapVerifyInfoList)) {
1663             return false;
1664         }
1665         return true;
1666     }
1667 
checkFileSize(List<HapVerifyInfo> hapVerifyInfoList)1668     private static boolean checkFileSize(List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1669         if (hapVerifyInfoList.isEmpty()) {
1670             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString("Hap verify infos is empty."));
1671             throw new BundleException("checkFileSizeWhenSplit failed, hapVerifyInfoList is empty.");
1672         }
1673         // check single file length
1674         int entryLimit = hapVerifyInfoList.get(0).getEntrySizeLimit();
1675         int notEntryLimit = hapVerifyInfoList.get(0).getNotEntrySizeLimit();
1676         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1677             if (hapVerifyInfo.getModuleType().equals(ENTRY) &&
1678                     entryLimit != 0 &&
1679                     (hapVerifyInfo.getFileLength() >= entryLimit * FILE_LENGTH_1KB)) {
1680                 String errMsg = "Module " + hapVerifyInfo.getModuleName() + "'s size is " +
1681                         getCeilFileSize(hapVerifyInfo.getFileLength(), entryLimit) +
1682                         "KB, which is overlarge than " + entryLimit + "KB.";
1683                 LOG.error(PackingToolErrMsg.CHECK_FILE_SIZE_INVALID.toString(errMsg));
1684                 return false;
1685             }
1686             if (!hapVerifyInfo.getModuleType().equals(ENTRY) &&
1687                     notEntryLimit != 0 &&
1688                     (hapVerifyInfo.getFileLength() >= notEntryLimit * FILE_LENGTH_1KB)) {
1689                     String errMsg = "Module " + hapVerifyInfo.getModuleName() + "'s size is " +
1690                             getCeilFileSize(hapVerifyInfo.getFileLength(), notEntryLimit) +
1691                             "KB, which is overlarge than " + notEntryLimit + "KB.";
1692                 LOG.error(PackingToolErrMsg.CHECK_FILE_SIZE_INVALID.toString(errMsg));
1693                 return false;
1694             }
1695         }
1696 
1697         Map<String, List<HapVerifyInfo>> deviceInfoMap = getDeviceHapVerifyInfoMap(hapVerifyInfoList);
1698         for (String device : deviceInfoMap.keySet()) {
1699             List<HapVerifyInfo>hapVerifyInfoList1 = deviceInfoMap.get(device);
1700             if (!checkAtomicServiceModuleAndDependenciesSize(hapVerifyInfoList1)) {
1701                 String errMsg = "Check the atomicService module size failed on device " + device + ".";
1702                 LOG.error(PackingToolErrMsg.CHECK_FILE_SIZE_INVALID.toString(errMsg));
1703                 return false;
1704             }
1705         }
1706         return true;
1707     }
1708 
getModuleDependency(HapVerifyInfo hapVerifyInfo, List<HapVerifyInfo> hapVerifyInfoList)1709     private static List<String> getModuleDependency(HapVerifyInfo hapVerifyInfo,
1710                                                     List<HapVerifyInfo> hapVerifyInfoList) throws BundleException {
1711         List<String> dependencyModules = new ArrayList<>();
1712         dependencyModules.addAll(hapVerifyInfo.getDependencies());
1713         List<String> dependencyItems = hapVerifyInfo.getDependencies();
1714         for (String dependency : dependencyItems) {
1715             HapVerifyInfo dependencyHapVerifyInfo = findAtomicServiceHapVerifyInfo(dependency, hapVerifyInfoList);
1716             if (dependencyHapVerifyInfo == null) {
1717                 continue;
1718             }
1719             List<String> childDependencies = getModuleDependency(dependencyHapVerifyInfo, hapVerifyInfoList);
1720             for (String childDependency : childDependencies) {
1721                 if (!dependencyModules.contains(childDependency)) {
1722                     dependencyModules.add(childDependency);
1723                 }
1724             }
1725         }
1726         return dependencyModules;
1727     }
1728 
findAtomicServiceHapVerifyInfo(String moduleName, List<HapVerifyInfo> hapVerifyInfoList)1729     private static HapVerifyInfo findAtomicServiceHapVerifyInfo(String moduleName,
1730                                                                 List<HapVerifyInfo> hapVerifyInfoList) {
1731         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfoList) {
1732             if (hapVerifyInfo.getModuleName().equals(moduleName)) {
1733                 return hapVerifyInfo;
1734             }
1735         }
1736         return null;
1737     }
1738 }
1739