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