1 /* 2 * Copyright (c) 2021-2023 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.io.BufferedInputStream; 19 import java.io.BufferedReader; 20 import java.io.BufferedWriter; 21 import java.io.File; 22 import java.io.FileInputStream; 23 import java.io.FileNotFoundException; 24 import java.io.FileOutputStream; 25 import java.io.FileWriter; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.io.OutputStream; 30 import java.nio.charset.StandardCharsets; 31 import java.nio.file.attribute.FileTime; 32 import java.security.MessageDigest; 33 import java.security.NoSuchAlgorithmException; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Enumeration; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Locale; 40 import java.util.Optional; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 43 import java.util.zip.CRC32; 44 import java.util.zip.CheckedOutputStream; 45 import java.util.zip.ZipInputStream; 46 import java.util.zip.ZipEntry; 47 import java.util.zip.ZipFile; 48 import java.util.zip.ZipOutputStream; 49 import com.alibaba.fastjson.JSON; 50 import com.alibaba.fastjson.JSONObject; 51 import com.alibaba.fastjson.serializer.SerializerFeature; 52 53 /** 54 * bundle compressor class, compress file and directory. 55 * 56 */ 57 public class Compressor { 58 private static final String HAP_SUFFIX = ".hap"; 59 private static final String HSP_SUFFIX = ".hsp"; 60 private static final String PNG_SUFFIX = ".png"; 61 private static final String UPPERCASE_PNG_SUFFIX = ".PNG"; 62 private static final String CONFIG_JSON = "config.json"; 63 private static final String MODULE_JSON = "module.json"; 64 private static final String PATCH_JSON = "patch.json"; 65 private static final String NAME = "name"; 66 private static final String NULL_DIR_NAME = ""; 67 private static final String RES_DIR_NAME = "res/"; 68 private static final String RESOURCES_DIR_NAME = "resources/"; 69 private static final String LIBS_DIR_NAME = "libs/"; 70 private static final String AN_DIR_NAME = "an/"; 71 private static final String AP_PATH_NAME = "ap/"; 72 private static final String ASSETS_DIR_NAME = "assets/"; 73 private static final String SO_DIR_NAME = "maple/"; 74 private static final String SO_ARM64_DIR_NAME = "maple/arm64/"; 75 private static final String LINUX_FILE_SEPARATOR = "/"; 76 private static final String DISTRO = "distro"; 77 private static final String FORMS = "forms"; 78 private static final String MODULE_NAME = "module-name"; 79 private static final String MODULE_NAME_NEW = "moduleName"; 80 private static final String JSON_END = "}"; 81 private static final String SEMICOLON = "\""; 82 private static final String COMPRESS_NATIVE_LIBS = "compressNativeLibs"; 83 private static final String SHARED_LIBS_DIR_NAME = "shared_libs/"; 84 private static final String DEVICE_TYPE = "deviceType"; 85 private static final String DEVICE_TYPE_FITNESSWATCH = "fitnessWatch"; 86 private static final String DEVICE_TYPE_FITNESSWATCH_NEW = "liteWearable"; 87 private static final String ENTRYCARD_NAME = "EntryCard/"; 88 private static final String PACKINFO_NAME = "pack.info"; 89 private static final String ENTRYCARD_BASE_NAME = "base"; 90 private static final String ENTRYCARD_SNAPSHOT_NAME = "snapshot"; 91 private static final String PIC_1X2 = "1x2"; 92 private static final String PIC_2X2 = "2x2"; 93 private static final String PIC_2X4 = "2x4"; 94 private static final String PIC_4X4 = "4x4"; 95 private static final String REGEX_LANGUAGE = "^[a-z]{2}$"; 96 private static final String REGEX_SCRIPT = "^[A-Z][a-z]{3}$"; 97 private static final String REGEX_COUNTRY = "^[A-Z]{2,3}|[0-9]{3}$"; 98 private static final String REGEX_ORIENTATION = "^vertical|horizontal$"; 99 private static final String REGEX_DEVICE_TYPE = "^phone|tablet|car|tv|wearable|liteWearable|2in1$"; 100 private static final String REGEX_SCREEN_DENSITY = "^sdpi|mdpi|ldpi|xldpi|xxldpi$"; 101 private static final String REGEX_COLOR_MODE = "^light|dark$"; 102 private static final String REGEX_SHAPE = "^circle$"; 103 private static final String JS_PATH = "js/"; 104 private static final String ETS_PATH = "ets/"; 105 private static final String TEMP_HAP_DIR = "tempHapDir"; 106 private static final String TEMP_HSP_DIR = "tempHspDir"; 107 private static final String TEMP_SELECTED_HAP_DIR = "tempSelectedHapDir"; 108 private static final String EMPTY_STRING = ""; 109 private static final String RELEASE = "Release"; 110 private static final String TYPE_SHARED = "shared"; 111 private static final String APP = "app"; 112 private static final String MODULE = "module"; 113 private static final String GENERATE_BUILD_HASH = "generateBuildHash"; 114 private static final String BUILD_HASH = "buildHash"; 115 private static final String TEMP_DIR = "temp"; 116 private static final String SHA_256 = "SHA-256"; 117 private static final String JSON_SUFFIX = ".json"; 118 private static final String ATOMIC_SERVICE = "atomicService"; 119 private static final String RAW_FILE_PATH = "resources/rawfile"; 120 121 // set timestamp to get fixed MD5 122 private static final long FILE_TIME = 1546272000000L; 123 private static final int ENTRY_FILE_LIMIT_DEFAULT = 2; 124 private static final int NOT_ENTRY_FILE_LIMIT_DEFAULT = 2; 125 private static final int TOTAL_FILE_LIMIT_DEFAULT = 10; 126 private static final int FILE_LIMIT = 10; 127 private static final int SHA256_BASE = 0xff; 128 private static final int SHA256_OFFSET = 0x100; 129 private static final int RADIX = 16; 130 private static final int BEGIN_INDEX = 1; 131 private static final int BUFFER_BYTE_SIZE = 1024; 132 private static final int BUFFER_WRITE_SIZE = 1444; 133 134 // set buffer size of each read 135 private static final int BUFFER_SIZE = 10 * 1024; 136 private static final Log LOG = new Log(Compressor.class.toString()); 137 138 private static int entryModuleSizeLimit = 2; 139 private static int notEntryModuleSizeLimit = 2; 140 private static int sumModuleSizeLimit = 10; 141 private static boolean isOverlay = false; 142 143 private ZipOutputStream zipOut = null; 144 private boolean mIsContain2x2EntryCard = true; 145 private List<String> list = new ArrayList<String>(); 146 private List<String> formNamesList = new ArrayList<String>(); 147 private List<String> fileNameList = new ArrayList<String>(); 148 private List<String> supportDimensionsList = Arrays.asList(PIC_1X2, PIC_2X2, PIC_2X4, PIC_4X4); 149 getEntryModuleSizeLimit()150 public static int getEntryModuleSizeLimit() { 151 return entryModuleSizeLimit; 152 } 153 setEntryModuleSizeLimit(int entry)154 public static void setEntryModuleSizeLimit(int entry) { 155 entryModuleSizeLimit = entry; 156 } 157 getNotEntryModuleSizeLimit()158 public static int getNotEntryModuleSizeLimit() { 159 return notEntryModuleSizeLimit; 160 } 161 setNotEntryModuleSizeLimit(int notEntry)162 public static void setNotEntryModuleSizeLimit(int notEntry) { 163 notEntryModuleSizeLimit = notEntry; 164 } 165 getSumModuleSizeLimit()166 public static int getSumModuleSizeLimit() { 167 return sumModuleSizeLimit; 168 } 169 setSumModuleSizeLimit(int sumModule)170 public static void setSumModuleSizeLimit(int sumModule) { 171 sumModuleSizeLimit = sumModule; 172 } 173 174 /** 175 * parse file size limit from utility. 176 * 177 * @param utility Indicates the utility. 178 */ parseFileSizeLimit(Utility utility)179 public void parseFileSizeLimit(Utility utility) throws BundleException { 180 int sumLimit = TOTAL_FILE_LIMIT_DEFAULT; 181 String totalLimit = utility.getTotalLimit(); 182 if (!totalLimit.isEmpty()) { 183 try { 184 sumLimit = Integer.parseInt(totalLimit); 185 } catch (NumberFormatException e) { 186 LOG.error("parseFileSizeLimit failed, input total-limit invalid."); 187 throw new BundleException("parseFileSizeLimit failed, input total-limit invalid."); 188 } 189 if (sumLimit <= 0 || sumLimit > FILE_LIMIT) { 190 LOG.error("parseFileSizeLimit failed, input total-limit invalid."); 191 throw new BundleException("parseFileSizeLimit failed, input total-limit invalid."); 192 } 193 } 194 String normalLimit = utility.getNormalModuleLimit(); 195 int notEntry = NOT_ENTRY_FILE_LIMIT_DEFAULT; 196 if (!normalLimit.isEmpty()) { 197 try { 198 notEntry = Integer.parseInt(normalLimit); 199 } catch (NumberFormatException e) { 200 LOG.error("parseFileSizeLimit failed, input normal-module-limit invalid."); 201 throw new BundleException("parseFileSizeLimit failed, input normal-module-limit invalid."); 202 } 203 if (notEntry <= 0 || notEntry > sumLimit || notEntry > FILE_LIMIT) { 204 LOG.error("parseFileSizeLimit failed, input normal-module-limit invalid."); 205 throw new BundleException("parseFileSizeLimit failed, input normal-module-limit invalid."); 206 } 207 } 208 String mainLimit = utility.getMainModuleLimit(); 209 int entryLimit = ENTRY_FILE_LIMIT_DEFAULT; 210 if (!mainLimit.isEmpty()) { 211 try { 212 entryLimit = Integer.parseInt(mainLimit); 213 } catch (NumberFormatException e) { 214 LOG.error("parseFileSizeLimit failed, input main-module-limit invalid."); 215 throw new BundleException("parseFileSizeLimit failed, input main-module-limit invalid."); 216 } 217 if (entryLimit <= 0 || entryLimit > sumLimit || entryLimit > FILE_LIMIT) { 218 LOG.error("parseFileSizeLimit failed, input main-module-limit invalid."); 219 throw new BundleException("parseFileSizeLimit failed, input main-module-limit invalid."); 220 } 221 } 222 setEntryModuleSizeLimit(entryLimit); 223 setNotEntryModuleSizeLimit(notEntry); 224 setSumModuleSizeLimit(sumLimit); 225 } 226 227 /** 228 * check path if is a module.json file 229 * 230 * @param path path input 231 * @return true if path is a module file 232 */ isModuleJSON(String path)233 private static boolean isModuleJSON(String path) 234 { 235 File file = new File(path); 236 if ((file.isFile()) && MODULE_JSON.equals(file.getName())) { 237 return true; 238 } 239 return false; 240 } 241 242 /** 243 * start compress. 244 * file orders as follows: 245 * for hap: 1.config.json 2.lib 3.res 4.assets 5.*.so 6.*.dex 7.*.apk 8.resources.index 246 * for app: 1.certificate 2.signature 3.pack.info 4.hap (1 and 2 may not be used) 247 * 248 * @param utility common data 249 * @return compressProcess if compress succeed 250 */ compressProcess(Utility utility)251 public boolean compressProcess(Utility utility) { 252 boolean compressResult = true; 253 File destFile = new File(utility.getOutPath()); 254 255 // if out file directory not exist, mkdirs. 256 File outParentFile = destFile.getParentFile(); 257 if ((outParentFile != null) && (!outParentFile.exists())) { 258 if (!outParentFile.mkdirs()) { 259 LOG.error("Compressor::compressProcess create out file parent directory failed."); 260 return false; 261 } 262 } 263 264 FileOutputStream fileOut = null; 265 CheckedOutputStream checkedOut = null; 266 try { 267 fileOut = new FileOutputStream(destFile); 268 checkedOut = new CheckedOutputStream(fileOut, new CRC32()); 269 zipOut = new ZipOutputStream(checkedOut); 270 compressExcute(utility); 271 } catch (FileNotFoundException exception) { 272 compressResult = false; 273 LOG.error("Compressor::compressProcess file not found exception" + exception.getMessage()); 274 } catch (BundleException ignored) { 275 compressResult = false; 276 LOG.error("Compressor::compressProcess Bundle exception."); 277 } finally { 278 closeZipOutputStream(); 279 Utility.closeStream(zipOut); 280 Utility.closeStream(checkedOut); 281 Utility.closeStream(fileOut); 282 } 283 // if compress failed, delete out file. 284 if (!compressResult) { 285 LOG.error("Compressor::compressProcess compress failed."); 286 if (!destFile.delete()) { 287 LOG.error("Compressor::compressProcess delete dest file failed."); 288 } 289 } 290 return compressResult; 291 } 292 compressExcute(Utility utility)293 private void compressExcute(Utility utility) throws BundleException { 294 switch (utility.getMode()) { 295 case Utility.MODE_HAP: 296 compressHap(utility); 297 break; 298 case Utility.MODE_HAR: 299 compressHarMode(utility); 300 break; 301 case Utility.MODE_APP: 302 compressAppMode(utility); 303 break; 304 case Utility.MODE_MULTI_APP: 305 compressAppModeForMultiProject(utility); 306 break; 307 case Utility.MODE_HQF: 308 compressHQFMode(utility); 309 break; 310 case Utility.MODE_APPQF: 311 compressAPPQFMode(utility); 312 break; 313 case Utility.MODE_HSP: 314 compressHsp(utility); 315 break; 316 default: 317 compressPackResMode(utility); 318 } 319 } 320 compressHsp(Utility utility)321 private void compressHsp(Utility utility) throws BundleException { 322 setGenerateBuildHash(utility); 323 if (isModuleJSON(utility.getJsonPath())) { 324 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 325 String jsonString = optional.get(); 326 if (!checkStageAtomicService(jsonString)) { 327 LOG.error("checkStageAtomicService failed."); 328 throw new BundleException("checkStageAtomicService failed."); 329 } 330 // check whether is an overlay hsp or not 331 if (!checkStageOverlayCfg(jsonString)) { 332 LOG.error("checkStageOverlayCfg failed."); 333 throw new BundleException("checkStageOverlayCfg failed."); 334 } 335 String moduleType = ModuleJsonUtil.parseModuleType(jsonString); 336 if (!TYPE_SHARED.equals(moduleType)) { 337 LOG.error("module type must be shared."); 338 throw new BundleException("compressHsp failed."); 339 } 340 } 341 compressHSPMode(utility); 342 buildHash(utility); 343 } 344 compressHap(Utility utility)345 private void compressHap(Utility utility) throws BundleException { 346 if (utility.getJsonPath().isEmpty() && !utility.getBinPath().isEmpty()) { 347 // only for slim device 348 compressHapMode(utility); 349 return; 350 } 351 setGenerateBuildHash(utility); 352 if (isModuleJSON(utility.getJsonPath())) { 353 if (!checkStageHap(utility)) { 354 LOG.error("checkStageHap failed."); 355 throw new BundleException("checkStageHap failed."); 356 } 357 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 358 String jsonString = optional.get(); 359 String moduleType = ModuleJsonUtil.parseModuleType(jsonString); 360 if (TYPE_SHARED.equals(moduleType)) { 361 LOG.warning("Compress mode is hap, but module type is shared."); 362 } 363 String bundleType = ModuleJsonUtil.parseStageBundleType(jsonString); 364 if (TYPE_SHARED.equals(bundleType)) { 365 LOG.warning("Compress mode is hap, but app type is shared."); 366 } 367 compressHapModeForModule(utility); 368 buildHash(utility); 369 } else { 370 if (!checkFAHap(utility)) { 371 LOG.error("checkFAHap failed."); 372 throw new BundleException("checkStageHap failed."); 373 } 374 compressHapMode(utility); 375 buildHash(utility); 376 } 377 } 378 hasGenerateBuildHash(Utility utility)379 private static boolean hasGenerateBuildHash(Utility utility) throws BundleException { 380 File file = new File(utility.getJsonPath()); 381 if (!file.exists()) { 382 LOG.error("Compressor::hasGenerateBuildHash failed for json file not exist"); 383 throw new BundleException("Compressor::hasGenerateBuildHash failed for json file not exist"); 384 } 385 InputStream json = null; 386 boolean res = false; 387 try { 388 json = new FileInputStream(file); 389 JSONObject jsonObject = JSON.parseObject(json, JSONObject.class); 390 if (!jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) { 391 LOG.error("json file is invalid."); 392 throw new BundleException("json file is invalid."); 393 } 394 JSONObject appJson = jsonObject.getJSONObject(APP); 395 JSONObject moduleJson = jsonObject.getJSONObject(MODULE); 396 if (appJson.containsKey(GENERATE_BUILD_HASH) || moduleJson.containsKey(GENERATE_BUILD_HASH)) { 397 res = true; 398 } 399 } catch (BundleException | IOException exception) { 400 LOG.error("Compressor::hasGenerateBuildHash failed."); 401 throw new BundleException("Compressor::hasGenerateBuildHash failed."); 402 } finally { 403 FileUtils.closeStream(json); 404 } 405 return res; 406 } 407 setGenerateBuildHash(Utility utility)408 private static void setGenerateBuildHash(Utility utility) throws BundleException { 409 if (utility.isBuildHashFinish() || !hasGenerateBuildHash(utility)) { 410 return; 411 } 412 copyFileToTempDir(utility); 413 File file = new File(utility.getJsonPath()); 414 if (!file.exists()) { 415 LOG.error("Compressor::setGenerateBuildHash failed for json file not exist"); 416 throw new BundleException("Compressor::setGenerateBuildHash failed for json file not exist"); 417 } 418 InputStream json = null; 419 BufferedWriter bw = null; 420 try { 421 json = new FileInputStream(file); 422 JSONObject jsonObject = JSON.parseObject(json, JSONObject.class); 423 if (!jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) { 424 LOG.error("json file is invalid."); 425 throw new BundleException("json file is invalid."); 426 } 427 JSONObject appJson = jsonObject.getJSONObject(APP); 428 JSONObject moduleJson = jsonObject.getJSONObject(MODULE); 429 if (appJson.containsKey(GENERATE_BUILD_HASH) && appJson.getBoolean(GENERATE_BUILD_HASH)) { 430 utility.setGenerateBuildHash(true); 431 } else { 432 if (moduleJson.containsKey(GENERATE_BUILD_HASH) && moduleJson.getBoolean(GENERATE_BUILD_HASH)) { 433 utility.setGenerateBuildHash(true); 434 } 435 } 436 appJson.remove(GENERATE_BUILD_HASH); 437 moduleJson.remove(GENERATE_BUILD_HASH); 438 String pretty = JSON.toJSONString(jsonObject, new SerializerFeature[]{ 439 SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, 440 SerializerFeature.WriteDateUseDateFormat}); 441 bw = new BufferedWriter(new FileWriter(utility.getJsonPath())); 442 bw.write(pretty); 443 } catch (BundleException | IOException exception) { 444 LOG.error("Compressor::setGenerateBuildHash failed."); 445 throw new BundleException("Compressor::setGenerateBuildHash failed."); 446 } finally { 447 FileUtils.closeStream(json); 448 if (bw != null) { 449 try { 450 bw.flush(); 451 bw.close(); 452 } catch (IOException e) { 453 LOG.error("Compressor::setGenerateBuildHash failed for IOException " + e.getMessage()); 454 throw new BundleException("Compressor::setGenerateBuildHash failed."); 455 } 456 } 457 } 458 } 459 copyFileToTempDir(Utility utility)460 private static void copyFileToTempDir(Utility utility) throws BundleException { 461 String jsonPath = utility.getJsonPath(); 462 File oldfile = new File(jsonPath); 463 if (!oldfile.exists()) { 464 LOG.error("Compressor::copyFileToTempDir failed for json file not found."); 465 throw new BundleException("Compressor::copyFileToTempDir failed for json file not found."); 466 } 467 String oldfileParent = oldfile.getParent(); 468 mkdir(new File(oldfileParent + File.separator + TEMP_DIR)); 469 String fileName; 470 if (isModuleJSON(utility.getJsonPath())) { 471 fileName = MODULE_JSON; 472 } else { 473 fileName = CONFIG_JSON; 474 } 475 String tempPath = oldfileParent + File.separator + TEMP_DIR + File.separator + fileName; 476 477 try (InputStream inStream = new FileInputStream(jsonPath); 478 FileOutputStream fs = new FileOutputStream(tempPath)) { 479 byte[] buffer = new byte[BUFFER_WRITE_SIZE]; 480 int byteread; 481 while ((byteread = inStream.read(buffer)) != -1) { 482 fs.write(buffer, 0, byteread); 483 } 484 utility.setJsonPath(tempPath); 485 } catch (IOException e) { 486 LOG.error("Compressor::copyFileToTempDir failed, IOException: " + e.getMessage()); 487 throw new BundleException("Compressor::copyFileToTempDir failed."); 488 } 489 } 490 mkdir(File file)491 private static void mkdir(File file) { 492 if (null != file && !file.exists()) { 493 mkdir(file.getParentFile()); 494 file.mkdir(); 495 } 496 } 497 buildHash(Utility utility)498 private static void buildHash(Utility utility) throws BundleException { 499 if (utility.isBuildHashFinish() || (!utility.getGenerateBuildHash())) { 500 return; 501 } 502 String filePath = utility.getOutPath(); 503 String hash = getSHA256(filePath); 504 try { 505 putBuildHash(utility, hash); 506 } catch (IOException e) { 507 LOG.error("Compressor::putBuildHash failed, " + e.getMessage()); 508 throw new BundleException("Compressor::putBuildHash failed."); 509 } 510 } 511 checkSum(String filename)512 private static byte[] checkSum(String filename) throws BundleException { 513 try (InputStream fis = new FileInputStream(filename)) { 514 byte[] buffer = new byte[BUFFER_BYTE_SIZE]; 515 MessageDigest complete = MessageDigest.getInstance(SHA_256); 516 int numRead; 517 do { 518 numRead = fis.read(buffer); 519 if (numRead > 0) { 520 complete.update(buffer, 0, numRead); 521 } 522 } while (numRead != -1); 523 return complete.digest(); 524 } catch (IOException | NoSuchAlgorithmException e) { 525 LOG.error("Compressor::checkSum failed, IOException or NoSuchAlgorithmException: " + e.getMessage()); 526 throw new BundleException("Compressor::checkSum failed."); 527 } 528 } 529 530 /** 531 * get SHA256 of hap or hsp 532 * 533 * @param filePath the path of hap or hsp. 534 */ getSHA256(String filePath)535 public static String getSHA256(String filePath) throws BundleException { 536 byte[] byteSum = checkSum(filePath); 537 StringBuilder temp = new StringBuilder(); 538 for (int i = 0; i < byteSum.length; i++) { 539 temp.append( 540 Integer.toString((byteSum[i] & SHA256_BASE) + SHA256_OFFSET, RADIX).substring(BEGIN_INDEX)); 541 } 542 return temp.toString(); 543 } 544 putBuildHash(Utility utility, String hash)545 private static void putBuildHash(Utility utility, String hash) throws BundleException, IOException { 546 if (utility.isBuildHashFinish()) { 547 return; 548 } 549 String jsonPath = utility.getJsonPath(); 550 File file = new File(jsonPath); 551 if (!file.exists()) { 552 LOG.error("Compressor::putBuildHash failed for json file not exist"); 553 throw new BundleException("Compressor::putBuildHash failed for json file not exist"); 554 } 555 InputStream json = null; 556 BufferedWriter bw = null; 557 try { 558 json = new FileInputStream(file); 559 JSONObject jsonObject = JSON.parseObject(json, JSONObject.class); 560 JSONObject moduleJson = jsonObject.getJSONObject(MODULE); 561 moduleJson.put(BUILD_HASH, hash); 562 String pretty = JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat, 563 SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat); 564 bw = new BufferedWriter(new FileWriter(jsonPath)); 565 bw.write(pretty); 566 } catch (IOException e) { 567 LOG.error("Compressor::putBuildHash failed, IOException: " + e.getMessage()); 568 throw new BundleException("Compressor::putBuildHash failed."); 569 } finally { 570 FileUtils.closeStream(json); 571 if (bw != null) { 572 bw.flush(); 573 bw.close(); 574 } 575 } 576 utility.setBuildHashFinish(true); 577 } 578 checkStageHap(Utility utility)579 private static boolean checkStageHap(Utility utility) throws BundleException { 580 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 581 String jsonString = optional.get(); 582 if (!checkStageAsanEnabledValid(jsonString)) { 583 LOG.error("checkStageAsanEnabledValid failed."); 584 return false; 585 } 586 // check atomicService in module.json 587 if (!checkStageAtomicService(jsonString)) { 588 LOG.error("checkStageAtomicService failed."); 589 return false; 590 } 591 return true; 592 } 593 checkStageAsanEnabledValid(String jsonString)594 private static boolean checkStageAsanEnabledValid(String jsonString) throws BundleException { 595 boolean asanEnabled = ModuleJsonUtil.getStageAsanEnabled(jsonString); 596 boolean debug = ModuleJsonUtil.getDebug(jsonString); 597 if (asanEnabled && !debug) { 598 LOG.error("asanEnabled is not supported for Release."); 599 return false; 600 } 601 return true; 602 } 603 checkStageAtomicService(String jsonString)604 private static boolean checkStageAtomicService(String jsonString) throws BundleException { 605 // check consistency of atomicService 606 if (!ModuleJsonUtil.isModuleAtomicServiceValid(jsonString)) { 607 LOG.error("check module atomicService failed."); 608 return false; 609 } 610 // check entry module must have ability 611 if (!ModuleJsonUtil.checkEntryInAtomicService(jsonString)) { 612 LOG.error("checkEntryInAtomicService failed."); 613 return false; 614 } 615 // check installationFree 616 if (!ModuleJsonUtil.checkAtomicServiceInstallationFree(jsonString)) { 617 LOG.error("check atomic service installationFree failed."); 618 return false; 619 } 620 621 return true; 622 } 623 checkStageOverlayCfg(String jsonString)624 private static boolean checkStageOverlayCfg(String jsonString) throws BundleException { 625 // check module 626 String targetModuleName = ModuleJsonUtil.getStageTargetModuleName(jsonString); 627 if (!targetModuleName.isEmpty()) { 628 // check targetModuleName and requestPermission 629 if (ModuleJsonUtil.isExistedStageRequestPermissions(jsonString)) { 630 LOG.error("targetModuleName cannot be existed with requestPermissions."); 631 return false; 632 } 633 // check targetModuleName and name 634 if (targetModuleName.equals(ModuleJsonUtil.parseStageModuleName(jsonString))) { 635 LOG.error("targetModuleName cannot be same with name in the overlay module."); 636 return false; 637 } 638 } else { 639 if (ModuleJsonUtil.isExistedStageModuleTargetPriority(jsonString)) { 640 LOG.error("targetPriority cannot be existed without the targetModuleName in module.json."); 641 return false; 642 } 643 } 644 // check app 645 String targetBundleName = ModuleJsonUtil.getStageTargetBundleName(jsonString); 646 if (!targetBundleName.isEmpty()) { 647 if (targetModuleName.isEmpty()) { 648 LOG.error("targetModuleName is necessary in the overlay bundle."); 649 return false; 650 } 651 if (targetBundleName.equals(ModuleJsonUtil.parseBundleName(jsonString))) { 652 LOG.error("targetBundleName cannot be same with the bundleName."); 653 return false; 654 } 655 } else { 656 if (ModuleJsonUtil.isExistedStageAppTargetPriority(jsonString)) { 657 LOG.error("targetPriority cannot be existed without the targetBundleName in app.json."); 658 return false; 659 } 660 } 661 return true; 662 } 663 checkFAHap(Utility utility)664 private static boolean checkFAHap(Utility utility) throws BundleException { 665 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 666 String jsonString = optional.get(); 667 return checkFAAsanEnabledValid(jsonString); 668 } 669 checkFAAsanEnabledValid(String jsonString)670 private static boolean checkFAAsanEnabledValid(String jsonString) throws BundleException { 671 boolean asanEnabled = ModuleJsonUtil.getFAAsanEnabled(jsonString); 672 boolean debug = ModuleJsonUtil.getFADebug(jsonString); 673 if (asanEnabled && !debug) { 674 LOG.error("asanEnabled is not supported for Release."); 675 return false; 676 } 677 return true; 678 } 679 680 /** 681 * compress in hap mode. 682 * 683 * @param utility common data 684 * @throws BundleException FileNotFoundException|IOException. 685 */ compressHapMode(Utility utility)686 private void compressHapMode(Utility utility) throws BundleException { 687 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 688 689 pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false); 690 691 if (!utility.getIndexPath().isEmpty() && !utility.getModuleName().isEmpty()) { 692 String assetsPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR; 693 pathToFile(utility, utility.getIndexPath(), assetsPath, false); 694 } 695 696 if (!utility.getLibPath().isEmpty()) { 697 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs()); 698 } 699 700 if (!utility.getFilePath().isEmpty()) { 701 pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false); 702 } 703 704 if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) { 705 String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 706 + RESOURCES_DIR_NAME; 707 String deviceTypes = utility.getDeviceType().replace("\"", "").trim(); 708 if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) || 709 DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) { 710 resPath = RES_DIR_NAME; 711 } 712 pathToFile(utility, utility.getResPath(), resPath, false); 713 } 714 715 if (!utility.getResourcesPath().isEmpty() && !utility.getModuleName().isEmpty()) { 716 String resourcesPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 717 + RESOURCES_DIR_NAME; 718 pathToFile(utility, utility.getResourcesPath(), resourcesPath, false); 719 } 720 721 if (!utility.getRpcidPath().isEmpty()) { 722 String rpcidPath = NULL_DIR_NAME; 723 pathToFile(utility, utility.getRpcidPath(), rpcidPath, false); 724 } 725 726 if (!utility.getPackInfoPath().isEmpty()) { 727 String packInfoPath = NULL_DIR_NAME; 728 pathToFile(utility, utility.getPackInfoPath(), packInfoPath, false); 729 } 730 731 if (!utility.getAssetsPath().isEmpty()) { 732 pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false); 733 } 734 735 if (!utility.getBinPath().isEmpty()) { 736 pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false); 737 } 738 // pack --dir-list 739 if (!utility.getFormatedDirList().isEmpty()) { 740 for (int i = 0; i < utility.getFormatedDirList().size(); ++i) { 741 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator; 742 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false); 743 } 744 } 745 746 compressHapModeMultiple(utility); 747 } 748 749 /** 750 * compress in hap mode for module.json. 751 * 752 * @param utility common data 753 * @throws BundleException FileNotFoundException|IOException. 754 */ compressHapModeForModule(Utility utility)755 private void compressHapModeForModule(Utility utility) throws BundleException { 756 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 757 758 pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false); 759 760 if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 761 String assetsPath = NULL_DIR_NAME; 762 pathToFile(utility, utility.getIndexPath(), assetsPath, false); 763 } 764 765 if (!utility.getLibPath().isEmpty()) { 766 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs()); 767 } 768 769 if (!utility.getANPath().isEmpty()) { 770 pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false); 771 } 772 773 if (!utility.getAPPath().isEmpty()) { 774 pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false); 775 } 776 777 if (!utility.getFilePath().isEmpty()) { 778 pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false); 779 } 780 781 if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) { 782 String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 783 + RESOURCES_DIR_NAME; 784 String deviceTypes = utility.getDeviceType().replace("\"", "").trim(); 785 if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) || 786 DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) { 787 resPath = RES_DIR_NAME; 788 } 789 pathToFile(utility, utility.getResPath(), resPath, false); 790 } 791 792 if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 793 String resourcesPath = RESOURCES_DIR_NAME; 794 pathToFile(utility, utility.getResourcesPath(), resourcesPath, false); 795 } 796 if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 797 String jsPath = JS_PATH; 798 pathToFile(utility, utility.getJsPath(), jsPath, false); 799 } 800 801 if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 802 String etsPath = ETS_PATH; 803 pathToFile(utility, utility.getEtsPath(), etsPath, false); 804 } 805 806 if (!utility.getRpcidPath().isEmpty()) { 807 String rpcidPath = NULL_DIR_NAME; 808 pathToFile(utility, utility.getRpcidPath(), rpcidPath, false); 809 } 810 811 if (!utility.getAssetsPath().isEmpty()) { 812 pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false); 813 } 814 815 if (!utility.getBinPath().isEmpty()) { 816 pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false); 817 } 818 819 if (!utility.getPackInfoPath().isEmpty()) { 820 pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false); 821 } 822 823 // pack --dir-list 824 if (!utility.getFormatedDirList().isEmpty()) { 825 for (int i = 0; i < utility.getFormatedDirList().size(); ++i) { 826 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator; 827 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false); 828 } 829 } 830 831 compressHapModeMultiple(utility); 832 } 833 834 /** 835 * compress in hap mode multiple path. 836 * 837 * @param utility common data 838 * @throws BundleException FileNotFoundException|IOException. 839 */ compressHapModeMultiple(Utility utility)840 private void compressHapModeMultiple(Utility utility) throws BundleException { 841 for (String soPathItem : utility.getFormattedSoPathList()) { 842 pathToFile(utility, soPathItem, SO_ARM64_DIR_NAME, false); 843 } 844 845 if (utility.getFormattedSoPathList().size() == 0 && !utility.getSoDir().isEmpty()) { 846 pathToFile(utility, utility.getSoDir(), SO_DIR_NAME, false); 847 } 848 849 for (String soPathItem : utility.getFormattedAbilitySoPathList()) { 850 pathToFile(utility, soPathItem, NULL_DIR_NAME, false); 851 } 852 853 for (String dexPathItem : utility.getFormattedDexPathList()) { 854 pathToFile(utility, dexPathItem, NULL_DIR_NAME, false); 855 } 856 857 for (String abcPathItem : utility.getFormattedAbcPathList()) { 858 pathToFile(utility, abcPathItem, NULL_DIR_NAME, false); 859 } 860 861 for (String apkPathItem : utility.getFormattedApkPathList()) { 862 pathToFile(utility, apkPathItem, NULL_DIR_NAME, false); 863 } 864 865 for (String jarPathItem : utility.getFormattedJarPathList()) { 866 pathToFile(utility, jarPathItem, NULL_DIR_NAME, false); 867 } 868 869 for (String txtPathItem : utility.getFormattedTxtPathList()) { 870 pathToFile(utility, txtPathItem, NULL_DIR_NAME, false); 871 } 872 873 if (!utility.getSharedLibsPath().isEmpty()) { 874 pathToFile(utility, utility.getSharedLibsPath(), SHARED_LIBS_DIR_NAME, utility.isCompressNativeLibs()); 875 } 876 } 877 878 /** 879 * compress in har mode. 880 * 881 * @param utility common data 882 * @throws BundleException FileNotFoundException|IOException. 883 */ compressHarMode(Utility utility)884 private void compressHarMode(Utility utility) throws BundleException { 885 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 886 887 if (!utility.getLibPath().isEmpty()) { 888 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs()); 889 } 890 891 if (!utility.getResPath().isEmpty()) { 892 pathToFile(utility, utility.getResPath(), RESOURCES_DIR_NAME, false); 893 } 894 895 if (!utility.getResourcesPath().isEmpty()) { 896 pathToFile(utility, utility.getResourcesPath(), RESOURCES_DIR_NAME, false); 897 } 898 899 if (!utility.getAssetsPath().isEmpty()) { 900 pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false); 901 } 902 903 for (String jarPathItem : utility.getFormattedJarPathList()) { 904 pathToFile(utility, jarPathItem, NULL_DIR_NAME, false); 905 } 906 907 for (String txtPathItem : utility.getFormattedTxtPathList()) { 908 pathToFile(utility, txtPathItem, NULL_DIR_NAME, false); 909 } 910 } 911 912 /** 913 * compress in app mode. 914 * 915 * @param utility common data 916 * @throws BundleException FileNotFoundException|IOException. 917 */ compressAppMode(Utility utility)918 private void compressAppMode(Utility utility) throws BundleException { 919 List<String> fileList = new ArrayList<>(); 920 File appOutputFile = new File(utility.getOutPath().trim()); 921 String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR; 922 String hspTempDirPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HSP_DIR; 923 try { 924 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 925 926 if (!utility.getCertificatePath().isEmpty()) { 927 pathToFile(utility, utility.getCertificatePath(), NULL_DIR_NAME, false); 928 } 929 930 if (!utility.getSignaturePath().isEmpty()) { 931 pathToFile(utility, utility.getSignaturePath(), NULL_DIR_NAME, false); 932 } 933 934 File tempDir = new File(tempPath); 935 if (!tempDir.exists()) { 936 tempDir.mkdirs(); 937 } 938 939 for (String hapPathItem : utility.getFormattedHapPathList()) { 940 File hapFile = new File(hapPathItem.trim()); 941 String hapTempPath = tempDir + File.separator + hapFile.getName(); 942 fileList.add(hapTempPath); 943 try { 944 compressPackinfoIntoHap(hapPathItem, hapTempPath, utility.getPackInfoPath()); 945 } catch (IOException e) { 946 LOG.error("Compressor::compressAppMode compress pack.info into hap failed."); 947 throw new BundleException("Compressor::compressAppMode compress pack.info into hap failed."); 948 } 949 } 950 951 File hspTempDir = new File(hspTempDirPath); 952 if (!hspTempDir.exists()) { 953 hspTempDir.mkdirs(); 954 } 955 for (String hspPathItem : utility.getFormattedHspPathList()) { 956 File hspFile = new File(hspPathItem.trim()); 957 String hspTempPath = hspTempDir + File.separator + hspFile.getName(); 958 fileList.add(hspTempPath); 959 try { 960 compressPackinfoIntoHap(hspPathItem, hspTempPath, utility.getPackInfoPath()); 961 } catch (IOException e) { 962 LOG.error("Compressor::compressAppMode compress pack.info into hsp failed."); 963 throw new BundleException("Compressor::compressAppMode compress pack.info into hsp failed."); 964 } 965 } 966 parseFileSizeLimit(utility); 967 // check hap is valid 968 if (!checkHapIsValid(fileList, utility.getSharedApp())) { 969 throw new BundleException("Compressor::compressFile verify failed, check version, " + 970 "apiVersion,moduleName,packageName."); 971 } 972 for (String hapPath : fileList) { 973 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 974 } 975 976 if (!utility.getEntryCardPath().isEmpty()) { 977 String entryCardPath = ENTRYCARD_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 978 + ENTRYCARD_BASE_NAME + ENTRYCARD_SNAPSHOT_NAME; 979 for (String entryCardPathItem : utility.getformattedEntryCardPathList()) { 980 pathToFile(utility, entryCardPathItem, entryCardPath, true); 981 } 982 } 983 984 if (!utility.getPackResPath().isEmpty()) { 985 pathToFile(utility, utility.getPackResPath(), NULL_DIR_NAME, false); 986 } 987 File file = new File(utility.getPackInfoPath()); 988 compressFile(utility, file, NULL_DIR_NAME, false); 989 } catch (BundleException e) { 990 LOG.error("Compressor::compressAppMode compress failed."); 991 throw new BundleException("Compressor::compressAppMode compress failed."); 992 } finally { 993 // delete temp file 994 for (String path : fileList) { 995 deleteFile(path); 996 } 997 deleteFile(tempPath); 998 deleteFile(hspTempDirPath); 999 } 1000 } 1001 1002 /** 1003 * compress in app mode for multi project. 1004 * 1005 * @param utility common data 1006 * @throws BundleException FileNotFoundException|IOException. 1007 */ compressAppModeForMultiProject(Utility utility)1008 private void compressAppModeForMultiProject(Utility utility) throws BundleException { 1009 List<String> fileList = new ArrayList<>(); 1010 File appOutputFile = new File(utility.getOutPath().trim()); 1011 String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR; 1012 String tempSelectedHapPath = appOutputFile.getParentFile().getParent() +File.separator + TEMP_SELECTED_HAP_DIR; 1013 try { 1014 File tempSelectedHapDir = new File(tempSelectedHapPath); 1015 FileUtils.makeDir(tempSelectedHapDir); 1016 File tempHapDir = new File(tempPath); 1017 FileUtils.makeDir(tempHapDir); 1018 // pack app and dispose conflict 1019 // save hap name into list 1020 List<String> seletedHaps = new ArrayList<>(); 1021 String finalPackInfoStr = disposeApp(utility, seletedHaps, tempSelectedHapPath); 1022 // pack hap and dispose conflict 1023 finalPackInfoStr = disposeHap(utility, seletedHaps, tempSelectedHapPath, finalPackInfoStr); 1024 1025 // save final pack.info file 1026 String finalPackInfoPath = tempSelectedHapDir.getPath() + File.separator + PACKINFO_NAME; 1027 writePackInfo(finalPackInfoPath, finalPackInfoStr); 1028 // pack haps 1029 for (String selectedHapName : seletedHaps) { 1030 String hapPathItem = tempSelectedHapDir.getPath() + File.separator + selectedHapName; 1031 File hapFile = new File(hapPathItem.trim()); 1032 String hapTempPath = tempHapDir.getPath() + File.separator + hapFile.getName(); 1033 fileList.add(hapTempPath); 1034 compressPackinfoIntoHap(hapPathItem, hapTempPath, finalPackInfoPath); 1035 } 1036 // check hap is valid 1037 if (!checkHapIsValid(fileList, false)) { 1038 String errMsg = "Compressor::compressAppModeForMultiProject There are some " + 1039 "haps with different version code or duplicated moduleName or packageName."; 1040 throw new BundleException(errMsg); 1041 } 1042 for (String hapPath : fileList) { 1043 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 1044 } 1045 File file = new File(finalPackInfoPath); 1046 compressFile(utility, file, NULL_DIR_NAME, false); 1047 } catch (BundleException | IOException exception) { 1048 String errMsg = "Compressor::compressAppModeForMultiProject file failed."; 1049 LOG.error(errMsg); 1050 throw new BundleException(errMsg); 1051 } finally { 1052 deleteFile(tempPath); 1053 deleteFile(tempSelectedHapPath); 1054 } 1055 } 1056 1057 /** 1058 * pack hap in app to selectedHaps 1059 * 1060 * @param utility is common data 1061 * @param seletedHaps is seleted haps should be pack into app 1062 * @return the final pack.info string after dispose app 1063 * @throws BundleException FileNotFoundException|IOException. 1064 */ disposeApp(Utility utility, List<String> seletedHaps, String tempDir)1065 private static String disposeApp(Utility utility, List<String> seletedHaps, 1066 String tempDir) throws BundleException { 1067 // dispose app conflict 1068 if (utility.getFormattedAppList().isEmpty()) { 1069 return ""; 1070 } 1071 String finalAppPackInfo = ""; 1072 try { 1073 for (String appPath : utility.getFormattedAppList()) { 1074 // select hap in app 1075 finalAppPackInfo = selectHapInApp(appPath, seletedHaps, tempDir, finalAppPackInfo); 1076 } 1077 } catch (BundleException | IOException e) { 1078 String errMsg = "Compressor:disposeApp disposeApp failed."; 1079 LOG.error(errMsg); 1080 throw new BundleException(errMsg); 1081 } 1082 return finalAppPackInfo; 1083 } 1084 1085 /** 1086 * select hap from app file list 1087 * 1088 * @param appPath is common data 1089 * @param selectedHaps is list of packInfos 1090 * @throws BundleException FileNotFoundException|IOException. 1091 */ selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, String finalAppPackInfo)1092 private static String selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, 1093 String finalAppPackInfo) throws BundleException, IOException { 1094 List<String> selectedHapsInApp = new ArrayList<>(); 1095 // classify hap in app 1096 copyHapAndHspFromApp(appPath, selectedHapsInApp, selectedHaps, tempDir); 1097 // rebuild pack.info 1098 String packInfoStr = FileUtils.getJsonInZips(new File(appPath), PACKINFO_NAME); 1099 if (packInfoStr.isEmpty()) { 1100 String errorMsg = "Compressor:selectHapInApp failed, app has no pack.info."; 1101 LOG.error(errorMsg); 1102 throw new BundleException(errorMsg); 1103 } 1104 if (finalAppPackInfo.isEmpty()) { 1105 finalAppPackInfo = packInfoStr; 1106 return finalAppPackInfo; 1107 } 1108 // read selected module in temp hap 1109 HashMap<String, String> packagePair = new HashMap<>(); 1110 for (String hapName : selectedHapsInApp) { 1111 packagePair.put(hapName, readModlueNameFromHap(tempDir + File.separator + hapName)); 1112 } 1113 return ModuleJsonUtil.mergeTwoPackInfoByPackagePair(finalAppPackInfo, packInfoStr, packagePair); 1114 } 1115 1116 /** 1117 * copy hap from app file 1118 * 1119 * @param appPath is common data 1120 * @param selectedHapsInApp is list of haps and hsps selected in app file 1121 * @param selectedHaps is the list of haps and hsps selected in input 1122 * @throws BundleException FileNotFoundException|IOException. 1123 */ copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, String tempDir)1124 private static void copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, 1125 String tempDir) throws BundleException { 1126 ZipInputStream zipInput = null; 1127 ZipFile zipFile = null; 1128 OutputStream outputStream = null; 1129 InputStream inputStream = null; 1130 ZipEntry zipEntry = null; 1131 try { 1132 zipInput = new ZipInputStream(new FileInputStream(appPath)); 1133 zipFile = new ZipFile(appPath); 1134 while ((zipEntry = zipInput.getNextEntry()) != null) { 1135 File file = null; 1136 if (!zipEntry.getName().endsWith(HAP_SUFFIX) && !zipEntry.getName().endsWith(HSP_SUFFIX)) { 1137 continue; 1138 } 1139 // copy duplicated hap to duplicated dir and get moduleName of duplicated hap 1140 if (selectedHaps.contains(zipEntry.getName())) { 1141 LOG.error("Compressor::copyHapFromApp file duplicated, file is " + zipEntry.getName() + "."); 1142 throw new BundleException("Compressor::copyHapFromApp file duplicated, file is " 1143 + zipEntry.getName() + "."); 1144 } else { 1145 // copy selectedHap to tempDir 1146 file = new File(tempDir + File.separator + zipEntry.getName()); 1147 selectedHaps.add(file.getName()); 1148 selectedHapsInApp.add(file.getName()); 1149 } 1150 outputStream = new FileOutputStream(file); 1151 inputStream = zipFile.getInputStream(zipEntry); 1152 int len; 1153 while ((len = inputStream.read()) != -1) { 1154 outputStream.write(len); 1155 } 1156 outputStream.close(); 1157 inputStream.close(); 1158 } 1159 } catch (IOException e) { 1160 String errMsg = "Compressor:copyHapFromApp app path not found."; 1161 LOG.error(errMsg); 1162 throw new BundleException(errMsg); 1163 } finally { 1164 Utility.closeStream(zipInput); 1165 Utility.closeStream(zipFile); 1166 Utility.closeStream(outputStream); 1167 Utility.closeStream(inputStream); 1168 } 1169 } 1170 1171 /** 1172 * read moduleName in hap 1173 * 1174 * @param hapPath is path of hap file 1175 * @throws BundleException FileNotFoundException|IOException. 1176 */ readModlueNameFromHap(String hapPath)1177 private static String readModlueNameFromHap(String hapPath) throws BundleException { 1178 String moduleName = ""; 1179 File hapFile = new File(hapPath); 1180 if (isModuleHap(hapPath)) { 1181 String jsonString = FileUtils.getJsonInZips(hapFile, MODULE_JSON); 1182 moduleName = ModuleJsonUtil.parseStageModuleName(jsonString); 1183 } else { 1184 String jsonString = FileUtils.getJsonInZips(hapFile, CONFIG_JSON); 1185 moduleName = ModuleJsonUtil.parseFaModuleName(jsonString); 1186 } 1187 return moduleName; 1188 } 1189 1190 /** 1191 * dispose input of hap 1192 * 1193 * @param utility is common data 1194 * @param seletedHaps is the selected haps of all input 1195 * @param tempDir is the path of temp directory 1196 * @param finalPackInfoStr is the pack.info of the final app 1197 * @throws BundleException FileNotFoundException|IOException. 1198 */ disposeHap(Utility utility, List<String> seletedHaps, String tempDir, String finalPackInfoStr)1199 private static String disposeHap(Utility utility, List<String> seletedHaps, String tempDir, 1200 String finalPackInfoStr) throws BundleException, IOException { 1201 // dispose hap conflict 1202 if (utility.getFormattedHapList().isEmpty()) { 1203 return finalPackInfoStr; 1204 } 1205 for (String hapPath : utility.getFormattedHapList()) { 1206 if (seletedHaps.contains(new File(hapPath).getName())) { 1207 LOG.error("Compressor::disposeHap file duplicated, file is " + new File(hapPath).getName() + "."); 1208 throw new BundleException("Compressor::disposeHap file duplicated, file is " 1209 + new File(hapPath).getName() + "."); 1210 } 1211 File hapFile = new File(hapPath); 1212 seletedHaps.add(hapFile.getName()); 1213 // copy hap to tempDir 1214 FileUtils.copyFile(hapFile, new File((tempDir +File.separator + hapFile.getName()))); 1215 String packInfo = FileUtils.getJsonInZips(hapFile, PACKINFO_NAME); 1216 1217 if (packInfo.isEmpty()) { 1218 String errMsg = "Compressor::disposeHap failed, hap has no pack.info."; 1219 LOG.error(errMsg); 1220 throw new BundleException(errMsg); 1221 } 1222 if (finalPackInfoStr.isEmpty()) { 1223 finalPackInfoStr = packInfo; 1224 } else { 1225 finalPackInfoStr = ModuleJsonUtil.mergeTwoPackInfo(finalPackInfoStr, packInfo); 1226 } 1227 } 1228 return finalPackInfoStr; 1229 } 1230 1231 /** 1232 * write string to pack.info 1233 * 1234 * @param filePath pack.info file path 1235 * @param packInfoStr is the string of pack.info 1236 * @throws BundleException FileNotFoundException|IOException. 1237 */ writePackInfo(String filePath, String packInfoStr)1238 private void writePackInfo(String filePath, String packInfoStr) throws BundleException, IOException { 1239 FileWriter fwriter = null; 1240 try { 1241 fwriter = new FileWriter(filePath); 1242 fwriter.write(packInfoStr); 1243 } catch (IOException e) { 1244 String errMsg = "Compressor:writePackInfo failed."; 1245 LOG.error(errMsg); 1246 throw new BundleException(errMsg); 1247 } finally { 1248 if (fwriter != null) { 1249 fwriter.flush(); 1250 fwriter.close(); 1251 } 1252 } 1253 } 1254 copy(InputStream input, OutputStream output)1255 private void copy(InputStream input, OutputStream output) throws IOException { 1256 int bytesRead; 1257 byte[] data = new byte[BUFFER_SIZE]; 1258 while ((bytesRead = input.read(data, 0, BUFFER_SIZE)) != -1) { 1259 output.write(data, 0, bytesRead); 1260 } 1261 } 1262 compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo)1263 private void compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo) 1264 throws IOException, BundleException { 1265 ZipFile sourceHapFile = new ZipFile(hapPathItem); 1266 ZipOutputStream append = new ZipOutputStream(new FileOutputStream(outPathString)); 1267 try { 1268 Enumeration<? extends ZipEntry> entries = sourceHapFile.entries(); 1269 while (entries.hasMoreElements()) { 1270 ZipEntry zipEntry = entries.nextElement(); 1271 if (PACKINFO_NAME.equals(zipEntry.getName())) { 1272 continue; 1273 } 1274 ZipEntry newEntry = new ZipEntry(zipEntry); 1275 append.putNextEntry(newEntry); 1276 if (!zipEntry.isDirectory()) { 1277 copy(sourceHapFile.getInputStream(zipEntry), append); 1278 } 1279 append.closeEntry(); 1280 } 1281 File packInfoFile = new File(packInfo); 1282 ZipEntry zipEntry = getStoredZipEntry(packInfoFile, PACKINFO_NAME); 1283 append.putNextEntry(zipEntry); 1284 FileInputStream in = new FileInputStream(packInfoFile); 1285 try { 1286 byte[] buf = new byte[BUFFER_SIZE]; 1287 int len; 1288 while ((len = in.read(buf)) != -1) { 1289 append.write(buf, 0, len); 1290 } 1291 } finally { 1292 in.close(); 1293 } 1294 append.closeEntry(); 1295 } catch (IOException exception) { 1296 LOG.error("Compressor::compressPackinfoIntoHap io exception."); 1297 throw new BundleException("Compressor::compressPackinfoIntoHap io exception."); 1298 } finally { 1299 sourceHapFile.close(); 1300 append.close(); 1301 } 1302 } 1303 1304 /** 1305 * delete file 1306 * 1307 * @param path file path which will be deleted 1308 */ deleteFile(final String path)1309 private static void deleteFile(final String path) { 1310 File file = new File(path); 1311 if (file.exists()) { 1312 if (file.isDirectory()) { 1313 File[] files = file.listFiles(); 1314 for (int i = 0; i < files.length; i++) { 1315 deleteFile(files[i].toString()); 1316 } 1317 } 1318 file.delete(); 1319 } 1320 } 1321 1322 /** 1323 * compress in res mode. 1324 * 1325 * @param utility common data 1326 * @throws BundleException FileNotFoundException|IOException. 1327 */ compressPackResMode(Utility utility)1328 private void compressPackResMode(Utility utility) throws BundleException { 1329 if (!utility.getPackInfoPath().isEmpty()) { 1330 File file = new File(utility.getPackInfoPath()); 1331 infoSpecialProcess(utility, file); 1332 } 1333 if (!utility.getEntryCardPath().isEmpty()) { 1334 getFileList(utility.getEntryCardPath()); 1335 if (!mIsContain2x2EntryCard) { 1336 LOG.error("Compressor::compressPackResMode No 2x2 resource file exists."); 1337 throw new BundleException("No 2x2 resource file exists."); 1338 } 1339 for (String fileName : fileNameList) { 1340 if (fileName.endsWith(PNG_SUFFIX) || fileName.endsWith(UPPERCASE_PNG_SUFFIX)) { 1341 String fName = fileName.trim(); 1342 String[] temp = fName.replace("\\", "/").split("/"); 1343 if (temp.length < 4) { 1344 LOG.error("Compressor::compressPackResMode the hap file path is invalid, length: " 1345 + temp.length + "."); 1346 continue; 1347 } 1348 String moduleName = temp[temp.length - 4]; 1349 if (!isModelName(moduleName)) { 1350 String errMessage = "Compressor::compressProcess compress pack.res failed, " + 1351 "please check the related configurations in module " + moduleName + "."; 1352 LOG.error(errMessage); 1353 throw new BundleException(errMessage); 1354 } 1355 String fileLanguageCountryName = temp[temp.length - 3]; 1356 if (!isThirdLevelDirectoryNameValid(fileLanguageCountryName)) { 1357 LOG.error("Compressor::compressProcess compress failed third level directory name: " 1358 + fileLanguageCountryName + " is invalid, please check it with reference to this example: " 1359 + "zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi."); 1360 throw new BundleException("Compress failed third level directory name Error."); 1361 } 1362 String filePicturingName = temp[temp.length - 1]; 1363 if (!isPicturing(filePicturingName, utility)) { 1364 LOG.error("Compressor::compressProcess Compress pack.res failed, Invalid resource file" + 1365 " name: " + filePicturingName + ", correct format example is formName-2x2.png."); 1366 throw new BundleException("Compress pack.res failed, Invalid resource file name: " 1367 + filePicturingName + ", correct format example is formName-2x2.png."); 1368 } 1369 1370 } else { 1371 LOG.error("Compressor::compressProcess compress failed No image in PNG format is found."); 1372 throw new BundleException("Compress pack.res failed, compress failed No image in" 1373 + " PNG format is found."); 1374 } 1375 } 1376 pathToFile(utility, utility.getEntryCardPath(), ENTRYCARD_NAME, false); 1377 } 1378 } 1379 1380 /** 1381 * Check whether modelname meets specifications. 1382 * 1383 * @param name modelName 1384 * @return false and true 1385 */ isModelName(String name)1386 private boolean isModelName(String name) { 1387 for (String listName : list) { 1388 if (name.equals(listName)) { 1389 return true; 1390 } 1391 } 1392 return false; 1393 } 1394 isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName)1395 private boolean isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName) { 1396 if (thirdLevelDirectoryName == null || thirdLevelDirectoryName.isEmpty()) { 1397 return false; 1398 } 1399 if (ENTRYCARD_BASE_NAME.equals(thirdLevelDirectoryName)) { 1400 return true; 1401 } 1402 // example: zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi 1403 int firstDelimiterIndex = thirdLevelDirectoryName.indexOf("_"); 1404 if (firstDelimiterIndex < 0) { 1405 return false; 1406 } 1407 String language = thirdLevelDirectoryName.substring(0, firstDelimiterIndex); 1408 int secondDelimiterIndex = thirdLevelDirectoryName.indexOf("_", firstDelimiterIndex + 1); 1409 if (secondDelimiterIndex < 0) { 1410 return false; 1411 } 1412 String script = thirdLevelDirectoryName.substring(firstDelimiterIndex + 1, secondDelimiterIndex); 1413 int thirdDelimiterIndex = thirdLevelDirectoryName.indexOf("-", secondDelimiterIndex + 1); 1414 if (thirdDelimiterIndex < 0) { 1415 return false; 1416 } 1417 String country = thirdLevelDirectoryName.substring(secondDelimiterIndex + 1, thirdDelimiterIndex); 1418 if (!checkLanguage(language) || !checkScript(script) || !checkCountry(country)) { 1419 return false; 1420 } 1421 int forthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", thirdDelimiterIndex + 1); 1422 if (forthDelimiterIndex < 0) { 1423 return false; 1424 } 1425 String orientation = thirdLevelDirectoryName.substring(thirdDelimiterIndex + 1, forthDelimiterIndex); 1426 int fifthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", forthDelimiterIndex + 1); 1427 if (fifthDelimiterIndex < 0) { 1428 return false; 1429 } 1430 String deviceType = thirdLevelDirectoryName.substring(forthDelimiterIndex + 1, fifthDelimiterIndex); 1431 if (!checkOrientation(orientation) || !checkDeviceType(deviceType)) { 1432 return false; 1433 } 1434 int sixthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", fifthDelimiterIndex + 1); 1435 if (sixthDelimiterIndex < 0) { 1436 String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, 1437 thirdLevelDirectoryName.length()); 1438 return checkScreenDensity(screenDensity); 1439 } else { 1440 String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, sixthDelimiterIndex); 1441 if (!checkScreenDensity(screenDensity)) { 1442 return false; 1443 } 1444 } 1445 int seventhDelimiterIndex = thirdLevelDirectoryName.indexOf("-", sixthDelimiterIndex + 1); 1446 if (seventhDelimiterIndex < 0) { 1447 String tmp = thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, thirdLevelDirectoryName.length()); 1448 return checkColorModeOrShape(tmp); 1449 } 1450 if (!checkColorMode(thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, seventhDelimiterIndex))) { 1451 return false; 1452 } 1453 String shape = thirdLevelDirectoryName.substring(seventhDelimiterIndex + 1, thirdLevelDirectoryName.length()); 1454 return checkShape(shape); 1455 } 1456 checkLanguage(String language)1457 private boolean checkLanguage(String language) { 1458 if (!Pattern.compile(REGEX_LANGUAGE).matcher(language).matches()) { 1459 LOG.error("Compressor::compressProcess language " + language + " is not in ISO 639-1 list."); 1460 return false; 1461 } 1462 return true; 1463 } 1464 checkScript(String script)1465 private boolean checkScript(String script) { 1466 if (!Pattern.compile(REGEX_SCRIPT).matcher(script).matches()) { 1467 LOG.error("Compressor::compressProcess script " + script + " is not in ISO 15924 list."); 1468 return false; 1469 } 1470 return true; 1471 } 1472 checkCountry(String country)1473 private boolean checkCountry(String country) { 1474 if (!Pattern.compile(REGEX_COUNTRY).matcher(country).matches()) { 1475 LOG.error("Compressor::compressProcess country " + country + " is not in ISO 3166-1 list."); 1476 return false; 1477 } 1478 return true; 1479 } 1480 checkOrientation(String orientation)1481 private boolean checkOrientation(String orientation) { 1482 if (!Pattern.compile(REGEX_ORIENTATION).matcher(orientation).matches()) { 1483 LOG.error("Compressor::compressProcess orientation " + orientation + 1484 " is not in {vertical, horizontal} list."); 1485 return false; 1486 } 1487 return true; 1488 } 1489 checkDeviceType(String deviceType)1490 private boolean checkDeviceType(String deviceType) { 1491 if (!Pattern.compile(REGEX_DEVICE_TYPE).matcher(deviceType).matches()) { 1492 LOG.error("Compressor::compressProcess deviceType " + deviceType + 1493 " is not in {phone, tablet, car, tv, wearable, liteWearable, 2in1} list."); 1494 return false; 1495 } 1496 return true; 1497 } 1498 checkScreenDensity(String screenDensity)1499 private boolean checkScreenDensity(String screenDensity) { 1500 if (!Pattern.compile(REGEX_SCREEN_DENSITY).matcher(screenDensity).matches()) { 1501 LOG.error("Compressor::compressProcess screenDensity " + screenDensity + 1502 " is not in {sdpi, mdpi, ldpi, xldpi, xxldpi} list."); 1503 return false; 1504 } 1505 return true; 1506 } 1507 checkColorMode(String colorMode)1508 private boolean checkColorMode(String colorMode) { 1509 if (!Pattern.compile(REGEX_COLOR_MODE).matcher(colorMode).matches()) { 1510 LOG.error("Compressor::compressProcess colorMode " + colorMode + 1511 " is not in {light, dark} list."); 1512 return false; 1513 } 1514 return true; 1515 } 1516 checkColorModeOrShape(String tmp)1517 private boolean checkColorModeOrShape(String tmp) { 1518 if (Pattern.compile(REGEX_COLOR_MODE).matcher(tmp).matches() || 1519 Pattern.compile(REGEX_SHAPE).matcher(tmp).matches()) { 1520 return true; 1521 } 1522 LOG.error("Compressor::compressProcess " + tmp + 1523 " is neither in colorMode list {light, dark} nor in shape list {circle}."); 1524 return false; 1525 } 1526 checkShape(String shape)1527 private boolean checkShape(String shape) { 1528 if (Pattern.compile(REGEX_SHAPE).matcher(shape).matches()) { 1529 return true; 1530 } 1531 LOG.error("Compressor::compressProcess shape" + shape + " is not in {circle} list."); 1532 return false; 1533 } 1534 1535 /** 1536 * Check whether picturingName meets specifications. 1537 * 1538 * @param name picturingName 1539 * @param utility common data 1540 * @return false and true 1541 */ isPicturing(String name, Utility utility)1542 private boolean isPicturing(String name, Utility utility) { 1543 boolean isSpecifications = false; 1544 if (name == null || name.isEmpty()) { 1545 return isSpecifications; 1546 } 1547 if (!name.endsWith(PNG_SUFFIX) && !name.endsWith(UPPERCASE_PNG_SUFFIX)) { 1548 LOG.error("isPicturing: the suffix is not .png or .PNG."); 1549 return false; 1550 } 1551 int delimiterIndex = name.lastIndexOf("-"); 1552 if (delimiterIndex < 0) { 1553 LOG.error("isPicturing: the entry card naming format is invalid and should be separated by '-'."); 1554 return false; 1555 } 1556 String formName = name.substring(0, delimiterIndex); 1557 if (!utility.getFormNameList().contains(formName)) { 1558 LOG.error("isPicturing: the name is not same as formName, name: " + formName + " is not in " + 1559 utility.getFormNameList().toString() + "."); 1560 return false; 1561 } 1562 String dimension = name.substring(delimiterIndex + 1, name.lastIndexOf(".")); 1563 if (!supportDimensionsList.contains(dimension)) { 1564 LOG.error("isPicturing: the dimension: " + dimension + " is invalid, is not in the following list: " 1565 + "{1X2, 2X2, 2X4, 4X4}."); 1566 return false; 1567 } 1568 return true; 1569 } 1570 getFileList(final String filePath)1571 private void getFileList(final String filePath) throws BundleException { 1572 File file = new File(filePath); 1573 if (!file.exists()) { 1574 LOG.error("getFileList: file is not exists."); 1575 return; 1576 } 1577 File[] files = file.listFiles(); 1578 if (files == null) { 1579 LOG.error("getFileList: no file in this file path."); 1580 return; 1581 } 1582 for (File f : files) { 1583 try { 1584 if (f.isFile()) { 1585 if (f.getName().endsWith(".DS_Store")) { 1586 deleteFile(f.getCanonicalPath()); 1587 continue; 1588 } 1589 String snapshotDirectoryName = f.getParentFile().getName(); 1590 if (!ENTRYCARD_SNAPSHOT_NAME.equals(snapshotDirectoryName)) { 1591 LOG.error("The level-4 directory of EntryCard must be named as snapshot" + 1592 ", but current is: " + snapshotDirectoryName + "."); 1593 throw new BundleException("The level-4 directory of EntryCard must be named as snapshot" + 1594 ", but current is: " + snapshotDirectoryName + "."); 1595 } 1596 checkContain2x2EntryCard(f.getParentFile()); 1597 fileNameList.add(f.getCanonicalPath()); 1598 } else if (f.isDirectory()) { 1599 getFileList(f.getCanonicalPath()); 1600 } else { 1601 LOG.error("It's not file or directory."); 1602 } 1603 } catch (IOException msg) { 1604 LOG.error("IOException error: " + msg.getMessage()); 1605 return; 1606 } 1607 } 1608 } 1609 checkContain2x2EntryCard(final File snapshotDirectory)1610 private void checkContain2x2EntryCard(final File snapshotDirectory) throws IOException, BundleException { 1611 if (!snapshotDirectory.exists()) { 1612 LOG.error("checkContain2x2EntryCard: file is not exist: " + snapshotDirectory.getName() + "."); 1613 throw new BundleException("checkContain2x2EntryCard: file is not exist."); 1614 } 1615 File[] files = snapshotDirectory.listFiles(); 1616 if (files == null) { 1617 LOG.error("checkContain2x2EntryCard: no file in this file path."); 1618 throw new BundleException("checkContain2x2EntryCard: no file in this file path."); 1619 } 1620 1621 for (File entryCardFile : files) { 1622 if (entryCardFile.isFile() && entryCardFile.getName().contains(PIC_2X2)) { 1623 return; 1624 } 1625 } 1626 mIsContain2x2EntryCard = false; 1627 LOG.error("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in " 1628 + snapshotDirectory.getCanonicalPath() + "."); 1629 throw new BundleException("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in " 1630 + snapshotDirectory.getCanonicalPath() + "."); 1631 } 1632 1633 /** 1634 * compress file or directory. 1635 * 1636 * @param utility common data 1637 * @param path create new file by path 1638 * @param baseDir base path for file 1639 * @param isCompression if need compression 1640 * @throws BundleException FileNotFoundException|IOException. 1641 */ pathToFile(Utility utility, String path, String baseDir, boolean isCompression)1642 private void pathToFile(Utility utility, String path, String baseDir, boolean isCompression) 1643 throws BundleException { 1644 if (path.isEmpty()) { 1645 return; 1646 } 1647 File fileItem = new File(path); 1648 if (fileItem.isDirectory()) { 1649 File[] files = fileItem.listFiles(); 1650 if (files == null) { 1651 return; 1652 } 1653 for (File file : files) { 1654 if (file.isDirectory()) { 1655 compressDirectory(utility, file, baseDir, isCompression); 1656 } else if (isCompression) { 1657 compressFile(utility, file, baseDir, isCompression); 1658 } else { 1659 compressFile(utility, file, baseDir, isCompression); 1660 } 1661 } 1662 } else { 1663 compressFile(utility, fileItem, baseDir, isCompression); 1664 } 1665 } 1666 1667 /** 1668 * compress file directory. 1669 * 1670 * @param utility common data 1671 * @param dir file directory 1672 * @param baseDir current directory name 1673 * @param isCompression if need compression 1674 * @throws BundleException FileNotFoundException|IOException. 1675 */ compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)1676 private void compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression) 1677 throws BundleException { 1678 File[] files = dir.listFiles(); 1679 if (files == null) { 1680 return; 1681 } 1682 for (File file : files) { 1683 if (file.isDirectory()) { 1684 compressDirectory(utility, file, baseDir + dir.getName() + File.separator, isCompression); 1685 } else { 1686 compressFile(utility, file, baseDir + dir.getName() + File.separator, isCompression); 1687 } 1688 } 1689 } 1690 1691 /** 1692 * compress pack.info 1693 * 1694 * @param sourceFile source 1695 * @param zipOutputStream ZipOutputStream 1696 * @param name filename 1697 * @param KeepDirStructure Empty File 1698 */ compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure)1699 private void compress(File sourceFile, ZipOutputStream zipOutputStream, String name, 1700 boolean KeepDirStructure) { 1701 FileInputStream in = null; 1702 try { 1703 byte[] buf = new byte[BUFFER_SIZE]; 1704 if (sourceFile.isFile()) { 1705 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name); 1706 zipOutputStream.putNextEntry(zipEntry); 1707 in = new FileInputStream(sourceFile); 1708 int len; 1709 while ((len = in.read(buf)) != -1) { 1710 zipOutputStream.write(buf, 0, len); 1711 } 1712 zipOutputStream.closeEntry(); 1713 } else { 1714 File[] listFiles = sourceFile.listFiles(); 1715 if (listFiles == null || listFiles.length == 0) { 1716 if (KeepDirStructure) { 1717 if (!name.isEmpty()) { 1718 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name + "/"); 1719 zipOutputStream.putNextEntry(zipEntry); 1720 } else { 1721 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name); 1722 zipOutputStream.putNextEntry(zipEntry); 1723 } 1724 zipOutputStream.closeEntry(); 1725 } 1726 } else { 1727 for (File file : listFiles) { 1728 if (KeepDirStructure) { 1729 isNameEmpty(zipOutputStream, name, KeepDirStructure, file); 1730 } else { 1731 compress(file, zipOutputStream, file.getName(), KeepDirStructure); 1732 } 1733 } 1734 } 1735 } 1736 } catch (FileNotFoundException ignored) { 1737 LOG.error("Compressor::compressFile file not found exception."); 1738 } catch (IOException exception) { 1739 LOG.error("Compressor::compressFile io exception: " + exception.getMessage()); 1740 } catch (BundleException bundleException) { 1741 LOG.error("Compressor::compressFile bundle exception" + bundleException.getMessage()); 1742 } finally { 1743 Utility.closeStream(in); 1744 } 1745 } 1746 getStoredZipEntry(File sourceFile, String name)1747 private ZipEntry getStoredZipEntry(File sourceFile, String name) throws BundleException { 1748 ZipEntry zipEntry = new ZipEntry(name); 1749 zipEntry.setMethod(ZipEntry.STORED); 1750 zipEntry.setCompressedSize(sourceFile.length()); 1751 zipEntry.setSize(sourceFile.length()); 1752 CRC32 crc = getCrcFromFile(sourceFile); 1753 zipEntry.setCrc(crc.getValue()); 1754 FileTime fileTime = FileTime.fromMillis(FILE_TIME); 1755 zipEntry.setLastAccessTime(fileTime); 1756 zipEntry.setLastModifiedTime(fileTime); 1757 return zipEntry; 1758 } 1759 getCrcFromFile(File file)1760 private CRC32 getCrcFromFile(File file) throws BundleException { 1761 FileInputStream fileInputStream = null; 1762 CRC32 crc = new CRC32(); 1763 try { 1764 fileInputStream = new FileInputStream(file); 1765 byte[] buffer = new byte[BUFFER_SIZE]; 1766 1767 int count = fileInputStream.read(buffer); 1768 while (count > 0) { 1769 crc.update(buffer, 0, count); 1770 count = fileInputStream.read(buffer); 1771 } 1772 } catch (FileNotFoundException ignored) { 1773 LOG.error("Uncompressor::getCrcFromFile file not found exception."); 1774 throw new BundleException("Get Crc from file failed."); 1775 } catch (IOException exception) { 1776 LOG.error("Uncompressor::getCrcFromFile io exception: " + exception.getMessage()); 1777 throw new BundleException("Get Crc from file failed."); 1778 } finally { 1779 Utility.closeStream(fileInputStream); 1780 } 1781 return crc; 1782 } 1783 1784 /** 1785 * isNameEmpty 1786 * 1787 * @param zipOutputStream ZipOutputStream 1788 * @param name filename 1789 * @param KeepDirStructure KeepDirStructure 1790 * @param file file 1791 */ isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure, File file)1792 private void isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure, File file) { 1793 if (!name.isEmpty()) { 1794 compress(file, zipOutputStream, name + "/" + file.getName(), KeepDirStructure); 1795 } else { 1796 compress(file, zipOutputStream, file.getName(), KeepDirStructure); 1797 } 1798 } 1799 1800 /** 1801 * compress process. 1802 * 1803 * @param utility common data 1804 * @param srcFile source file to zip 1805 * @param baseDir current directory name of file 1806 * @param isCompression if need compression 1807 * @throws BundleException FileNotFoundException|IOException. 1808 */ compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)1809 private void compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression) 1810 throws BundleException { 1811 BufferedInputStream bufferedInputStream = null; 1812 FileInputStream fileInputStream = null; 1813 try { 1814 String entryName = (baseDir + srcFile.getName()).replace(File.separator, LINUX_FILE_SEPARATOR); 1815 ZipEntry zipEntry = new ZipEntry(entryName); 1816 if (!entryName.contains(RAW_FILE_PATH) && 1817 srcFile.getName().toLowerCase(Locale.ENGLISH).endsWith(JSON_SUFFIX)) { 1818 zipEntry.setMethod(ZipEntry.STORED); 1819 jsonSpecialProcess(utility, srcFile, zipEntry); 1820 return; 1821 } 1822 1823 if (isCompression) { 1824 zipEntry.setMethod(ZipEntry.DEFLATED); 1825 } else { 1826 zipEntry.setMethod(ZipEntry.STORED); 1827 1828 // update size 1829 zipEntry.setCompressedSize(srcFile.length()); 1830 zipEntry.setSize(srcFile.length()); 1831 1832 // update crc 1833 CRC32 crc = getCrcFromFile(utility, srcFile); 1834 zipEntry.setCrc(crc.getValue()); 1835 } 1836 1837 // update fileTime 1838 FileTime fileTime = FileTime.fromMillis(FILE_TIME); 1839 zipEntry.setLastAccessTime(fileTime); 1840 zipEntry.setLastModifiedTime(fileTime); 1841 1842 zipOut.putNextEntry(zipEntry); 1843 byte[] data = new byte[BUFFER_SIZE]; 1844 fileInputStream = new FileInputStream(srcFile); 1845 bufferedInputStream = new BufferedInputStream(fileInputStream); 1846 1847 int count = bufferedInputStream.read(data); 1848 while (count > 0) { 1849 zipOut.write(data, 0, count); 1850 count = bufferedInputStream.read(data); 1851 } 1852 } catch (FileNotFoundException ignored) { 1853 throw new BundleException("CoompressFile failed."); 1854 } catch (IOException exception) { 1855 LOG.error("Compressor::compressFile io exception: " + exception.getMessage()); 1856 throw new BundleException("CoompressFile failed."); 1857 } finally { 1858 Utility.closeStream(bufferedInputStream); 1859 Utility.closeStream(fileInputStream); 1860 } 1861 } 1862 1863 /** 1864 * check hap type for pack app. 1865 * 1866 * @param hapPath source file to zip 1867 * @return true is for is stage type and false is for FA type 1868 * @throws BundleException FileNotFoundException|IOException. 1869 */ isModuleHap(String hapPath)1870 public static boolean isModuleHap(String hapPath) throws BundleException { 1871 if (!hapPath.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)) { 1872 return true; 1873 } 1874 1875 FileInputStream zipInput = null; 1876 ZipInputStream zin = null; 1877 ZipEntry entry = null; 1878 try { 1879 zipInput = new FileInputStream(hapPath); 1880 zin = new ZipInputStream(zipInput); 1881 while ((entry = zin.getNextEntry()) != null) { 1882 if (MODULE_JSON.equals(entry.getName().toLowerCase())) { 1883 return true; 1884 } 1885 } 1886 } catch (IOException exception) { 1887 LOG.error("Compressor::isModuleHap io exception: " + exception.getMessage()); 1888 throw new BundleException("Compressor::isModuleHap failed."); 1889 } finally { 1890 Utility.closeStream(zipInput); 1891 Utility.closeStream(zin); 1892 } 1893 return false; 1894 } 1895 1896 /** 1897 * get CRC32 from file. 1898 * 1899 * @param utility common data 1900 * @param file source file 1901 * @return CRC32 1902 * @throws BundleException FileNotFoundException|IOException. 1903 */ getCrcFromFile(Utility utility, File file)1904 private CRC32 getCrcFromFile(Utility utility, File file) throws BundleException { 1905 FileInputStream fileInputStream = null; 1906 CRC32 crc = new CRC32(); 1907 try { 1908 fileInputStream = new FileInputStream(file); 1909 byte[] buffer = new byte[BUFFER_SIZE]; 1910 1911 int count = fileInputStream.read(buffer); 1912 while (count > 0) { 1913 crc.update(buffer, 0, count); 1914 count = fileInputStream.read(buffer); 1915 } 1916 } catch (FileNotFoundException ignored) { 1917 throw new BundleException("Get Crc from file failed."); 1918 } catch (IOException exception) { 1919 LOG.error("Compressor::getCrcFromFile io exception: " + exception.getMessage()); 1920 throw new BundleException("Get Crc from file failed."); 1921 } finally { 1922 Utility.closeStream(fileInputStream); 1923 } 1924 return crc; 1925 } 1926 infoSpecialProcess(Utility utility, File srcFile)1927 private void infoSpecialProcess(Utility utility, File srcFile) 1928 throws BundleException { 1929 FileInputStream fileInputStream = null; 1930 BufferedReader bufferedReader = null; 1931 InputStreamReader inputStreamReader = null; 1932 1933 try { 1934 fileInputStream = new FileInputStream(srcFile); 1935 inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 1936 bufferedReader = new BufferedReader(inputStreamReader); 1937 bufferedReader.mark((int) srcFile.length() + 1); 1938 // parse moduleName from pack.info 1939 parsePackModuleName(bufferedReader, utility); 1940 bufferedReader.reset(); 1941 parsePackFormName(bufferedReader, utility); 1942 bufferedReader.reset(); 1943 parseDeviceType(bufferedReader, utility); 1944 bufferedReader.reset(); 1945 1946 Pattern pattern = Pattern.compile(System.lineSeparator()); 1947 String str = bufferedReader.readLine(); 1948 StringBuilder builder = new StringBuilder(); 1949 while (str != null) { 1950 Matcher matcher = pattern.matcher(str.trim()); 1951 String dest = matcher.replaceAll(""); 1952 builder.append(dest); 1953 str = bufferedReader.readLine(); 1954 } 1955 } catch (IOException exception) { 1956 LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage()); 1957 throw new BundleException("Json special process failed."); 1958 } finally { 1959 Utility.closeStream(bufferedReader); 1960 Utility.closeStream(inputStreamReader); 1961 Utility.closeStream(fileInputStream); 1962 } 1963 } 1964 1965 /** 1966 * trim and remove "\r\n" in *.json file. 1967 * 1968 * @param utility common data 1969 * @param srcFile file input 1970 * @param entry zip file entry 1971 * @throws BundleException FileNotFoundException|IOException. 1972 */ jsonSpecialProcess(Utility utility, File srcFile, ZipEntry entry)1973 private void jsonSpecialProcess(Utility utility, File srcFile, ZipEntry entry) 1974 throws BundleException { 1975 FileInputStream fileInputStream = null; 1976 BufferedReader bufferedReader = null; 1977 InputStreamReader inputStreamReader = null; 1978 1979 try { 1980 fileInputStream = new FileInputStream(srcFile); 1981 inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 1982 bufferedReader = new BufferedReader(inputStreamReader); 1983 bufferedReader.mark((int) srcFile.length() + 1); 1984 bufferedReader.reset(); 1985 String srcName = srcFile.getName().toLowerCase(Locale.ENGLISH); 1986 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 1987 String jsonString = optional.get(); 1988 String jsonName = new File(utility.getJsonPath()).getName().toLowerCase(Locale.ENGLISH); 1989 if (CONFIG_JSON.equals(jsonName)) { 1990 parseCompressNativeLibs(bufferedReader, utility); 1991 utility.setModuleName(ModuleJsonUtil.parseFaModuleName(jsonString)); 1992 } else if (MODULE_JSON.equals(jsonName)) { 1993 utility.setIsCompressNativeLibs(ModuleJsonUtil.stageIsCompressNativeLibs(jsonString)); 1994 utility.setModuleName(ModuleJsonUtil.parseStageModuleName(jsonString)); 1995 } else if (PATCH_JSON.equals(jsonName)) { 1996 utility.setModuleName(ModuleJsonUtil.parsePatchModuleName(jsonString)); 1997 } 1998 bufferedReader.reset(); 1999 parseDeviceType(bufferedReader, utility); 2000 bufferedReader.reset(); 2001 2002 Pattern pattern = Pattern.compile(System.lineSeparator()); 2003 String str = bufferedReader.readLine(); 2004 StringBuilder builder = new StringBuilder(); 2005 while (str != null) { 2006 Matcher matcher = pattern.matcher(str.trim()); 2007 String dest = matcher.replaceAll(""); 2008 builder.append(dest); 2009 str = bufferedReader.readLine(); 2010 } 2011 byte[] trimJson = builder.toString().getBytes(StandardCharsets.UTF_8); 2012 2013 // update crc 2014 CRC32 crc = new CRC32(); 2015 crc.update(trimJson); 2016 entry.setCrc(crc.getValue()); 2017 2018 // update size 2019 entry.setSize(trimJson.length); 2020 entry.setCompressedSize(trimJson.length); 2021 2022 // update fileTime 2023 FileTime fileTime = FileTime.fromMillis(FILE_TIME); 2024 entry.setLastAccessTime(fileTime); 2025 entry.setLastModifiedTime(fileTime); 2026 2027 // compress data 2028 zipOut.putNextEntry(entry); 2029 zipOut.write(trimJson); 2030 } catch (IOException exception) { 2031 LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage()); 2032 throw new BundleException("Json special process failed."); 2033 } finally { 2034 Utility.closeStream(bufferedReader); 2035 Utility.closeStream(inputStreamReader); 2036 Utility.closeStream(fileInputStream); 2037 } 2038 } 2039 2040 /** 2041 * Parse module name from pack.info 2042 * 2043 * @param bufferedReader pack.info buffered Reader 2044 * @param utility common data 2045 * @throws BundleException IOException 2046 */ parsePackModuleName(BufferedReader bufferedReader, Utility utility)2047 private void parsePackModuleName(BufferedReader bufferedReader, Utility utility) throws BundleException { 2048 String lineStr = null; 2049 try { 2050 while ((lineStr = bufferedReader.readLine()) != null) { 2051 if (lineStr.contains(DISTRO)) { 2052 continue; 2053 } 2054 if (lineStr.contains(JSON_END)) { 2055 continue; 2056 } 2057 if (lineStr.contains(MODULE_NAME_NEW) || lineStr.contains(MODULE_NAME)) { 2058 getModuleNameFromString(lineStr, utility); 2059 } 2060 } 2061 } catch (IOException exception) { 2062 LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage()); 2063 throw new BundleException("Parse module name failed."); 2064 } 2065 } 2066 2067 /** 2068 * Parse Forms name from pack.info 2069 * 2070 * @param bufferedReader pack.info buffered Reader 2071 * @param utility common data 2072 * @throws BundleException IOException 2073 */ parsePackFormName(BufferedReader bufferedReader, Utility utility)2074 private void parsePackFormName(BufferedReader bufferedReader, Utility utility) throws BundleException { 2075 String lineStr = null; 2076 try { 2077 while ((lineStr = bufferedReader.readLine()) != null) { 2078 if (lineStr.contains("abilities")) { 2079 continue; 2080 } 2081 if (lineStr.contains(FORMS)) { 2082 continue; 2083 } 2084 if (lineStr.contains(JSON_END)) { 2085 continue; 2086 } 2087 if (lineStr.contains(NAME)) { 2088 getNameFromString(lineStr, utility); 2089 } 2090 } 2091 } catch (IOException exception) { 2092 LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage()); 2093 throw new BundleException("Parse module name failed."); 2094 } 2095 } 2096 2097 2098 /** 2099 * Get name from line string 2100 * 2101 * @param lineStr line string 2102 * @param utility common data 2103 * @throws BundleException StringIndexOutOfBoundsException 2104 */ getNameFromString(String lineStr, Utility utility)2105 private void getNameFromString(String lineStr, Utility utility) throws BundleException { 2106 try { 2107 int endIndex = lineStr.lastIndexOf(SEMICOLON); 2108 if (endIndex <= 0) { 2109 LOG.error("Compressor::getModuleNameFromString field the json is not standard."); 2110 throw new BundleException("Parse module name failed, module-name is invalid."); 2111 } 2112 int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1; 2113 String formName = lineStr.substring(startIndex, endIndex); 2114 if (formName == null || formName.isEmpty()) { 2115 LOG.error("Compressor::getModuleNameFromString field module-name is empty."); 2116 throw new BundleException("Parse module name failed, module-name is empty."); 2117 } 2118 String[] nameList = formName.split("\\."); 2119 if (nameList.length <= 1) { 2120 formNamesList.add(formName); 2121 utility.addFormNameList(formName); 2122 } 2123 } catch (StringIndexOutOfBoundsException exception) { 2124 LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage()); 2125 throw new BundleException("Parse module name failed, module-name is invalid."); 2126 } 2127 } 2128 2129 /** 2130 * Get module name from line string 2131 * 2132 * @param lineStr line string 2133 * @param utility common data 2134 * @throws BundleException StringIndexOutOfBoundsException 2135 */ getModuleNameFromString(String lineStr, Utility utility)2136 private void getModuleNameFromString(String lineStr, Utility utility) throws BundleException { 2137 try { 2138 int endIndex = lineStr.lastIndexOf(SEMICOLON); 2139 if (endIndex <= 0) { 2140 LOG.error("Compressor::getModuleNameFromString field the json is not standard."); 2141 throw new BundleException("Parse module name failed, module-name is invalid."); 2142 } 2143 int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1; 2144 String moduleName = lineStr.substring(startIndex, endIndex); 2145 list.add(moduleName); 2146 if (moduleName == null || moduleName.isEmpty()) { 2147 LOG.error("Compressor::getModuleNameFromString field module-name is empty."); 2148 throw new BundleException("Parse module name failed, module-name is empty."); 2149 } 2150 utility.setModuleName(moduleName); 2151 } catch (StringIndexOutOfBoundsException exception) { 2152 LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage()); 2153 throw new BundleException("Parse module name failed, module-name is invalid."); 2154 } 2155 } 2156 parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility)2157 private void parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility) throws BundleException { 2158 String lineStr = null; 2159 try { 2160 while ((lineStr = bufferedReader.readLine()) != null) { 2161 if (lineStr.contains(COMPRESS_NATIVE_LIBS)) { 2162 if (lineStr.contains(Utility.FALSE_STRING)) { 2163 utility.setIsCompressNativeLibs(false); 2164 break; 2165 } 2166 } 2167 } 2168 } catch (IOException exception) { 2169 LOG.error("Compressor::parseCompressNativeLibs io exception: " + exception.getMessage()); 2170 throw new BundleException("Parse compress native libs failed."); 2171 } 2172 } 2173 2174 /** 2175 * ZipOutputStream flush, closeEntry and finish. 2176 */ closeZipOutputStream()2177 private void closeZipOutputStream() { 2178 try { 2179 if (zipOut != null) { 2180 zipOut.flush(); 2181 } 2182 } catch (IOException exception) { 2183 LOG.error("Compressor::closeZipOutputStream flush exception " + exception.getMessage()); 2184 } 2185 try { 2186 if (zipOut != null) { 2187 zipOut.closeEntry(); 2188 } 2189 } catch (IOException exception) { 2190 LOG.error("Compressor::closeZipOutputStream close entry io exception " + exception.getMessage()); 2191 } 2192 try { 2193 if (zipOut != null) { 2194 zipOut.finish(); 2195 } 2196 } catch (IOException exception) { 2197 LOG.error("Compressor::closeZipOutputStream finish exception " + exception.getMessage()); 2198 } 2199 } 2200 2201 /** 2202 * Parse device type from config.json 2203 * 2204 * @param bufferedReader config.json buffered Reader 2205 * @param utility common data 2206 * @throws BundleException IOException 2207 */ parseDeviceType(BufferedReader bufferedReader, Utility utility)2208 private void parseDeviceType(BufferedReader bufferedReader, Utility utility) throws BundleException { 2209 String lineStr = null; 2210 boolean isDeviceTypeStart = false; 2211 try { 2212 while ((lineStr = bufferedReader.readLine()) != null) { 2213 if (!isDeviceTypeStart) { 2214 if (lineStr.contains(DEVICE_TYPE)) { 2215 isDeviceTypeStart = true; 2216 } 2217 continue; 2218 } 2219 if (lineStr.contains(JSON_END)) { 2220 break; 2221 } 2222 utility.setDeviceType(lineStr); 2223 break; 2224 } 2225 } catch (IOException exception) { 2226 LOG.error("Compressor::parseDeviceType io exception: " + exception.getMessage()); 2227 throw new BundleException("Parse device type failed."); 2228 } 2229 } 2230 2231 /** 2232 * check hap and hsp is valid in haps when pack app, check type has bundleName, 2233 * vendor, version, apiVersion moduleName, packageName. 2234 * 2235 * @param fileLists is the list of hapPath. 2236 * @return true is for successful and false is for failed 2237 * @throws BundleException FileNotFoundException|IOException. 2238 */ checkHapIsValid(List<String> fileLists, boolean isSharedApp)2239 private boolean checkHapIsValid(List<String> fileLists, boolean isSharedApp) throws BundleException { 2240 List<HapVerifyInfo> hapVerifyInfos = new ArrayList<>(); 2241 for (String hapPath : fileLists) { 2242 if (hapPath.isEmpty()) { 2243 LOG.error("Compressor::checkHapIsValid input wrong hap file."); 2244 throw new BundleException("Compressor::checkHapIsValid input wrong hap file."); 2245 } 2246 File srcFile = new File(hapPath); 2247 String fileStr = srcFile.getName(); 2248 if (fileStr.isEmpty()) { 2249 LOG.error("Compressor::checkHapIsValid get file name failed."); 2250 throw new BundleException("Compressor::checkHapIsValid get file name failed."); 2251 } 2252 if (!fileStr.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX) 2253 && !fileStr.toLowerCase(Locale.ENGLISH).endsWith(HSP_SUFFIX)) { 2254 LOG.error("Compressor::checkHapIsValid input wrong hap file."); 2255 throw new BundleException("Compressor::checkHapIsValid input wrong hap file."); 2256 } 2257 if (isModuleHap(hapPath)) { 2258 hapVerifyInfos.add(parseStageHapVerifyInfo(hapPath)); 2259 } else { 2260 hapVerifyInfos.add(parseFAHapVerifyInfo(hapPath)); 2261 } 2262 } 2263 if (isSharedApp) { 2264 boolean res = checkSharedAppIsValid(hapVerifyInfos); 2265 if (!res) { 2266 return false; 2267 } 2268 if (!isOverlay) { 2269 return true; 2270 } 2271 } else { 2272 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 2273 String bundleType = hapVerifyInfo.getBundleType(); 2274 if (TYPE_SHARED.equals(bundleType)) { 2275 LOG.error("Compressor::checkHapIsValid shared app should not be included in --hsp-path."); 2276 return false; 2277 } 2278 } 2279 } 2280 setAtomicServiceFileSizeLimit(hapVerifyInfos); 2281 if (!HapVerify.checkHapIsValid(hapVerifyInfos)) { 2282 return false; 2283 } 2284 return true; 2285 } 2286 2287 /** 2288 * parse stage file to hap verify info from hap path. 2289 * 2290 * @param filePath is the hap path 2291 * @return hapVerifyInfo 2292 */ parseStageHapVerifyInfo(String filePath)2293 public static HapVerifyInfo parseStageHapVerifyInfo(String filePath) throws BundleException { 2294 HapVerifyInfo hapVerifyInfo = readStageHapVerifyInfo(filePath); 2295 hapVerifyInfo.setStageModule(true); 2296 ModuleJsonUtil.parseStageHapVerifyInfo(hapVerifyInfo); 2297 hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath)); 2298 return hapVerifyInfo; 2299 } 2300 2301 /** 2302 * set file size limit for each HapVerifyInfo. 2303 * 2304 * @param hapVerifyInfos Indicates hapVerifyInfo list. 2305 */ setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos)2306 public static void setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos) { 2307 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 2308 if (!hapVerifyInfo.getBundleType().equals(ATOMIC_SERVICE)) { 2309 continue; 2310 } 2311 hapVerifyInfo.setEntrySizeLimit(getEntryModuleSizeLimit()); 2312 hapVerifyInfo.setNotEntrySizeLimit(getNotEntryModuleSizeLimit()); 2313 hapVerifyInfo.setSumSizeLimit(getSumModuleSizeLimit()); 2314 } 2315 } 2316 2317 /** 2318 * parse fa file to hap verify info from hap path. 2319 * 2320 * @param filePath is the hap path 2321 * @return hapVerifyInfo 2322 */ parseFAHapVerifyInfo(String filePath)2323 public static HapVerifyInfo parseFAHapVerifyInfo(String filePath) throws BundleException { 2324 HapVerifyInfo hapVerifyInfo = readFAHapVerifyInfo(filePath); 2325 hapVerifyInfo.setStageModule(false); 2326 hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath)); 2327 ModuleJsonUtil.parseFAHapVerifyInfo(hapVerifyInfo); 2328 return hapVerifyInfo; 2329 } 2330 2331 /** 2332 * read stage hap verify info from hap file. 2333 * 2334 * @param srcPath source file to zip 2335 * @return HapVerifyInfo of parse result 2336 * @throws BundleException FileNotFoundException|IOException. 2337 */ readStageHapVerifyInfo(String srcPath)2338 public static HapVerifyInfo readStageHapVerifyInfo(String srcPath) throws BundleException { 2339 HapVerifyInfo hapVerifyInfo = new HapVerifyInfo(); 2340 ZipFile zipFile = null; 2341 try { 2342 File srcFile = new File(srcPath); 2343 zipFile = new ZipFile(srcFile); 2344 hapVerifyInfo.setResourceMap(FileUtils.getProfileJson(zipFile)); 2345 hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(MODULE_JSON, zipFile)); 2346 } catch (IOException e) { 2347 LOG.error("FileUtil::parseStageHapVerifyInfo file not available."); 2348 throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available."); 2349 } finally { 2350 Utility.closeStream(zipFile); 2351 } 2352 return hapVerifyInfo; 2353 } 2354 2355 /** 2356 * read fa hap verify info from hap file. 2357 * 2358 * @param srcPath source file to zip 2359 * @return HapVerifyInfo of parse result 2360 * @throws BundleException FileNotFoundException|IOException. 2361 */ readFAHapVerifyInfo(String srcPath)2362 public static HapVerifyInfo readFAHapVerifyInfo(String srcPath) throws BundleException { 2363 HapVerifyInfo hapVerifyInfo = new HapVerifyInfo(); 2364 ZipFile zipFile = null; 2365 try { 2366 File srcFile = new File(srcPath); 2367 zipFile = new ZipFile(srcFile); 2368 hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(CONFIG_JSON, zipFile)); 2369 } catch (IOException e) { 2370 LOG.error("FileUtil::parseStageHapVerifyInfo file not available."); 2371 throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available."); 2372 } finally { 2373 Utility.closeStream(zipFile); 2374 } 2375 return hapVerifyInfo; 2376 } 2377 2378 /** 2379 * compress in hqf mode. 2380 * 2381 * @param utility common data 2382 * @throws BundleException FileNotFoundException|IOException. 2383 */ compressHQFMode(Utility utility)2384 private void compressHQFMode(Utility utility) throws BundleException { 2385 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 2386 2387 if (!utility.getEtsPath().isEmpty()) { 2388 pathToFile(utility, utility.getEtsPath(), ETS_PATH, false); 2389 } 2390 if (!utility.getLibPath().isEmpty()) { 2391 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, false); 2392 } 2393 } 2394 2395 /** 2396 * compress in appqf mode. 2397 * 2398 * @param utility common data 2399 * @throws BundleException FileNotFoundException|IOException. 2400 */ compressAPPQFMode(Utility utility)2401 private void compressAPPQFMode(Utility utility) throws BundleException { 2402 List<String> fileList = utility.getFormatedHQFList(); 2403 if (!checkHQFIsValid(fileList)) { 2404 LOG.error("checkHQFIsValid failed when pack appqf file."); 2405 throw new BundleException("checkHQFIsValid failed when pack appqf file."); 2406 } 2407 for (String hapPath : fileList) { 2408 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 2409 } 2410 } 2411 2412 /** 2413 * check input hqf is valid. 2414 * 2415 * @param fileList is input path of hqf files 2416 * @throws BundleException FileNotFoundException|IOException. 2417 */ checkHQFIsValid(List<String> fileList)2418 private boolean checkHQFIsValid(List<String> fileList) throws BundleException { 2419 List<HQFInfo> hqfVerifyInfos = new ArrayList<>(); 2420 for (String file : fileList) { 2421 hqfVerifyInfos.add(ModuleJsonUtil.parseHQFInfo(file)); 2422 } 2423 if (!HQFVerify.checkHQFIsValid(hqfVerifyInfos)) { 2424 LOG.error("input hqf is invalid."); 2425 return false; 2426 } 2427 return true; 2428 } 2429 compressHSPMode(Utility utility)2430 private void compressHSPMode(Utility utility) throws BundleException { 2431 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 2432 2433 pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false); 2434 2435 if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 2436 String assetsPath = NULL_DIR_NAME; 2437 pathToFile(utility, utility.getIndexPath(), assetsPath, false); 2438 } 2439 2440 if (!utility.getLibPath().isEmpty()) { 2441 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs()); 2442 } 2443 2444 if (!utility.getANPath().isEmpty()) { 2445 pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false); 2446 } 2447 2448 if (!utility.getAPPath().isEmpty()) { 2449 pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false); 2450 } 2451 2452 if (!utility.getFilePath().isEmpty()) { 2453 pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false); 2454 } 2455 2456 if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) { 2457 String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 2458 + RESOURCES_DIR_NAME; 2459 if (DEVICE_TYPE_FITNESSWATCH.equals( 2460 utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim()) || 2461 DEVICE_TYPE_FITNESSWATCH_NEW.equals( 2462 utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim())) { 2463 resPath = RES_DIR_NAME; 2464 } 2465 pathToFile(utility, utility.getResPath(), resPath, false); 2466 } 2467 2468 if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 2469 String resourcesPath = RESOURCES_DIR_NAME; 2470 pathToFile(utility, utility.getResourcesPath(), resourcesPath, false); 2471 } 2472 if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 2473 String jsPath = JS_PATH; 2474 pathToFile(utility, utility.getJsPath(), jsPath, false); 2475 } 2476 2477 if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 2478 String etsPath = ETS_PATH; 2479 pathToFile(utility, utility.getEtsPath(), etsPath, false); 2480 } 2481 2482 if (!utility.getRpcidPath().isEmpty()) { 2483 String rpcidPath = NULL_DIR_NAME; 2484 pathToFile(utility, utility.getRpcidPath(), rpcidPath, false); 2485 } 2486 2487 if (!utility.getAssetsPath().isEmpty()) { 2488 pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false); 2489 } 2490 2491 if (!utility.getBinPath().isEmpty()) { 2492 pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false); 2493 } 2494 2495 if (!utility.getPackInfoPath().isEmpty()) { 2496 pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false); 2497 } 2498 2499 // pack --dir-list 2500 if (!utility.getFormatedDirList().isEmpty()) { 2501 for (int i = 0; i < utility.getFormatedDirList().size(); ++i) { 2502 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator; 2503 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false); 2504 } 2505 } 2506 2507 compressHapModeMultiple(utility); 2508 } 2509 checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos)2510 private static boolean checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException { 2511 if (hapVerifyInfos.isEmpty()) { 2512 LOG.error("no module included"); 2513 return false; 2514 } 2515 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 2516 if (!hapVerifyInfo.getTargetBundleName().isEmpty()) { 2517 isOverlay = true; 2518 return true; 2519 } 2520 } 2521 return HapVerify.checkSharedApppIsValid(hapVerifyInfos); 2522 } 2523 } 2524