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