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