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