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 == null || !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 //pack encrypt.json file 1219 packEncryptJsonFile(utility); 1220 } catch (BundleException e) { 1221 LOG.error("Compressor::compressAppMode compress failed. msg: " + e.getMessage()); 1222 throw new BundleException("Compressor::compressAppMode compress failed."); 1223 } finally { 1224 // delete temp file 1225 for (String path : fileList) { 1226 deleteFile(path); 1227 } 1228 deleteFile(tempPath); 1229 deleteFile(hspTempDirPath); 1230 } 1231 } 1232 compressFastAppMode(Utility utility)1233 private void compressFastAppMode(Utility utility) throws BundleException { 1234 Path appOutPath = Paths.get(utility.getOutPath().trim()); 1235 Path tmpDir = null; 1236 try { 1237 tmpDir = Files.createTempDirectory(appOutPath.getParent(), TEMP_DIR); 1238 Path appPackInfo = Paths.get(utility.getPackInfoPath()); 1239 List<String> fileList = new ArrayList<>(); 1240 for (String hapPath : utility.getFormattedHapPathList()) { 1241 Path path = Paths.get(hapPath); 1242 Path hap = PackageUtil.pack(path, appPackInfo, tmpDir, utility.getCompressLevel()); 1243 if (hap != null) { 1244 fileList.add(hap.toString()); 1245 } 1246 } 1247 for (String hspPath : utility.getFormattedHspPathList()) { 1248 Path path = Paths.get(hspPath); 1249 Path hsp = PackageUtil.pack(path, appPackInfo, tmpDir, utility.getCompressLevel()); 1250 if (hsp != null) { 1251 fileList.add(hsp.toString()); 1252 } 1253 } 1254 // check hap is valid 1255 if (!checkHapIsValid(fileList, utility.getSharedApp())) { 1256 String msg = "Compressor::checkHapIsValid verify failed, check version, " + 1257 "apiVersion, moduleName, packageName."; 1258 LOG.error(msg); 1259 throw new BundleException(msg); 1260 } 1261 // packApp 1262 packFastApp(utility, fileList); 1263 } catch (IOException ex) { 1264 LOG.error("Compressor::compressAppMode compress failed: " + ex.getMessage()); 1265 throw new BundleException("Compressor::compressAppMode compress failed."); 1266 } finally { 1267 if (tmpDir != null) { 1268 PackageUtil.rmdir(tmpDir); 1269 } 1270 } 1271 } 1272 packFastApp(Utility utility, List<String> fileList)1273 private void packFastApp(Utility utility, List<String> fileList) throws BundleException { 1274 // pack.info 1275 pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false); 1276 // pack encrypt.json file 1277 packEncryptJsonFile(utility); 1278 // hap/hsp 1279 for (String hapPath : fileList) { 1280 HapVerifyInfo hapVerifyInfo = hapVerifyInfoMap.get(getFileNameByPath(hapPath)); 1281 if (hapVerifyInfo != null && !hapVerifyInfo.isDebug()) { 1282 pathToFile(utility, hapPath, NULL_DIR_NAME, true); 1283 } else { 1284 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 1285 } 1286 } 1287 // form/card 1288 if (!utility.getEntryCardPath().isEmpty()) { 1289 String entryCardPath = ENTRYCARD_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 1290 + ENTRYCARD_BASE_NAME + ENTRYCARD_SNAPSHOT_NAME; 1291 for (String entryCardPathItem : utility.getformattedEntryCardPathList()) { 1292 pathToFile(utility, entryCardPathItem, entryCardPath, true); 1293 } 1294 } 1295 if (!utility.getPackResPath().isEmpty()) { 1296 pathToFile(utility, utility.getPackResPath(), NULL_DIR_NAME, false); 1297 } 1298 // others 1299 if (!utility.getCertificatePath().isEmpty()) { 1300 pathToFile(utility, utility.getCertificatePath(), NULL_DIR_NAME, false); 1301 } 1302 if (!utility.getSignaturePath().isEmpty()) { 1303 pathToFile(utility, utility.getSignaturePath(), NULL_DIR_NAME, false); 1304 } 1305 } 1306 1307 /** 1308 * compress in app mode for multi project. 1309 * 1310 * @param utility common data 1311 * @throws BundleException FileNotFoundException|IOException. 1312 */ compressAppModeForMultiProject(Utility utility)1313 private void compressAppModeForMultiProject(Utility utility) throws BundleException { 1314 List<String> fileList = new ArrayList<>(); 1315 File appOutputFile = new File(utility.getOutPath().trim()); 1316 String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR; 1317 String tempSelectedHapPath = appOutputFile.getParentFile().getParent() +File.separator + TEMP_SELECTED_HAP_DIR; 1318 try { 1319 File tempSelectedHapDir = new File(tempSelectedHapPath); 1320 FileUtils.makeDir(tempSelectedHapDir); 1321 File tempHapDir = new File(tempPath); 1322 FileUtils.makeDir(tempHapDir); 1323 // pack app and dispose conflict 1324 // save hap name into list 1325 List<String> seletedHaps = new ArrayList<>(); 1326 String finalPackInfoStr = disposeApp(utility, seletedHaps, tempSelectedHapPath); 1327 // pack hap and dispose conflict 1328 finalPackInfoStr = disposeHap(utility, seletedHaps, tempSelectedHapPath, finalPackInfoStr); 1329 1330 // save final pack.info file 1331 String finalPackInfoPath = tempSelectedHapDir.getPath() + File.separator + PACKINFO_NAME; 1332 writePackInfo(finalPackInfoPath, finalPackInfoStr); 1333 // pack haps 1334 for (String selectedHapName : seletedHaps) { 1335 String hapPathItem = tempSelectedHapDir.getPath() + File.separator + selectedHapName; 1336 File hapFile = new File(hapPathItem.trim()); 1337 String hapTempPath = tempHapDir.getPath() + File.separator + hapFile.getName(); 1338 fileList.add(hapTempPath); 1339 compressPackinfoIntoHap(hapPathItem, hapTempPath, finalPackInfoPath, utility.getCompressLevel()); 1340 } 1341 // check hap is valid 1342 if (!checkHapIsValid(fileList, false)) { 1343 String errMsg = "Compressor::compressAppModeForMultiProject There are some " + 1344 "haps with different version code or duplicated moduleName or packageName."; 1345 throw new BundleException(errMsg); 1346 } 1347 for (String hapPath : fileList) { 1348 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 1349 } 1350 File file = new File(finalPackInfoPath); 1351 compressFile(utility, file, NULL_DIR_NAME, false); 1352 //pack encrypt.json file 1353 packEncryptJsonFile(utility); 1354 } catch (BundleException | IOException exception) { 1355 String errMsg = "Compressor::compressAppModeForMultiProject file failed: " + exception.getMessage(); 1356 LOG.error(errMsg); 1357 throw new BundleException(errMsg); 1358 } finally { 1359 deleteFile(tempPath); 1360 deleteFile(tempSelectedHapPath); 1361 } 1362 } 1363 1364 /** 1365 * compress in hapAddition mode. 1366 * 1367 * @param utility common data 1368 */ hapAddition(Utility utility)1369 private void hapAddition(Utility utility) { 1370 File hapPath = new File(utility.getAbsoluteHapPath()); 1371 String hapFileName = hapPath.getName(); 1372 1373 File destFile = new File(utility.getOutPath() + LINUX_FILE_SEPARATOR + hapFileName); 1374 File outParentFile = destFile.getParentFile(); 1375 if ((outParentFile != null) && (!outParentFile.exists())) { 1376 if (!outParentFile.mkdirs()) { 1377 LOG.error("Compressor::hapAddition create out file parent directory failed."); 1378 } 1379 } 1380 FileOutputStream fileOut = null; 1381 CheckedOutputStream checkedOut = null; 1382 String currentDir = System.getProperty("user.dir"); 1383 String hapAdditionPath = currentDir + LINUX_FILE_SEPARATOR + HAPADDITION_FOLDER_NAME; 1384 String backName = BACKUP_PREFIX + hapFileName; 1385 String hapPathOri = utility.getHapPath(); 1386 try { 1387 copyHapFile(utility, backName); 1388 fileOut = new FileOutputStream(destFile); 1389 checkedOut = new CheckedOutputStream(fileOut, new CRC32()); 1390 zipOut = new ZipArchiveOutputStream(checkedOut); 1391 1392 compressHapAddition(utility, hapAdditionPath); 1393 } catch (BundleException | IOException exception) { 1394 LOG.error("Compressor::HapAddition hapFile not found exception" + exception.getMessage()); 1395 copyFileSafely(backName, hapPathOri); 1396 } finally { 1397 closeZipOutputStream(); 1398 Utility.closeStream(zipOut); 1399 Utility.closeStream(checkedOut); 1400 Utility.closeStream(fileOut); 1401 // delete packaging and unpacking process files 1402 deleteFile(backName); 1403 deleteFile(hapAdditionPath); 1404 } 1405 } 1406 copyHapFile(Utility utility, String backName)1407 private void copyHapFile(Utility utility, String backName) throws IOException, BundleException { 1408 File hapFile = new File(utility.getAbsoluteHapPath()); 1409 String currentDir = System.getProperty("user.dir"); 1410 String backupPath = currentDir + LINUX_FILE_SEPARATOR + backName; 1411 File backupFile = new File(backupPath); 1412 FileUtils.copyFile(hapFile, backupFile); 1413 utility.setHapPath(backName); 1414 } 1415 copyFileSafely(String source, String dest)1416 private void copyFileSafely(String source, String dest) { 1417 try { 1418 File sourceFile = new File(source); 1419 File destFile = new File(dest); 1420 FileUtils.copyFile(sourceFile, destFile); 1421 } catch (IOException | BundleException e) { 1422 LOG.error("copyFileSafely failed: " + e.getMessage()); 1423 } 1424 } 1425 readerFile(String jsonPath)1426 private static String readerFile(String jsonPath) throws IOException { 1427 byte[] bytes = Files.readAllBytes(Paths.get(jsonPath)); 1428 return new String(bytes, StandardCharsets.UTF_8); 1429 } 1430 writeJsonFile(String dataJson, String targetPath)1431 private static void writeJsonFile(String dataJson, String targetPath) throws BundleException { 1432 try (FileWriter fileWriter = new FileWriter(targetPath)) { 1433 Object object = JSON.parse(dataJson); 1434 String jsonString = new String(); 1435 if (object instanceof JSONArray) { 1436 JSONArray jsonArray = JSONArray.parseArray(dataJson); 1437 jsonString = JSON.toJSONString( 1438 jsonArray, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue); 1439 } else { 1440 JSONObject jsonObject = JSONObject.parseObject(dataJson); 1441 jsonString = JSON.toJSONString( 1442 jsonObject, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue); 1443 } 1444 fileWriter.write(jsonString); 1445 fileWriter.flush(); 1446 } catch (IOException exception) { 1447 LOG.error("writeJsonFile failed, " + exception.getMessage()); 1448 throw new BundleException(exception.getMessage()); 1449 } catch (JSONException e) { 1450 LOG.error("json file is invalid: " + e.getMessage()); 1451 throw new BundleException(e.getMessage()); 1452 } 1453 } 1454 setUtilityParameter(String hapAdditionPath, Utility utility)1455 private static void setUtilityParameter(String hapAdditionPath, Utility utility) throws IOException { 1456 Path basePath = Paths.get(hapAdditionPath); 1457 try (Stream<Path> pathStream = Files.walk(basePath, 1)) { 1458 pathStream.forEach(path -> { 1459 String fileName = path.getFileName().toString(); 1460 String filePath = path.toString(); 1461 switch (fileName) { 1462 case ETS_FILE_NAME: 1463 utility.setEtsPath(filePath); 1464 break; 1465 case DIR_FILE_NAME: 1466 utility.setLibPath(filePath); 1467 break; 1468 case AN_FILE_NAME: 1469 utility.setANPath(filePath); 1470 break; 1471 case AP_FILE_NAME: 1472 utility.setAPPath(filePath); 1473 break; 1474 case RESOURCE_FILE_NAME: 1475 utility.setResourcesPath(filePath); 1476 break; 1477 case JS_FILE_NAME: 1478 utility.setJsPath(filePath); 1479 break; 1480 case ASSETS_FILE_NAME: 1481 utility.setAssetsPath(filePath); 1482 break; 1483 case MAPLE_FILE_NAME: 1484 utility.setSoDir(filePath); 1485 break; 1486 case SHARED_LIBS_FILE_NAME: 1487 utility.setSharedLibsPath(filePath); 1488 break; 1489 case MODULE_JSON: 1490 utility.setJsonPath(filePath); 1491 break; 1492 case RES_INDEX: 1493 utility.setIndexPath(filePath); 1494 break; 1495 case PACKINFO_NAME: 1496 utility.setPackInfoPath(filePath); 1497 break; 1498 } 1499 }); 1500 } 1501 } 1502 compressHapAddition(Utility utility, String hapAdditionPath)1503 private void compressHapAddition(Utility utility, String hapAdditionPath) throws BundleException { 1504 // decompression hap file to hapAddition 1505 1506 unpackHap(utility.getHapPath(), hapAdditionPath); 1507 1508 // generate addition.json file 1509 try { 1510 String data = readerFile(utility.getJsonPath()); 1511 String currentDir = System.getProperty("user.dir"); 1512 String targetParentPath = currentDir + LINUX_FILE_SEPARATOR + TARGET_FILE_PATH; 1513 File targetParent = new File(targetParentPath); 1514 if (!targetParent.exists()) { 1515 if (!targetParent.mkdirs()) { 1516 LOG.error("Compressor::compressHapAddition create target file parent directory failed."); 1517 } 1518 } 1519 String targetPath = targetParentPath + LINUX_FILE_SEPARATOR + ADDITION_JSON; 1520 writeJsonFile(data, targetPath); 1521 } catch (IOException | JSONException | BundleException e) { 1522 String errMsg = "Compressor::compressHapAddition generate addition.json file failed, " + e.getMessage(); 1523 LOG.error(errMsg); 1524 throw new BundleException(errMsg); 1525 } 1526 1527 // package a new hap file 1528 try { 1529 setUtilityParameter(hapAdditionPath, utility); 1530 } catch (IOException e) { 1531 String errMsg = "Compressor::compressHapAddition setUtilityParameter failed."; 1532 LOG.error(errMsg); 1533 throw new BundleException(errMsg); 1534 } 1535 if (utility.getHapPath().endsWith(HAP_SUFFIX)) { 1536 compressHap(utility); 1537 } else if (utility.getHapPath().endsWith(HSP_SUFFIX)) { 1538 compressHsp(utility); 1539 } else { 1540 String errMsg = "Compressor::compressHapAddition compressFile failed."; 1541 LOG.error(errMsg); 1542 throw new BundleException(errMsg); 1543 } 1544 } 1545 1546 /** 1547 * pack hap in app to selectedHaps 1548 * 1549 * @param utility is common data 1550 * @param seletedHaps is seleted haps should be pack into app 1551 * @return the final pack.info string after dispose app 1552 * @throws BundleException FileNotFoundException|IOException. 1553 */ disposeApp(Utility utility, List<String> seletedHaps, String tempDir)1554 private static String disposeApp(Utility utility, List<String> seletedHaps, 1555 String tempDir) throws BundleException { 1556 // dispose app conflict 1557 if (utility.getFormattedAppList().isEmpty()) { 1558 return ""; 1559 } 1560 String finalAppPackInfo = ""; 1561 try { 1562 for (String appPath : utility.getFormattedAppList()) { 1563 // select hap in app 1564 finalAppPackInfo = selectHapInApp(appPath, seletedHaps, tempDir, finalAppPackInfo); 1565 } 1566 } catch (BundleException | IOException e) { 1567 String errMsg = "Compressor:disposeApp disposeApp failed."; 1568 LOG.error(errMsg); 1569 throw new BundleException(errMsg); 1570 } 1571 return finalAppPackInfo; 1572 } 1573 1574 /** 1575 * select hap from app file list 1576 * 1577 * @param appPath is common data 1578 * @param selectedHaps is list of packInfos 1579 * @throws BundleException FileNotFoundException|IOException. 1580 */ selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, String finalAppPackInfo)1581 private static String selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, 1582 String finalAppPackInfo) throws BundleException, IOException { 1583 List<String> selectedHapsInApp = new ArrayList<>(); 1584 // classify hap in app 1585 copyHapAndHspFromApp(appPath, selectedHapsInApp, selectedHaps, tempDir); 1586 // rebuild pack.info 1587 String packInfoStr = FileUtils.getJsonInZips(new File(appPath), PACKINFO_NAME); 1588 if (packInfoStr.isEmpty()) { 1589 String errorMsg = "Compressor:selectHapInApp failed, app has no pack.info."; 1590 LOG.error(errorMsg); 1591 throw new BundleException(errorMsg); 1592 } 1593 if (finalAppPackInfo.isEmpty()) { 1594 finalAppPackInfo = packInfoStr; 1595 return finalAppPackInfo; 1596 } 1597 // read selected module in temp hap 1598 HashMap<String, String> packagePair = new HashMap<>(); 1599 for (String hapName : selectedHapsInApp) { 1600 packagePair.put(hapName, readModlueNameFromHap(tempDir + File.separator + hapName)); 1601 } 1602 return ModuleJsonUtil.mergeTwoPackInfoByPackagePair(finalAppPackInfo, packInfoStr, packagePair); 1603 } 1604 1605 /** 1606 * copy hap from app file 1607 * 1608 * @param appPath is common data 1609 * @param selectedHapsInApp is list of haps and hsps selected in app file 1610 * @param selectedHaps is the list of haps and hsps selected in input 1611 * @throws BundleException FileNotFoundException|IOException. 1612 */ copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, String tempDir)1613 private static void copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, 1614 String tempDir) throws BundleException { 1615 ZipInputStream zipInput = null; 1616 ZipFile zipFile = null; 1617 OutputStream outputStream = null; 1618 InputStream inputStream = null; 1619 ZipEntry zipEntry = null; 1620 try { 1621 zipInput = new ZipInputStream(new FileInputStream(appPath)); 1622 zipFile = new ZipFile(appPath); 1623 while ((zipEntry = zipInput.getNextEntry()) != null) { 1624 File file = null; 1625 if (!zipEntry.getName().endsWith(HAP_SUFFIX) && !zipEntry.getName().endsWith(HSP_SUFFIX)) { 1626 continue; 1627 } 1628 // copy duplicated hap to duplicated dir and get moduleName of duplicated hap 1629 if (selectedHaps.contains(zipEntry.getName())) { 1630 LOG.error("Compressor::copyHapFromApp file duplicated, file is " + zipEntry.getName() + "."); 1631 throw new BundleException("Compressor::copyHapFromApp file duplicated, file is " 1632 + zipEntry.getName() + "."); 1633 } else { 1634 // copy selectedHap to tempDir 1635 file = new File(tempDir + File.separator + zipEntry.getName()); 1636 selectedHaps.add(file.getName()); 1637 selectedHapsInApp.add(file.getName()); 1638 } 1639 outputStream = new FileOutputStream(file); 1640 inputStream = zipFile.getInputStream(zipEntry); 1641 int len; 1642 byte[] buf = new byte[BUFFER_SIZE]; 1643 while ((len = inputStream.read(buf)) != -1) { 1644 outputStream.write(buf, 0, len); 1645 } 1646 outputStream.close(); 1647 inputStream.close(); 1648 } 1649 } catch (IOException e) { 1650 String errMsg = "Compressor:copyHapFromApp app path not found."; 1651 LOG.error(errMsg); 1652 throw new BundleException(errMsg); 1653 } finally { 1654 Utility.closeStream(zipInput); 1655 Utility.closeStream(zipFile); 1656 Utility.closeStream(outputStream); 1657 Utility.closeStream(inputStream); 1658 } 1659 } 1660 1661 /** 1662 * read moduleName in hap 1663 * 1664 * @param hapPath is path of hap file 1665 * @throws BundleException FileNotFoundException|IOException. 1666 */ readModlueNameFromHap(String hapPath)1667 private static String readModlueNameFromHap(String hapPath) throws BundleException { 1668 String moduleName = ""; 1669 File hapFile = new File(hapPath); 1670 if (isModuleHap(hapPath)) { 1671 String jsonString = FileUtils.getJsonInZips(hapFile, MODULE_JSON); 1672 moduleName = ModuleJsonUtil.parseStageModuleName(jsonString); 1673 } else { 1674 String jsonString = FileUtils.getJsonInZips(hapFile, CONFIG_JSON); 1675 moduleName = ModuleJsonUtil.parseFaModuleName(jsonString); 1676 } 1677 return moduleName; 1678 } 1679 1680 /** 1681 * dispose input of hap 1682 * 1683 * @param utility is common data 1684 * @param seletedHaps is the selected haps of all input 1685 * @param tempDir is the path of temp directory 1686 * @param finalPackInfoStr is the pack.info of the final app 1687 * @throws BundleException FileNotFoundException|IOException. 1688 */ disposeHap(Utility utility, List<String> seletedHaps, String tempDir, String finalPackInfoStr)1689 private static String disposeHap(Utility utility, List<String> seletedHaps, String tempDir, 1690 String finalPackInfoStr) throws BundleException, IOException { 1691 // dispose hap conflict 1692 if (utility.getFormattedHapList().isEmpty()) { 1693 return finalPackInfoStr; 1694 } 1695 for (String hapPath : utility.getFormattedHapList()) { 1696 if (seletedHaps.contains(new File(hapPath).getName())) { 1697 LOG.error("Compressor::disposeHap file duplicated, file is " + new File(hapPath).getName() + "."); 1698 throw new BundleException("Compressor::disposeHap file duplicated, file is " 1699 + new File(hapPath).getName() + "."); 1700 } 1701 File hapFile = new File(hapPath); 1702 seletedHaps.add(hapFile.getName()); 1703 // copy hap to tempDir 1704 FileUtils.copyFile(hapFile, new File((tempDir +File.separator + hapFile.getName()))); 1705 String packInfo = FileUtils.getJsonInZips(hapFile, PACKINFO_NAME); 1706 1707 if (packInfo.isEmpty()) { 1708 String errMsg = "Compressor::disposeHap failed, hap has no pack.info."; 1709 LOG.error(errMsg); 1710 throw new BundleException(errMsg); 1711 } 1712 if (finalPackInfoStr.isEmpty()) { 1713 finalPackInfoStr = packInfo; 1714 } else { 1715 finalPackInfoStr = ModuleJsonUtil.mergeTwoPackInfo(finalPackInfoStr, packInfo); 1716 } 1717 } 1718 return finalPackInfoStr; 1719 } 1720 1721 /** 1722 * write string to pack.info 1723 * 1724 * @param filePath pack.info file path 1725 * @param packInfoStr is the string of pack.info 1726 * @throws BundleException FileNotFoundException|IOException. 1727 */ writePackInfo(String filePath, String packInfoStr)1728 private void writePackInfo(String filePath, String packInfoStr) throws BundleException, IOException { 1729 FileWriter fwriter = null; 1730 try { 1731 fwriter = new FileWriter(filePath); 1732 fwriter.write(packInfoStr); 1733 } catch (IOException e) { 1734 String errMsg = "Compressor:writePackInfo failed."; 1735 LOG.error(errMsg); 1736 throw new BundleException(errMsg); 1737 } finally { 1738 if (fwriter != null) { 1739 fwriter.flush(); 1740 fwriter.close(); 1741 } 1742 } 1743 } 1744 copy(InputStream input, OutputStream output)1745 private void copy(InputStream input, OutputStream output) throws IOException { 1746 int bytesRead; 1747 byte[] data = new byte[BUFFER_SIZE]; 1748 while ((bytesRead = input.read(data, 0, BUFFER_SIZE)) != -1) { 1749 output.write(data, 0, bytesRead); 1750 } 1751 } 1752 compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo, int compressLevel)1753 private void compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo, int compressLevel) 1754 throws IOException, BundleException { 1755 ZipFile sourceHapFile = new ZipFile(hapPathItem); 1756 ZipOutputStream append = new ZipOutputStream(new FileOutputStream(outPathString)); 1757 append.setLevel(compressLevel); 1758 try { 1759 Enumeration<? extends ZipEntry> entries = sourceHapFile.entries(); 1760 while (entries.hasMoreElements()) { 1761 ZipEntry zipEntry = new ZipEntry(entries.nextElement()); 1762 if (PACKINFO_NAME.equals(zipEntry.getName())) { 1763 continue; 1764 } 1765 ZipEntry newEntry = zipEntry.getMethod() == 1766 ZipEntry.STORED ? new ZipEntry(zipEntry) : new ZipEntry(zipEntry.getName()); 1767 append.putNextEntry(newEntry); 1768 if (!zipEntry.isDirectory()) { 1769 copy(sourceHapFile.getInputStream(zipEntry), append); 1770 } 1771 append.closeEntry(); 1772 } 1773 File packInfoFile = new File(packInfo); 1774 ZipEntry zipEntry = getStoredZipEntry(packInfoFile, PACKINFO_NAME); 1775 append.putNextEntry(zipEntry); 1776 FileInputStream in = new FileInputStream(packInfoFile); 1777 try { 1778 byte[] buf = new byte[BUFFER_SIZE]; 1779 int len; 1780 while ((len = in.read(buf)) != -1) { 1781 append.write(buf, 0, len); 1782 } 1783 } finally { 1784 in.close(); 1785 } 1786 append.closeEntry(); 1787 } catch (IOException exception) { 1788 LOG.error("Compressor::compressPackinfoIntoHap io exception."); 1789 throw new BundleException("Compressor::compressPackinfoIntoHap io exception."); 1790 } finally { 1791 sourceHapFile.close(); 1792 append.close(); 1793 } 1794 } 1795 1796 /** 1797 * delete file 1798 * 1799 * @param path file path which will be deleted 1800 */ deleteFile(final String path)1801 private static void deleteFile(final String path) { 1802 File file = new File(path); 1803 if (file.exists()) { 1804 if (file.isDirectory()) { 1805 File[] files = file.listFiles(); 1806 for (int i = 0; i < files.length; i++) { 1807 deleteFile(files[i].toString()); 1808 } 1809 } 1810 file.delete(); 1811 } 1812 } 1813 1814 /** 1815 * compress in res mode. 1816 * 1817 * @param utility common data 1818 * @throws BundleException FileNotFoundException|IOException. 1819 */ compressPackResMode(Utility utility)1820 private void compressPackResMode(Utility utility) throws BundleException { 1821 if (!utility.getPackInfoPath().isEmpty()) { 1822 parsePackInfoJsonFile(utility, utility.getPackInfoPath()); 1823 File file = new File(utility.getPackInfoPath()); 1824 infoSpecialProcess(utility, file); 1825 } 1826 if (!utility.getEntryCardPath().isEmpty()) { 1827 getFileList(utility.getEntryCardPath()); 1828 List<String> snapshotNameList = new ArrayList<>(); 1829 for (String fileName : fileNameList) { 1830 if (fileName.endsWith(PNG_SUFFIX) || fileName.endsWith(UPPERCASE_PNG_SUFFIX)) { 1831 String fName = fileName.trim(); 1832 String[] temp = fName.replace("\\", "/").split("/"); 1833 if (temp.length < 4) { 1834 LOG.error("Compressor::compressPackResMode the hap file path is invalid, length: " 1835 + temp.length + "."); 1836 continue; 1837 } 1838 String fileLanguageCountryName = temp[temp.length - 3]; 1839 if (!isThirdLevelDirectoryNameValid(fileLanguageCountryName)) { 1840 LOG.error("Compressor::compressProcess compress failed third level directory name: " 1841 + fileLanguageCountryName + " is invalid, please check it with reference to this example: " 1842 + "zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi."); 1843 throw new BundleException("Compress failed third level directory name Error."); 1844 } 1845 String filePicturingName = temp[temp.length - 1]; 1846 if (!isPicturing(filePicturingName, utility)) { 1847 LOG.error("Compressor::compressProcess Compress pack.res failed, Invalid resource file" + 1848 " name: " + filePicturingName + ", correct format example is formName-2x2.png."); 1849 throw new BundleException("Compress pack.res failed, Invalid resource file name: " 1850 + filePicturingName + ", correct format example is formName-2x2.png."); 1851 } 1852 1853 String fullSnapshotName = temp[temp.length - 4] + "/" + 1854 filePicturingName.substring(0, filePicturingName.lastIndexOf(".")); 1855 snapshotNameList.add(fullSnapshotName); 1856 1857 } else { 1858 LOG.error("Compressor::compressProcess compress failed No image in PNG format is found."); 1859 throw new BundleException("Compress pack.res failed, compress failed No image in" 1860 + " PNG format is found."); 1861 } 1862 } 1863 1864 for (String formName : formNamesList) { 1865 if (!snapshotNameList.contains(formName)) { 1866 LOG.error("Compressor::compressProcess compress failed entryCard " + formName 1867 + " has no related snapshot " + snapshotNameList.toString() + "."); 1868 throw new BundleException("Compress pack.res failed, compress failed entryCard has no related snapshot."); 1869 } 1870 } 1871 pathToFileResMode(utility, utility.getEntryCardPath(), ENTRYCARD_NAME, false); 1872 } 1873 } 1874 1875 /** 1876 * Check whether modelname meets specifications. 1877 * 1878 * @param name modelName 1879 * @return false and true 1880 */ isModelName(String name)1881 private boolean isModelName(String name) { 1882 for (String listName : list) { 1883 if (name.equals(listName)) { 1884 return true; 1885 } 1886 } 1887 return false; 1888 } 1889 isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName)1890 private boolean isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName) { 1891 if (thirdLevelDirectoryName == null || thirdLevelDirectoryName.isEmpty()) { 1892 return false; 1893 } 1894 if (ENTRYCARD_BASE_NAME.equals(thirdLevelDirectoryName)) { 1895 return true; 1896 } 1897 // example: zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi 1898 int firstDelimiterIndex = thirdLevelDirectoryName.indexOf("_"); 1899 if (firstDelimiterIndex < 0) { 1900 return false; 1901 } 1902 String language = thirdLevelDirectoryName.substring(0, firstDelimiterIndex); 1903 int secondDelimiterIndex = thirdLevelDirectoryName.indexOf("_", firstDelimiterIndex + 1); 1904 if (secondDelimiterIndex < 0) { 1905 return false; 1906 } 1907 String script = thirdLevelDirectoryName.substring(firstDelimiterIndex + 1, secondDelimiterIndex); 1908 int thirdDelimiterIndex = thirdLevelDirectoryName.indexOf("-", secondDelimiterIndex + 1); 1909 if (thirdDelimiterIndex < 0) { 1910 return false; 1911 } 1912 String country = thirdLevelDirectoryName.substring(secondDelimiterIndex + 1, thirdDelimiterIndex); 1913 if (!checkLanguage(language) || !checkScript(script) || !checkCountry(country)) { 1914 return false; 1915 } 1916 int forthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", thirdDelimiterIndex + 1); 1917 if (forthDelimiterIndex < 0) { 1918 return false; 1919 } 1920 String orientation = thirdLevelDirectoryName.substring(thirdDelimiterIndex + 1, forthDelimiterIndex); 1921 int fifthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", forthDelimiterIndex + 1); 1922 if (fifthDelimiterIndex < 0) { 1923 return false; 1924 } 1925 String deviceType = thirdLevelDirectoryName.substring(forthDelimiterIndex + 1, fifthDelimiterIndex); 1926 if (!checkOrientation(orientation) || !checkDeviceType(deviceType)) { 1927 return false; 1928 } 1929 int sixthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", fifthDelimiterIndex + 1); 1930 if (sixthDelimiterIndex < 0) { 1931 String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, 1932 thirdLevelDirectoryName.length()); 1933 return checkScreenDensity(screenDensity); 1934 } else { 1935 String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, sixthDelimiterIndex); 1936 if (!checkScreenDensity(screenDensity)) { 1937 return false; 1938 } 1939 } 1940 int seventhDelimiterIndex = thirdLevelDirectoryName.indexOf("-", sixthDelimiterIndex + 1); 1941 if (seventhDelimiterIndex < 0) { 1942 String tmp = thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, thirdLevelDirectoryName.length()); 1943 return checkColorModeOrShape(tmp); 1944 } 1945 if (!checkColorMode(thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, seventhDelimiterIndex))) { 1946 return false; 1947 } 1948 String shape = thirdLevelDirectoryName.substring(seventhDelimiterIndex + 1, thirdLevelDirectoryName.length()); 1949 return checkShape(shape); 1950 } 1951 checkLanguage(String language)1952 private boolean checkLanguage(String language) { 1953 if (!Pattern.compile(REGEX_LANGUAGE).matcher(language).matches()) { 1954 LOG.error("Compressor::compressProcess language " + language + " is not in ISO 639-1 list."); 1955 return false; 1956 } 1957 return true; 1958 } 1959 checkScript(String script)1960 private boolean checkScript(String script) { 1961 if (!Pattern.compile(REGEX_SCRIPT).matcher(script).matches()) { 1962 LOG.error("Compressor::compressProcess script " + script + " is not in ISO 15924 list."); 1963 return false; 1964 } 1965 return true; 1966 } 1967 checkCountry(String country)1968 private boolean checkCountry(String country) { 1969 if (!Pattern.compile(REGEX_COUNTRY).matcher(country).matches()) { 1970 LOG.error("Compressor::compressProcess country " + country + " is not in ISO 3166-1 list."); 1971 return false; 1972 } 1973 return true; 1974 } 1975 checkOrientation(String orientation)1976 private boolean checkOrientation(String orientation) { 1977 if (!Pattern.compile(REGEX_ORIENTATION).matcher(orientation).matches()) { 1978 LOG.error("Compressor::compressProcess orientation " + orientation + 1979 " is not in {vertical, horizontal} list."); 1980 return false; 1981 } 1982 return true; 1983 } 1984 checkDeviceType(String deviceType)1985 private boolean checkDeviceType(String deviceType) { 1986 if (!Pattern.compile(REGEX_DEVICE_TYPE).matcher(deviceType).matches()) { 1987 LOG.error("Compressor::compressProcess deviceType " + deviceType + 1988 " is not in {phone, tablet, car, tv, wearable, liteWearable, 2in1} list."); 1989 return false; 1990 } 1991 return true; 1992 } 1993 checkScreenDensity(String screenDensity)1994 private boolean checkScreenDensity(String screenDensity) { 1995 if (!Pattern.compile(REGEX_SCREEN_DENSITY).matcher(screenDensity).matches()) { 1996 LOG.error("Compressor::compressProcess screenDensity " + screenDensity + 1997 " is not in {sdpi, mdpi, ldpi, xldpi, xxldpi} list."); 1998 return false; 1999 } 2000 return true; 2001 } 2002 checkColorMode(String colorMode)2003 private boolean checkColorMode(String colorMode) { 2004 if (!Pattern.compile(REGEX_COLOR_MODE).matcher(colorMode).matches()) { 2005 LOG.error("Compressor::compressProcess colorMode " + colorMode + 2006 " is not in {light, dark} list."); 2007 return false; 2008 } 2009 return true; 2010 } 2011 checkColorModeOrShape(String tmp)2012 private boolean checkColorModeOrShape(String tmp) { 2013 if (Pattern.compile(REGEX_COLOR_MODE).matcher(tmp).matches() || 2014 Pattern.compile(REGEX_SHAPE).matcher(tmp).matches()) { 2015 return true; 2016 } 2017 LOG.error("Compressor::compressProcess " + tmp + 2018 " is neither in colorMode list {light, dark} nor in shape list {circle}."); 2019 return false; 2020 } 2021 checkShape(String shape)2022 private boolean checkShape(String shape) { 2023 if (Pattern.compile(REGEX_SHAPE).matcher(shape).matches()) { 2024 return true; 2025 } 2026 LOG.error("Compressor::compressProcess shape" + shape + " is not in {circle} list."); 2027 return false; 2028 } 2029 2030 /** 2031 * Check whether picturingName meets specifications. 2032 * 2033 * @param name picturingName 2034 * @param utility common data 2035 * @return false and true 2036 */ isPicturing(String name, Utility utility)2037 private boolean isPicturing(String name, Utility utility) { 2038 boolean isSpecifications = false; 2039 if (name == null || name.isEmpty()) { 2040 return isSpecifications; 2041 } 2042 if (!name.endsWith(PNG_SUFFIX) && !name.endsWith(UPPERCASE_PNG_SUFFIX)) { 2043 LOG.error("isPicturing: the suffix is not .png or .PNG."); 2044 return false; 2045 } 2046 int delimiterIndex = name.lastIndexOf("-"); 2047 if (delimiterIndex < 0) { 2048 LOG.error("isPicturing: the entry card naming format is invalid and should be separated by '-'."); 2049 return false; 2050 } 2051 String formName = name.substring(0, delimiterIndex); 2052 if (!utility.getFormNameList().contains(formName)) { 2053 LOG.error("isPicturing: the name is not same as formName, name: " + formName + " is not in " + 2054 utility.getFormNameList().toString() + "."); 2055 return false; 2056 } 2057 String dimension = name.substring(delimiterIndex + 1, name.lastIndexOf(".")); 2058 if (!supportDimensionsList.contains(dimension)) { 2059 LOG.error("isPicturing: the dimension: " + dimension + " is invalid, is not in the following list: " 2060 + "{1X2, 2X2, 2X4, 4X4, 1X1, 6X4}."); 2061 return false; 2062 } 2063 2064 return true; 2065 } 2066 getFileList(final String filePath)2067 private void getFileList(final String filePath) throws BundleException { 2068 File file = new File(filePath); 2069 if (!file.exists()) { 2070 LOG.error("getFileList: file is not exists."); 2071 return; 2072 } 2073 File[] files = file.listFiles(); 2074 if (files == null) { 2075 LOG.error("getFileList: no file in this file path."); 2076 return; 2077 } 2078 for (File f : files) { 2079 try { 2080 if (f.isFile()) { 2081 if (f.getName().endsWith(".DS_Store")) { 2082 deleteFile(f.getCanonicalPath()); 2083 continue; 2084 } 2085 String snapshotDirectoryName = f.getParentFile().getName(); 2086 if (!ENTRYCARD_SNAPSHOT_NAME.equals(snapshotDirectoryName)) { 2087 LOG.error("The level-4 directory of EntryCard must be named as snapshot" + 2088 ", but current is: " + snapshotDirectoryName + "."); 2089 throw new BundleException("The level-4 directory of EntryCard must be named as snapshot" + 2090 ", but current is: " + snapshotDirectoryName + "."); 2091 } 2092 fileNameList.add(f.getCanonicalPath()); 2093 } else if (f.isDirectory()) { 2094 getFileList(f.getCanonicalPath()); 2095 } else { 2096 LOG.error("It's not file or directory."); 2097 } 2098 } catch (IOException msg) { 2099 LOG.error("IOException error: " + msg.getMessage()); 2100 return; 2101 } 2102 } 2103 } 2104 checkContain2x2EntryCard(final File snapshotDirectory)2105 private void checkContain2x2EntryCard(final File snapshotDirectory) throws IOException, BundleException { 2106 if (!snapshotDirectory.exists()) { 2107 LOG.error("checkContain2x2EntryCard: file is not exist: " + snapshotDirectory.getName() + "."); 2108 throw new BundleException("checkContain2x2EntryCard: file is not exist."); 2109 } 2110 File[] files = snapshotDirectory.listFiles(); 2111 if (files == null) { 2112 LOG.error("checkContain2x2EntryCard: no file in this file path."); 2113 throw new BundleException("checkContain2x2EntryCard: no file in this file path."); 2114 } 2115 2116 for (File entryCardFile : files) { 2117 if (entryCardFile.isFile() && entryCardFile.getName().contains(PIC_2X2)) { 2118 return; 2119 } 2120 } 2121 mIsContain2x2EntryCard = false; 2122 LOG.error("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in " 2123 + snapshotDirectory.getCanonicalPath() + "."); 2124 throw new BundleException("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in " 2125 + snapshotDirectory.getCanonicalPath() + "."); 2126 } 2127 2128 /** 2129 * compress file or directory. 2130 * 2131 * @param utility common data 2132 * @param path create new file by path 2133 * @param baseDir base path for file 2134 * @param isCompression if need compression 2135 * @throws BundleException FileNotFoundException|IOException. 2136 */ pathToFile(Utility utility, String path, String baseDir, boolean isCompression)2137 private void pathToFile(Utility utility, String path, String baseDir, boolean isCompression) 2138 throws BundleException { 2139 if (path.isEmpty()) { 2140 return; 2141 } 2142 if (isCompression && LIBS_DIR_NAME.equals(baseDir)) { 2143 compressNativeLibsParallel(path, baseDir, utility.getCompressLevel()); 2144 return; 2145 } 2146 File fileItem = new File(path); 2147 if (fileItem.isDirectory()) { 2148 File[] files = fileItem.listFiles(); 2149 if (files == null) { 2150 return; 2151 } 2152 for (File file : files) { 2153 if (file.isDirectory()) { 2154 compressDirectory(utility, file, baseDir, isCompression); 2155 } else if (isCompression) { 2156 compressFile(utility, file, baseDir, isCompression); 2157 } else { 2158 compressFile(utility, file, baseDir, isCompression); 2159 } 2160 } 2161 } else { 2162 compressFile(utility, fileItem, baseDir, isCompression); 2163 } 2164 } 2165 compressNativeLibsParallel(String path, String baseDir, int compressLevel)2166 private void compressNativeLibsParallel(String path, String baseDir, int compressLevel) 2167 throws BundleException { 2168 try { 2169 int cores = Runtime.getRuntime().availableProcessors(); 2170 ThreadPoolExecutor executorService = new ThreadPoolExecutor(cores, cores, 60L, 2171 TimeUnit.SECONDS, new LinkedBlockingQueue<>()); 2172 ParallelScatterZipCreator zipCreator = new ParallelScatterZipCreator( 2173 executorService, new DefaultBackingStoreSupplier(null), compressLevel); 2174 File file = new File(path); 2175 if (file.isDirectory()) { 2176 File[] files = file.listFiles(); 2177 if (files == null) { 2178 return; 2179 } 2180 for (File f : files) { 2181 addArchiveEntry(f, baseDir, zipCreator); 2182 } 2183 } else { 2184 addArchiveEntry(file, baseDir, zipCreator); 2185 } 2186 zipCreator.writeTo(zipOut); 2187 } catch (IOException | InterruptedException | ExecutionException e) { 2188 String errMsg = "Compressor::compressNativeLibsParallel exception: " + e.getMessage(); 2189 LOG.error(errMsg); 2190 throw new BundleException(errMsg); 2191 } 2192 } 2193 addArchiveEntry(File file, String baseDir, ParallelScatterZipCreator zipCreator)2194 private void addArchiveEntry(File file, String baseDir, ParallelScatterZipCreator zipCreator) 2195 throws IOException { 2196 if (file.isDirectory()) { 2197 File[] files = file.listFiles(); 2198 if (files == null) { 2199 return; 2200 } 2201 for (File f : files) { 2202 addArchiveEntry(f, baseDir + file.getName() + File.separator, zipCreator); 2203 } 2204 } else { 2205 String entryName = (baseDir + file.getName()).replace(File.separator, LINUX_FILE_SEPARATOR); 2206 ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName); 2207 zipEntry.setMethod(ZipArchiveEntry.DEFLATED); 2208 InputStreamSupplier supplier = () -> { 2209 try { 2210 return Files.newInputStream(file.toPath()); 2211 } catch (IOException e) { 2212 LOG.error("Compressor::compressNativeLibsParallel exception: " + e.getMessage()); 2213 return null; 2214 } 2215 }; 2216 zipCreator.addArchiveEntry(zipEntry, supplier); 2217 } 2218 } 2219 2220 /** 2221 * compress file or directory, res mode 2222 * 2223 * @param utility common data 2224 * @param path create new file by path 2225 * @param baseDir base path for file 2226 * @param isCompression if need compression 2227 * @throws BundleException FileNotFoundException|IOException. 2228 */ pathToFileResMode(Utility utility, String path, String baseDir, boolean isCompression)2229 private void pathToFileResMode(Utility utility, String path, String baseDir, boolean isCompression) 2230 throws BundleException { 2231 if (path.isEmpty()) { 2232 return; 2233 } 2234 File fileItem = new File(path); 2235 if (fileItem.isDirectory()) { 2236 File[] files = fileItem.listFiles(); 2237 if (files == null) { 2238 return; 2239 } 2240 for (File file : files) { 2241 if (!list.contains(file.getName())) { 2242 // moduleName not in pack.info 2243 continue; 2244 } 2245 if (file.isDirectory()) { 2246 compressDirectory(utility, file, baseDir, isCompression); 2247 } else if (isCompression) { 2248 compressFile(utility, file, baseDir, isCompression); 2249 } else { 2250 compressFile(utility, file, baseDir, isCompression); 2251 } 2252 } 2253 } else { 2254 compressFile(utility, fileItem, baseDir, isCompression); 2255 } 2256 } 2257 2258 /** 2259 * compress file directory. 2260 * 2261 * @param utility common data 2262 * @param dir file directory 2263 * @param baseDir current directory name 2264 * @param isCompression if need compression 2265 * @throws BundleException FileNotFoundException|IOException. 2266 */ compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)2267 private void compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression) 2268 throws BundleException { 2269 File[] files = dir.listFiles(); 2270 if (files == null) { 2271 return; 2272 } 2273 for (File file : files) { 2274 if (file.isDirectory()) { 2275 compressDirectory(utility, file, baseDir + dir.getName() + File.separator, isCompression); 2276 } else { 2277 compressFile(utility, file, baseDir + dir.getName() + File.separator, isCompression); 2278 } 2279 } 2280 } 2281 2282 /** 2283 * compress pack.info 2284 * 2285 * @param sourceFile source 2286 * @param zipOutputStream ZipOutputStream 2287 * @param name filename 2288 * @param keepDirStructure Empty File 2289 */ compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean keepDirStructure)2290 private void compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean keepDirStructure) { 2291 FileInputStream in = null; 2292 try { 2293 byte[] buf = new byte[BUFFER_SIZE]; 2294 if (sourceFile.isFile()) { 2295 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name); 2296 zipOutputStream.putNextEntry(zipEntry); 2297 in = new FileInputStream(sourceFile); 2298 int len; 2299 while ((len = in.read(buf)) != -1) { 2300 zipOutputStream.write(buf, 0, len); 2301 } 2302 zipOutputStream.closeEntry(); 2303 } else { 2304 File[] listFiles = sourceFile.listFiles(); 2305 if (listFiles == null || listFiles.length == 0) { 2306 if (keepDirStructure) { 2307 if (!name.isEmpty()) { 2308 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name + "/"); 2309 zipOutputStream.putNextEntry(zipEntry); 2310 } else { 2311 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name); 2312 zipOutputStream.putNextEntry(zipEntry); 2313 } 2314 zipOutputStream.closeEntry(); 2315 } 2316 } else { 2317 for (File file : listFiles) { 2318 if (keepDirStructure) { 2319 isNameEmpty(zipOutputStream, name, keepDirStructure, file); 2320 } else { 2321 compress(file, zipOutputStream, file.getName(), keepDirStructure); 2322 } 2323 } 2324 } 2325 } 2326 } catch (FileNotFoundException ignored) { 2327 LOG.error("Compressor::compressFile file not found exception."); 2328 } catch (IOException exception) { 2329 LOG.error("Compressor::compressFile io exception: " + exception.getMessage()); 2330 } catch (BundleException bundleException) { 2331 LOG.error("Compressor::compressFile bundle exception" + bundleException.getMessage()); 2332 } finally { 2333 Utility.closeStream(in); 2334 } 2335 } 2336 getStoredZipEntry(File sourceFile, String name)2337 private ZipEntry getStoredZipEntry(File sourceFile, String name) throws BundleException { 2338 ZipEntry zipEntry = new ZipEntry(name); 2339 zipEntry.setMethod(ZipEntry.STORED); 2340 zipEntry.setCompressedSize(sourceFile.length()); 2341 zipEntry.setSize(sourceFile.length()); 2342 CRC32 crc = getCrcFromFile(sourceFile); 2343 zipEntry.setCrc(crc.getValue()); 2344 return zipEntry; 2345 } 2346 getCrcFromFile(File file)2347 private CRC32 getCrcFromFile(File file) throws BundleException { 2348 FileInputStream fileInputStream = null; 2349 CRC32 crc = new CRC32(); 2350 try { 2351 fileInputStream = new FileInputStream(file); 2352 byte[] buffer = new byte[BUFFER_SIZE]; 2353 2354 int count = fileInputStream.read(buffer); 2355 while (count > 0) { 2356 crc.update(buffer, 0, count); 2357 count = fileInputStream.read(buffer); 2358 } 2359 } catch (FileNotFoundException ignored) { 2360 LOG.error("Uncompressor::getCrcFromFile file not found exception."); 2361 throw new BundleException("Get Crc from file failed."); 2362 } catch (IOException exception) { 2363 LOG.error("Uncompressor::getCrcFromFile io exception: " + exception.getMessage()); 2364 throw new BundleException("Get Crc from file failed."); 2365 } finally { 2366 Utility.closeStream(fileInputStream); 2367 } 2368 return crc; 2369 } 2370 2371 /** 2372 * isNameEmpty 2373 * 2374 * @param zipOutputStream ZipOutputStream 2375 * @param name filename 2376 * @param keepDirStructure KeepDirStructure 2377 * @param file file 2378 */ isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean keepDirStructure, File file)2379 private void isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean keepDirStructure, File file) { 2380 if (!name.isEmpty()) { 2381 compress(file, zipOutputStream, name + "/" + file.getName(), keepDirStructure); 2382 } else { 2383 compress(file, zipOutputStream, file.getName(), keepDirStructure); 2384 } 2385 } 2386 2387 /** 2388 * compress process. 2389 * 2390 * @param utility common data 2391 * @param srcFile source file to zip 2392 * @param baseDir current directory name of file 2393 * @param isCompression if need compression 2394 * @throws BundleException FileNotFoundException|IOException. 2395 */ compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)2396 private void compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression) 2397 throws BundleException { 2398 BufferedInputStream bufferedInputStream = null; 2399 FileInputStream fileInputStream = null; 2400 try { 2401 String entryName = (baseDir + srcFile.getName()).replace(File.separator, LINUX_FILE_SEPARATOR); 2402 ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName); 2403 isEntryOpen = true; 2404 if (!entryName.contains(RAW_FILE_PATH) 2405 && !entryName.contains(RES_FILE_PATH) 2406 && srcFile.getName().toLowerCase(Locale.ENGLISH).endsWith(JSON_SUFFIX) 2407 && !entryName.equals(Constants.FILE_ENCRYPT_JSON)) { 2408 zipEntry.setMethod(ZipEntry.STORED); 2409 if (jsonSpecialProcess(utility, srcFile, zipEntry)) { 2410 return; 2411 } 2412 } 2413 2414 if (isCompression) { 2415 zipEntry.setMethod(ZipEntry.DEFLATED); 2416 } else { 2417 zipEntry.setMethod(ZipEntry.STORED); 2418 2419 // update size 2420 zipEntry.setCompressedSize(srcFile.length()); 2421 zipEntry.setSize(srcFile.length()); 2422 2423 // update crc 2424 CRC32 crc = getCrcFromFile(utility, srcFile); 2425 zipEntry.setCrc(crc.getValue()); 2426 } 2427 2428 zipOut.putArchiveEntry(zipEntry); 2429 byte[] data = new byte[BUFFER_SIZE]; 2430 fileInputStream = new FileInputStream(srcFile); 2431 bufferedInputStream = new BufferedInputStream(fileInputStream); 2432 2433 int count = bufferedInputStream.read(data); 2434 while (count > 0) { 2435 zipOut.write(data, 0, count); 2436 count = bufferedInputStream.read(data); 2437 } 2438 } catch (FileNotFoundException ignored) { 2439 throw new BundleException("CoompressFile failed."); 2440 } catch (IOException exception) { 2441 LOG.error("Compressor::compressFile io exception: " + exception.getMessage()); 2442 throw new BundleException("CoompressFile failed."); 2443 } finally { 2444 Utility.closeStream(bufferedInputStream); 2445 Utility.closeStream(fileInputStream); 2446 } 2447 } 2448 2449 /** 2450 * check hap type for pack app. 2451 * 2452 * @param hapPath source file to zip 2453 * @return true is for is stage type and false is for FA type 2454 * @throws BundleException FileNotFoundException|IOException. 2455 */ isModuleHap(String hapPath)2456 public static boolean isModuleHap(String hapPath) throws BundleException { 2457 if (!hapPath.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)) { 2458 return true; 2459 } 2460 2461 FileInputStream zipInput = null; 2462 ZipInputStream zin = null; 2463 ZipEntry entry = null; 2464 try { 2465 zipInput = new FileInputStream(hapPath); 2466 zin = new ZipInputStream(zipInput); 2467 while ((entry = zin.getNextEntry()) != null) { 2468 if (MODULE_JSON.equals(entry.getName().toLowerCase())) { 2469 return true; 2470 } 2471 } 2472 } catch (IOException exception) { 2473 LOG.error("Compressor::isModuleHap io exception: " + exception.getMessage()); 2474 throw new BundleException("Compressor::isModuleHap failed."); 2475 } finally { 2476 Utility.closeStream(zipInput); 2477 Utility.closeStream(zin); 2478 } 2479 return false; 2480 } 2481 2482 /** 2483 * get CRC32 from file. 2484 * 2485 * @param utility common data 2486 * @param file source file 2487 * @return CRC32 2488 * @throws BundleException FileNotFoundException|IOException. 2489 */ getCrcFromFile(Utility utility, File file)2490 private CRC32 getCrcFromFile(Utility utility, File file) throws BundleException { 2491 FileInputStream fileInputStream = null; 2492 CRC32 crc = new CRC32(); 2493 try { 2494 fileInputStream = new FileInputStream(file); 2495 byte[] buffer = new byte[BUFFER_SIZE]; 2496 2497 int count = fileInputStream.read(buffer); 2498 while (count > 0) { 2499 crc.update(buffer, 0, count); 2500 count = fileInputStream.read(buffer); 2501 } 2502 } catch (FileNotFoundException ignored) { 2503 LOG.error("Compressor::getCrcFromFile FileNotFoundException : " + ignored.getMessage()); 2504 throw new BundleException("Get Crc from file failed: " + file.getName()); 2505 } catch (IOException exception) { 2506 LOG.error("Compressor::getCrcFromFile io exception: " + exception.getMessage()); 2507 throw new BundleException("Get Crc from file failed."); 2508 } finally { 2509 Utility.closeStream(fileInputStream); 2510 } 2511 return crc; 2512 } 2513 parsePackInfoJsonFile(Utility utility, String jsonPath)2514 private void parsePackInfoJsonFile(Utility utility, String jsonPath) 2515 throws BundleException { 2516 try { 2517 String jsonString = FileUtils.getFileContent(jsonPath).get(); 2518 List<String> nameList = new ArrayList<>(); 2519 ModuleJsonUtil.parsePackInfoFormsName(jsonString, nameList, formNamesList); 2520 for (String formName : nameList) { 2521 if (formName.isEmpty()) { 2522 LOG.error("Compressor::infoSpecialJsonFileProcess form name is empty."); 2523 continue; 2524 } 2525 2526 utility.addFormNameList(formName); 2527 } 2528 } catch (JSONException e) { 2529 LOG.error("Compressor::infoSpecialJsonFileProcess json Path: " + jsonPath + 2530 "json exception: " + e.getMessage()); 2531 } 2532 } 2533 infoSpecialProcess(Utility utility, File srcFile)2534 private void infoSpecialProcess(Utility utility, File srcFile) 2535 throws BundleException { 2536 FileInputStream fileInputStream = null; 2537 BufferedReader bufferedReader = null; 2538 InputStreamReader inputStreamReader = null; 2539 2540 try { 2541 fileInputStream = new FileInputStream(srcFile); 2542 inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 2543 bufferedReader = new BufferedReader(inputStreamReader); 2544 bufferedReader.mark((int) srcFile.length() + 1); 2545 // parse moduleName from pack.info 2546 parsePackModuleName(bufferedReader, utility); 2547 bufferedReader.reset(); 2548 parseDeviceType(bufferedReader, utility); 2549 bufferedReader.reset(); 2550 2551 Pattern pattern = Pattern.compile(System.lineSeparator()); 2552 String str = bufferedReader.readLine(); 2553 StringBuilder builder = new StringBuilder(); 2554 while (str != null) { 2555 Matcher matcher = pattern.matcher(str.trim()); 2556 String dest = matcher.replaceAll(""); 2557 builder.append(dest); 2558 str = bufferedReader.readLine(); 2559 } 2560 } catch (IOException exception) { 2561 LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage()); 2562 throw new BundleException("Json special process failed."); 2563 } finally { 2564 Utility.closeStream(bufferedReader); 2565 Utility.closeStream(inputStreamReader); 2566 Utility.closeStream(fileInputStream); 2567 } 2568 } 2569 2570 /** 2571 * trim and remove "\r\n" in *.json file. 2572 * 2573 * @param utility common data 2574 * @param srcFile file input 2575 * @param entry zip file entry 2576 * @return true if process success 2577 */ jsonSpecialProcess(Utility utility, File srcFile, ZipArchiveEntry entry)2578 private boolean jsonSpecialProcess(Utility utility, File srcFile, ZipArchiveEntry entry) { 2579 FileInputStream fileInputStream = null; 2580 BufferedReader bufferedReader = null; 2581 InputStreamReader inputStreamReader = null; 2582 2583 try { 2584 fileInputStream = new FileInputStream(srcFile); 2585 inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 2586 bufferedReader = new BufferedReader(inputStreamReader); 2587 bufferedReader.mark((int) srcFile.length() + 1); 2588 bufferedReader.reset(); 2589 Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath()); 2590 String jsonString = optional.get(); 2591 String jsonName = new File(utility.getJsonPath()).getName().toLowerCase(Locale.ENGLISH); 2592 if (CONFIG_JSON.equals(jsonName)) { 2593 parseCompressNativeLibs(bufferedReader, utility); 2594 utility.setModuleName(ModuleJsonUtil.parseFaModuleName(jsonString)); 2595 } else if (MODULE_JSON.equals(jsonName)) { 2596 utility.setIsCompressNativeLibs(ModuleJsonUtil.stageIsCompressNativeLibs(jsonString)); 2597 utility.setModuleName(ModuleJsonUtil.parseStageModuleName(jsonString)); 2598 } else if (PATCH_JSON.equals(jsonName)) { 2599 utility.setModuleName(ModuleJsonUtil.parsePatchModuleName(jsonString)); 2600 } 2601 bufferedReader.reset(); 2602 parseDeviceType(bufferedReader, utility); 2603 bufferedReader.reset(); 2604 2605 Pattern pattern = Pattern.compile(System.lineSeparator()); 2606 String str = bufferedReader.readLine(); 2607 StringBuilder builder = new StringBuilder(); 2608 while (str != null) { 2609 Matcher matcher = pattern.matcher(str.trim()); 2610 String dest = matcher.replaceAll(""); 2611 builder.append(dest); 2612 str = bufferedReader.readLine(); 2613 } 2614 Object jsonObject = JSON.parse(builder.toString()); 2615 byte[] trimJson = JSON.toJSONBytes(jsonObject, 2616 SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat); 2617 2618 // update crc 2619 CRC32 crc = new CRC32(); 2620 crc.update(trimJson); 2621 entry.setCrc(crc.getValue()); 2622 2623 // update size 2624 entry.setSize(trimJson.length); 2625 entry.setCompressedSize(trimJson.length); 2626 2627 // compress data 2628 zipOut.putArchiveEntry(entry); 2629 zipOut.write(trimJson); 2630 } catch (Exception exception) { 2631 LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage()); 2632 LOG.warning("Json format err: " + srcFile.getAbsolutePath()); 2633 return false; 2634 } finally { 2635 Utility.closeStream(bufferedReader); 2636 Utility.closeStream(inputStreamReader); 2637 Utility.closeStream(fileInputStream); 2638 } 2639 return true; 2640 } 2641 2642 /** 2643 * Parse module name from pack.info 2644 * 2645 * @param bufferedReader pack.info buffered Reader 2646 * @param utility common data 2647 * @throws BundleException IOException 2648 */ parsePackModuleName(BufferedReader bufferedReader, Utility utility)2649 private void parsePackModuleName(BufferedReader bufferedReader, Utility utility) throws BundleException { 2650 String lineStr = null; 2651 try { 2652 while ((lineStr = bufferedReader.readLine()) != null) { 2653 if (lineStr.contains(DISTRO)) { 2654 continue; 2655 } 2656 if (lineStr.contains(JSON_END)) { 2657 continue; 2658 } 2659 if (lineStr.contains(MODULE_NAME_NEW) || lineStr.contains(MODULE_NAME)) { 2660 getModuleNameFromString(lineStr, utility); 2661 } 2662 } 2663 } catch (IOException exception) { 2664 LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage()); 2665 throw new BundleException("Parse module name failed."); 2666 } 2667 } 2668 2669 /** 2670 * Parse Forms name from pack.info 2671 * 2672 * @param bufferedReader pack.info buffered Reader 2673 * @param utility common data 2674 * @throws BundleException IOException 2675 */ parsePackFormName(BufferedReader bufferedReader, Utility utility)2676 private void parsePackFormName(BufferedReader bufferedReader, Utility utility) throws BundleException { 2677 String lineStr = null; 2678 try { 2679 while ((lineStr = bufferedReader.readLine()) != null) { 2680 if (lineStr.contains("abilities")) { 2681 continue; 2682 } 2683 if (lineStr.contains(FORMS)) { 2684 continue; 2685 } 2686 if (lineStr.contains(JSON_END)) { 2687 continue; 2688 } 2689 if (lineStr.contains(NAME)) { 2690 getNameFromString(lineStr, utility); 2691 } 2692 } 2693 } catch (IOException exception) { 2694 LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage()); 2695 throw new BundleException("Parse module name failed."); 2696 } 2697 } 2698 2699 2700 /** 2701 * Get name from line string 2702 * 2703 * @param lineStr line string 2704 * @param utility common data 2705 * @throws BundleException StringIndexOutOfBoundsException 2706 */ getNameFromString(String lineStr, Utility utility)2707 private void getNameFromString(String lineStr, Utility utility) throws BundleException { 2708 try { 2709 int endIndex = lineStr.lastIndexOf(SEMICOLON); 2710 if (endIndex <= 0) { 2711 LOG.error("Compressor::getModuleNameFromString field the json is not standard."); 2712 throw new BundleException("Parse module name failed, module-name is invalid."); 2713 } 2714 int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1; 2715 String formName = lineStr.substring(startIndex, endIndex); 2716 if (formName == null || formName.isEmpty()) { 2717 LOG.error("Compressor::getModuleNameFromString field module-name is empty."); 2718 throw new BundleException("Parse module name failed, module-name is empty."); 2719 } 2720 String[] nameList = formName.split("\\."); 2721 if (nameList.length <= 1) { 2722 formNamesList.add(formName); 2723 utility.addFormNameList(formName); 2724 } 2725 } catch (StringIndexOutOfBoundsException exception) { 2726 LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage()); 2727 throw new BundleException("Parse module name failed, module-name is invalid."); 2728 } 2729 } 2730 2731 /** 2732 * Get module name from line string 2733 * 2734 * @param lineStr line string 2735 * @param utility common data 2736 * @throws BundleException StringIndexOutOfBoundsException 2737 */ getModuleNameFromString(String lineStr, Utility utility)2738 private void getModuleNameFromString(String lineStr, Utility utility) throws BundleException { 2739 try { 2740 int endIndex = lineStr.lastIndexOf(SEMICOLON); 2741 if (endIndex <= 0) { 2742 LOG.error("Compressor::getModuleNameFromString field the json is not standard."); 2743 throw new BundleException("Parse module name failed, module-name is invalid."); 2744 } 2745 int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1; 2746 String moduleName = lineStr.substring(startIndex, endIndex); 2747 list.add(moduleName); 2748 if (moduleName == null || moduleName.isEmpty()) { 2749 LOG.error("Compressor::getModuleNameFromString field module-name is empty."); 2750 throw new BundleException("Parse module name failed, module-name is empty."); 2751 } 2752 utility.setModuleName(moduleName); 2753 } catch (StringIndexOutOfBoundsException exception) { 2754 LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage()); 2755 throw new BundleException("Parse module name failed, module-name is invalid."); 2756 } 2757 } 2758 parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility)2759 private void parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility) throws BundleException { 2760 String lineStr = null; 2761 try { 2762 while ((lineStr = bufferedReader.readLine()) != null) { 2763 if (lineStr.contains(COMPRESS_NATIVE_LIBS)) { 2764 if (lineStr.contains(Utility.TRUE_STRING)) { 2765 utility.setIsCompressNativeLibs(true); 2766 break; 2767 } 2768 } 2769 } 2770 } catch (IOException exception) { 2771 LOG.error("Compressor::parseCompressNativeLibs io exception: " + exception.getMessage()); 2772 throw new BundleException("Parse compress native libs failed."); 2773 } 2774 } 2775 2776 /** 2777 * ZipOutputStream flush, closeEntry and finish. 2778 */ closeZipOutputStream()2779 private void closeZipOutputStream() { 2780 try { 2781 if (zipOut != null) { 2782 zipOut.flush(); 2783 } 2784 } catch (IOException exception) { 2785 LOG.error("Compressor::closeZipOutputStream flush exception " + exception.getMessage()); 2786 } 2787 try { 2788 if (zipOut != null && isEntryOpen) { 2789 zipOut.closeArchiveEntry(); 2790 } 2791 } catch (IOException exception) { 2792 LOG.error("Compressor::closeZipOutputStream close entry io exception " + exception.getMessage()); 2793 } 2794 try { 2795 if (zipOut != null) { 2796 zipOut.finish(); 2797 } 2798 } catch (IOException exception) { 2799 LOG.error("Compressor::closeZipOutputStream finish exception " + exception.getMessage()); 2800 } 2801 } 2802 2803 /** 2804 * Parse device type from config.json 2805 * 2806 * @param bufferedReader config.json buffered Reader 2807 * @param utility common data 2808 * @throws BundleException IOException 2809 */ parseDeviceType(BufferedReader bufferedReader, Utility utility)2810 private void parseDeviceType(BufferedReader bufferedReader, Utility utility) throws BundleException { 2811 String lineStr = null; 2812 boolean isDeviceTypeStart = false; 2813 try { 2814 while ((lineStr = bufferedReader.readLine()) != null) { 2815 if (!isDeviceTypeStart) { 2816 if (lineStr.contains(DEVICE_TYPE)) { 2817 isDeviceTypeStart = true; 2818 } 2819 continue; 2820 } 2821 if (lineStr.contains(JSON_END)) { 2822 break; 2823 } 2824 utility.setDeviceType(lineStr); 2825 break; 2826 } 2827 } catch (IOException exception) { 2828 LOG.error("Compressor::parseDeviceType io exception: " + exception.getMessage()); 2829 throw new BundleException("Parse device type failed."); 2830 } 2831 } 2832 2833 /** 2834 * check hap and hsp is valid in haps when pack app, check type has bundleName, 2835 * vendor, version, apiVersion moduleName, packageName. 2836 * 2837 * @param fileLists is the list of hapPath. 2838 * @return true is for successful and false is for failed 2839 * @throws BundleException FileNotFoundException|IOException. 2840 */ checkHapIsValid(List<String> fileLists, boolean isSharedApp)2841 private boolean checkHapIsValid(List<String> fileLists, boolean isSharedApp) throws BundleException { 2842 List<HapVerifyInfo> hapVerifyInfos = new ArrayList<>(); 2843 for (String hapPath : fileLists) { 2844 if (hapPath.isEmpty()) { 2845 LOG.error("Compressor::checkHapIsValid input wrong hap file."); 2846 throw new BundleException("Compressor::checkHapIsValid input wrong hap file."); 2847 } 2848 File srcFile = new File(hapPath); 2849 String fileStr = srcFile.getName(); 2850 if (fileStr.isEmpty()) { 2851 LOG.error("Compressor::checkHapIsValid get file name failed."); 2852 throw new BundleException("Compressor::checkHapIsValid get file name failed."); 2853 } 2854 if (!fileStr.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX) 2855 && !fileStr.toLowerCase(Locale.ENGLISH).endsWith(HSP_SUFFIX)) { 2856 LOG.error("Compressor::checkHapIsValid input wrong hap file."); 2857 throw new BundleException("Compressor::checkHapIsValid input wrong hap file."); 2858 } 2859 if (isModuleHap(hapPath)) { 2860 hapVerifyInfos.add(parseStageHapVerifyInfo(hapPath)); 2861 } else { 2862 hapVerifyInfos.add(parseFAHapVerifyInfo(hapPath)); 2863 } 2864 2865 if (!hapVerifyInfos.isEmpty()) { 2866 hapVerifyInfoMap.put(getFileNameByPath(hapPath), hapVerifyInfos.get(hapVerifyInfos.size() - 1)); 2867 } 2868 } 2869 2870 if (isSharedApp) { 2871 boolean res = checkSharedAppIsValid(hapVerifyInfos); 2872 if (!res) { 2873 return false; 2874 } 2875 if (!isOverlay) { 2876 return true; 2877 } 2878 } else { 2879 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 2880 String bundleType = hapVerifyInfo.getBundleType(); 2881 if (TYPE_SHARED.equals(bundleType)) { 2882 LOG.error("Compressor::checkHapIsValid only one item can be entered in the -hsp-path" + 2883 " when bundleType is shared."); 2884 return false; 2885 } 2886 } 2887 } 2888 setAtomicServiceFileSizeLimit(hapVerifyInfos); 2889 if (!HapVerify.checkHapIsValid(hapVerifyInfos)) { 2890 return false; 2891 } 2892 return true; 2893 } 2894 2895 /** 2896 * parse stage file to hap verify info from hap path. 2897 * 2898 * @param filePath is the hap path 2899 * @return hapVerifyInfo 2900 */ parseStageHapVerifyInfo(String filePath)2901 public static HapVerifyInfo parseStageHapVerifyInfo(String filePath) throws BundleException { 2902 HapVerifyInfo hapVerifyInfo = readStageHapVerifyInfo(filePath); 2903 hapVerifyInfo.setStageModule(true); 2904 ModuleJsonUtil.parseStageHapVerifyInfo(hapVerifyInfo); 2905 hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath)); 2906 return hapVerifyInfo; 2907 } 2908 2909 /** 2910 * set file size limit for each HapVerifyInfo. 2911 * 2912 * @param hapVerifyInfos Indicates hapVerifyInfo list. 2913 */ setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos)2914 public static void setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos) { 2915 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 2916 if (!hapVerifyInfo.getBundleType().equals(ATOMIC_SERVICE)) { 2917 continue; 2918 } 2919 hapVerifyInfo.setEntrySizeLimit(getEntryModuleSizeLimit()); 2920 hapVerifyInfo.setNotEntrySizeLimit(getNotEntryModuleSizeLimit()); 2921 hapVerifyInfo.setSumSizeLimit(getSumModuleSizeLimit()); 2922 } 2923 } 2924 2925 /** 2926 * parse fa file to hap verify info from hap path. 2927 * 2928 * @param filePath is the hap path 2929 * @return hapVerifyInfo 2930 */ parseFAHapVerifyInfo(String filePath)2931 public static HapVerifyInfo parseFAHapVerifyInfo(String filePath) throws BundleException { 2932 HapVerifyInfo hapVerifyInfo = readFAHapVerifyInfo(filePath); 2933 hapVerifyInfo.setStageModule(false); 2934 hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath)); 2935 ModuleJsonUtil.parseFAHapVerifyInfo(hapVerifyInfo); 2936 return hapVerifyInfo; 2937 } 2938 2939 /** 2940 * read stage hap verify info from hap file. 2941 * 2942 * @param srcPath source file to zip 2943 * @return HapVerifyInfo of parse result 2944 * @throws BundleException FileNotFoundException|IOException. 2945 */ readStageHapVerifyInfo(String srcPath)2946 public static HapVerifyInfo readStageHapVerifyInfo(String srcPath) throws BundleException { 2947 HapVerifyInfo hapVerifyInfo = new HapVerifyInfo(); 2948 ZipFile zipFile = null; 2949 try { 2950 File srcFile = new File(srcPath); 2951 zipFile = new ZipFile(srcFile); 2952 hapVerifyInfo.setResourceMap(FileUtils.getProfileJson(zipFile)); 2953 hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(MODULE_JSON, zipFile)); 2954 } catch (IOException e) { 2955 LOG.error("FileUtil::parseStageHapVerifyInfo file not available."); 2956 throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available."); 2957 } finally { 2958 Utility.closeStream(zipFile); 2959 } 2960 return hapVerifyInfo; 2961 } 2962 2963 /** 2964 * read fa hap verify info from hap file. 2965 * 2966 * @param srcPath source file to zip 2967 * @return HapVerifyInfo of parse result 2968 * @throws BundleException FileNotFoundException|IOException. 2969 */ readFAHapVerifyInfo(String srcPath)2970 public static HapVerifyInfo readFAHapVerifyInfo(String srcPath) throws BundleException { 2971 HapVerifyInfo hapVerifyInfo = new HapVerifyInfo(); 2972 ZipFile zipFile = null; 2973 try { 2974 File srcFile = new File(srcPath); 2975 zipFile = new ZipFile(srcFile); 2976 hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(CONFIG_JSON, zipFile)); 2977 } catch (IOException e) { 2978 LOG.error("FileUtil::parseStageHapVerifyInfo file not available."); 2979 throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available."); 2980 } finally { 2981 Utility.closeStream(zipFile); 2982 } 2983 return hapVerifyInfo; 2984 } 2985 2986 /** 2987 * compress in hqf mode. 2988 * 2989 * @param utility common data 2990 * @throws BundleException FileNotFoundException|IOException. 2991 */ compressHQFMode(Utility utility)2992 private void compressHQFMode(Utility utility) throws BundleException { 2993 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 2994 2995 if (!utility.getEtsPath().isEmpty()) { 2996 pathToFile(utility, utility.getEtsPath(), ETS_PATH, false); 2997 } 2998 if (!utility.getLibPath().isEmpty()) { 2999 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, false); 3000 } 3001 if (!utility.getResourcesPath().isEmpty()) { 3002 pathToFile(utility, utility.getResourcesPath(), RESOURCES_DIR_NAME, false); 3003 } 3004 } 3005 3006 /** 3007 * compress in appqf mode. 3008 * 3009 * @param utility common data 3010 * @throws BundleException FileNotFoundException|IOException. 3011 */ compressAPPQFMode(Utility utility)3012 private void compressAPPQFMode(Utility utility) throws BundleException { 3013 List<String> fileList = utility.getFormatedHQFList(); 3014 if (!checkHQFIsValid(fileList)) { 3015 LOG.error("checkHQFIsValid failed when pack appqf file."); 3016 throw new BundleException("checkHQFIsValid failed when pack appqf file."); 3017 } 3018 for (String hapPath : fileList) { 3019 pathToFile(utility, hapPath, NULL_DIR_NAME, false); 3020 } 3021 } 3022 3023 /** 3024 * check input hqf is valid. 3025 * 3026 * @param fileList is input path of hqf files 3027 * @throws BundleException FileNotFoundException|IOException. 3028 */ checkHQFIsValid(List<String> fileList)3029 private boolean checkHQFIsValid(List<String> fileList) throws BundleException { 3030 List<HQFInfo> hqfVerifyInfos = new ArrayList<>(); 3031 for (String file : fileList) { 3032 hqfVerifyInfos.add(ModuleJsonUtil.parseHQFInfo(file)); 3033 } 3034 if (!HQFVerify.checkHQFIsValid(hqfVerifyInfos)) { 3035 LOG.error("input hqf is invalid."); 3036 return false; 3037 } 3038 return true; 3039 } 3040 compressHSPMode(Utility utility)3041 private void compressHSPMode(Utility utility) throws BundleException { 3042 pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false); 3043 3044 pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false); 3045 3046 if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 3047 String assetsPath = NULL_DIR_NAME; 3048 pathToFile(utility, utility.getIndexPath(), assetsPath, false); 3049 } 3050 3051 if (!utility.getLibPath().isEmpty()) { 3052 pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs()); 3053 } 3054 3055 if (!utility.getANPath().isEmpty()) { 3056 pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false); 3057 } 3058 3059 if (!utility.getAPPath().isEmpty()) { 3060 pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false); 3061 } 3062 3063 if (!utility.getFilePath().isEmpty()) { 3064 pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false); 3065 } 3066 3067 if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) { 3068 String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR 3069 + RESOURCES_DIR_NAME; 3070 if (DEVICE_TYPE_FITNESSWATCH.equals( 3071 utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim()) || 3072 DEVICE_TYPE_FITNESSWATCH_NEW.equals( 3073 utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim())) { 3074 resPath = RES_DIR_NAME; 3075 } 3076 pathToFile(utility, utility.getResPath(), resPath, false); 3077 } 3078 3079 if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 3080 String resourcesPath = RESOURCES_DIR_NAME; 3081 pathToFile(utility, utility.getResourcesPath(), resourcesPath, false); 3082 } 3083 if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 3084 String jsPath = JS_PATH; 3085 pathToFile(utility, utility.getJsPath(), jsPath, false); 3086 } 3087 3088 if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) { 3089 String etsPath = ETS_PATH; 3090 pathToFile(utility, utility.getEtsPath(), etsPath, false); 3091 } 3092 3093 if (!utility.getRpcidPath().isEmpty()) { 3094 String rpcidPath = NULL_DIR_NAME; 3095 pathToFile(utility, utility.getRpcidPath(), rpcidPath, false); 3096 } 3097 3098 if (!utility.getAssetsPath().isEmpty()) { 3099 pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false); 3100 } 3101 3102 if (!utility.getBinPath().isEmpty()) { 3103 pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false); 3104 } 3105 3106 if (!utility.getPackInfoPath().isEmpty()) { 3107 pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false); 3108 } 3109 3110 // pack --dir-list 3111 if (!utility.getFormatedDirList().isEmpty()) { 3112 for (int i = 0; i < utility.getFormatedDirList().size(); ++i) { 3113 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator; 3114 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false); 3115 } 3116 } 3117 if (!utility.getPkgContextPath().isEmpty()) { 3118 pathToFile(utility, utility.getPkgContextPath(), NULL_DIR_NAME, false); 3119 } 3120 3121 compressHapModeMultiple(utility); 3122 } 3123 checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos)3124 private static boolean checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException { 3125 if (hapVerifyInfos.isEmpty()) { 3126 LOG.error("no module included"); 3127 return false; 3128 } 3129 if (hapVerifyInfos.size() > SHARED_APP_HSP_LIMIT) { 3130 LOG.error("Shared app only can contain one module"); 3131 return false; 3132 } 3133 for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) { 3134 if (!hapVerifyInfo.getTargetBundleName().isEmpty()) { 3135 isOverlay = true; 3136 return true; 3137 } 3138 } 3139 return HapVerify.checkSharedApppIsValid(hapVerifyInfos); 3140 } 3141 versionNormalize(Utility utility)3142 private void versionNormalize(Utility utility) { 3143 List<VersionNormalizeUtil> utils = new ArrayList<>(); 3144 Path tempDir = null; 3145 for (String hapPath : utility.getFormattedHapList()) { 3146 try { 3147 tempDir = Files.createTempDirectory(Paths.get(utility.getOutPath()), "temp"); 3148 3149 unpackHap(hapPath, tempDir.toAbsolutePath().toString()); 3150 VersionNormalizeUtil util = new VersionNormalizeUtil(); 3151 File moduleFile = new File( 3152 tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + MODULE_JSON); 3153 File configFile = new File( 3154 tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + CONFIG_JSON); 3155 File packInfoFile = new File( 3156 tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + PACKINFO_NAME); 3157 3158 if (moduleFile.exists() && configFile.exists()) { 3159 LOG.error("versionNormalize failed, invalid hap structure."); 3160 throw new BundleException("versionNormalize failed, invalid hap structure."); 3161 } 3162 if (moduleFile.exists()) { 3163 String moduleJsonPath = tempDir.resolve(MODULE_JSON).toString(); 3164 util = parseAndModifyModuleJson(moduleJsonPath, utility); 3165 } else if (configFile.exists()) { 3166 String configJsonPath = tempDir.resolve(CONFIG_JSON).toString(); 3167 util = parseAndModifyConfigJson(configJsonPath, utility); 3168 } else { 3169 LOG.error("versionNormalize failed, invalid hap structure."); 3170 throw new BundleException("versionNormalize failed, invalid hap structure."); 3171 } 3172 if (packInfoFile.exists()) { 3173 String packInfoPath = tempDir.resolve(PACKINFO_NAME).toString(); 3174 parseAndModifyPackInfo(packInfoPath, utility); 3175 } 3176 3177 verifyModuleVersion(util, utility); 3178 utils.add(util); 3179 3180 String modifiedHapPath = Paths.get(utility.getOutPath()) + 3181 LINUX_FILE_SEPARATOR + Paths.get(hapPath).getFileName().toString(); 3182 compressDirToHap(tempDir, modifiedHapPath); 3183 } catch (IOException | BundleException e) { 3184 LOG.error("versionNormalize failed " + e.getMessage()); 3185 } finally { 3186 if (tempDir != null) { 3187 deleteDirectory(tempDir.toFile()); 3188 } 3189 } 3190 } 3191 writeVersionRecord(utils, utility.getOutPath()); 3192 } 3193 parseAndModifyModuleJson(String jsonFilePath, Utility utility)3194 private VersionNormalizeUtil parseAndModifyModuleJson(String jsonFilePath, Utility utility) 3195 throws BundleException { 3196 VersionNormalizeUtil util = new VersionNormalizeUtil(); 3197 try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) { 3198 JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class); 3199 if (!jsonObject.containsKey(APP)) { 3200 LOG.error("parseAndModifyJson failed, json file not valid."); 3201 throw new BundleException("parseAndModifyJson failed, json file not valid."); 3202 } 3203 JSONObject appObject = jsonObject.getJSONObject(APP); 3204 if (!appObject.containsKey(VERSION_CODE)) { 3205 LOG.error("parseAndModifyJson failed, json file not valid."); 3206 throw new BundleException("parseAndModifyJson failed, json file not valid."); 3207 } 3208 if (!appObject.containsKey(VERSION_NAME)) { 3209 LOG.error("parseAndModifyJson failed, json file not valid."); 3210 throw new BundleException("parseAndModifyJson failed, json file not valid."); 3211 } 3212 util.setOriginVersionCode(appObject.getIntValue(VERSION_CODE)); 3213 util.setOriginVersionName(appObject.getString(VERSION_NAME)); 3214 3215 JSONObject moduleObject = jsonObject.getJSONObject(MODULE); 3216 if (!moduleObject.containsKey(NAME)) { 3217 LOG.error("parseAndModifyModuleJson failed, json file not valid."); 3218 throw new BundleException("parseAndModifyModuleJson failed, json file not valid."); 3219 } 3220 util.setModuleName(moduleObject.getString(NAME)); 3221 appObject.put(VERSION_CODE, utility.getVersionCode()); 3222 appObject.put(VERSION_NAME, utility.getVersionName()); 3223 writeJson(jsonFilePath, jsonObject); 3224 } catch (IOException e) { 3225 LOG.error("parseAndModifyModuleJson failed, IOException." + e.getMessage()); 3226 throw new BundleException("parseAndModifyModuleJson failed, IOException." + e.getMessage()); 3227 } 3228 return util; 3229 } 3230 parseAndModifyPackInfo(String packInfoPath, Utility utility)3231 private void parseAndModifyPackInfo(String packInfoPath, Utility utility) 3232 throws BundleException { 3233 try (FileInputStream jsonStream = new FileInputStream(packInfoPath)) { 3234 JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class); 3235 if (jsonObject == null) { 3236 LOG.warning("parseAndModifyPackInfo failed, json format invalid."); 3237 return; 3238 } 3239 JSONObject summaryObject = jsonObject.getJSONObject(SUMMARY); 3240 if (summaryObject == null) { 3241 LOG.warning("parseAndModifyPackInfo failed, summary invalid."); 3242 return; 3243 } 3244 JSONObject appObject = summaryObject.getJSONObject(APP); 3245 if (appObject == null) { 3246 LOG.warning("parseAndModifyPackInfo failed, app invalid."); 3247 return; 3248 } 3249 JSONObject versionObject = appObject.getJSONObject(VERSION); 3250 if (versionObject == null) { 3251 LOG.warning("parseAndModifyPackInfo failed, version invalid."); 3252 return; 3253 } 3254 versionObject.put(CODE, utility.getVersionCode()); 3255 versionObject.put(NAME, utility.getVersionName()); 3256 writeJson(packInfoPath, jsonObject); 3257 } catch (IOException e) { 3258 LOG.warning("parseAndModifyPackInfo failed, IOException." + e.getMessage()); 3259 } 3260 } 3261 writeJson(String jsonFilePath, JSONObject jsonObject)3262 private void writeJson(String jsonFilePath, JSONObject jsonObject) throws IOException, BundleException { 3263 BufferedWriter bw = null; 3264 try { 3265 String pretty = JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat, 3266 SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat); 3267 bw = new BufferedWriter(new OutputStreamWriter( 3268 new FileOutputStream(jsonFilePath), StandardCharsets.UTF_8)); 3269 bw.write(pretty); 3270 } catch (IOException exception) { 3271 LOG.error("Compressor::writeJson failed for IOException " + exception.getMessage()); 3272 throw new BundleException("Compressor::writeJson failed for IOException"); 3273 } finally { 3274 if (bw != null) { 3275 bw.flush(); 3276 bw.close(); 3277 } 3278 } 3279 } 3280 parseAndModifyConfigJson(String jsonFilePath, Utility utility)3281 private VersionNormalizeUtil parseAndModifyConfigJson(String jsonFilePath, Utility utility) 3282 throws BundleException { 3283 VersionNormalizeUtil util = new VersionNormalizeUtil(); 3284 try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) { 3285 JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class); 3286 if (!jsonObject.containsKey(APP)) { 3287 LOG.error("parseAndModifyJson failed, json file not valid."); 3288 throw new BundleException("parseAndModifyJson failed, json file not valid."); 3289 } 3290 JSONObject appObject = jsonObject.getJSONObject(APP); 3291 if (!appObject.containsKey(VERSION)) { 3292 LOG.error("parseAndModifyModuleJson failed, json file not valid."); 3293 throw new BundleException("parseAndModifyModuleJson failed, json file not valid."); 3294 } 3295 JSONObject versionObj = appObject.getJSONObject(VERSION); 3296 if (!versionObj.containsKey(CODE)) { 3297 LOG.error("parseAndModifyModuleJson failed, json file not valid."); 3298 throw new BundleException("parseAndModifyModuleJson failed, json file not valid."); 3299 } 3300 util.setOriginVersionCode(versionObj.getIntValue(CODE)); 3301 if (!versionObj.containsKey(NAME)) { 3302 LOG.error("parseAndModifyModuleJson failed, json file not valid."); 3303 throw new BundleException("parseAndModifyModuleJson failed, json file not valid."); 3304 } 3305 util.setOriginVersionName(versionObj.getString(NAME)); 3306 3307 JSONObject moduleObject = jsonObject.getJSONObject(MODULE); 3308 if (!moduleObject.containsKey(NAME)) { 3309 LOG.error("parseAndModifyModuleJson failed, json file not valid."); 3310 throw new BundleException("parseAndModifyModuleJson failed, json file not valid."); 3311 } 3312 util.setModuleName(moduleObject.getString(NAME)); 3313 3314 versionObj.put(CODE, utility.getVersionCode()); 3315 versionObj.put(NAME, utility.getVersionName()); 3316 writeJson(jsonFilePath, jsonObject); 3317 } catch (IOException e) { 3318 LOG.error("parseAndModifyModuleJson IOException." + e.getMessage()); 3319 throw new BundleException("parseAndModifyModuleJson IOException." + e.getMessage()); 3320 } 3321 return util; 3322 } 3323 compressDirToHap(Path sourceDir, String zipFilePath)3324 private void compressDirToHap(Path sourceDir, String zipFilePath) 3325 throws IOException, BundleException { 3326 Utility utility = new Utility(); 3327 utility.setOutPath(zipFilePath); 3328 if (zipFilePath.endsWith(HAP_SUFFIX)) { 3329 utility.setMode(Utility.MODE_HAP); 3330 } else if (zipFilePath.endsWith(HSP_SUFFIX)) { 3331 utility.setMode(Utility.MODE_HSP); 3332 } 3333 try (Stream<Path> pathStream = Files.walk(sourceDir, 1)) { 3334 pathStream.forEach(path -> { 3335 String fileName = path.getFileName().toString(); 3336 String filePath = path.toString(); 3337 3338 switch (fileName) { 3339 case ETS_FILE_NAME: 3340 utility.setEtsPath(filePath); 3341 break; 3342 case HNP_FILE_NAME: 3343 utility.setHnpPath(filePath); 3344 break; 3345 case LIBS_DIR: 3346 utility.setLibPath(filePath); 3347 break; 3348 case AN_FILE_NAME: 3349 utility.setANPath(filePath); 3350 break; 3351 case AP_FILE_NAME: 3352 utility.setAPPath(filePath); 3353 break; 3354 case RESOURCE_FILE_NAME: 3355 utility.setResourcesPath(filePath); 3356 break; 3357 case JS_FILE_NAME: 3358 utility.setJsPath(filePath); 3359 break; 3360 case ASSETS_FILE_NAME: 3361 utility.setAssetsPath(filePath); 3362 break; 3363 case MAPLE_FILE_NAME: 3364 utility.setSoDir(filePath); 3365 break; 3366 case SHARED_LIBS_FILE_NAME: 3367 utility.setSharedLibsPath(filePath); 3368 break; 3369 case CONFIG_JSON: 3370 utility.setJsonPath(filePath); 3371 break; 3372 case MODULE_JSON: 3373 utility.setJsonPath(filePath); 3374 break; 3375 case RES_INDEX: 3376 utility.setIndexPath(filePath); 3377 break; 3378 case PACKINFO_NAME: 3379 utility.setPackInfoPath(filePath); 3380 break; 3381 case RPCID: 3382 utility.setRpcid(filePath); 3383 break; 3384 case PKG_CONTEXT_INFO: 3385 utility.setPkgContextPath(filePath); 3386 break; 3387 } 3388 }); 3389 } 3390 compressProcess(utility); 3391 } 3392 deleteDirectory(File dir)3393 private static void deleteDirectory(File dir) { 3394 if (dir.isDirectory()) { 3395 File[] children = dir.listFiles(); 3396 if (children != null) { 3397 for (File child : children) { 3398 deleteDirectory(child); 3399 } 3400 } 3401 } 3402 dir.delete(); 3403 } 3404 writeVersionRecord(List<VersionNormalizeUtil> utils, String outPath)3405 private static void writeVersionRecord(List<VersionNormalizeUtil> utils, String outPath) { 3406 String jsonString = JSON.toJSONString(utils); 3407 try (FileWriter fileWriter = new FileWriter(outPath + LINUX_FILE_SEPARATOR + VERSION_RECORD)) { 3408 fileWriter.write(jsonString); 3409 } catch (IOException e) { 3410 LOG.error("writeVersionRecord failed " + e.getMessage()); 3411 } 3412 } 3413 verifyModuleVersion(VersionNormalizeUtil versionNormalizeUtil, Utility utility)3414 private static void verifyModuleVersion(VersionNormalizeUtil versionNormalizeUtil, Utility utility) 3415 throws BundleException { 3416 if (versionNormalizeUtil.getOriginVersionCode() > utility.getVersionCode()) { 3417 String errorMsg = "versionNormalize failed, module " + versionNormalizeUtil.getModuleName() 3418 + " version code less than input version code"; 3419 LOG.error(errorMsg); 3420 throw new BundleException(errorMsg); 3421 } else if (versionNormalizeUtil.getOriginVersionCode() == utility.getVersionCode()) { 3422 LOG.warning("versionNormalize warning: module " + 3423 versionNormalizeUtil.getModuleName() + " version code not changed"); 3424 } 3425 if (versionNormalizeUtil.getOriginVersionName().equals(utility.getVersionName())) { 3426 LOG.warning("versionNormalize warning: module " + 3427 versionNormalizeUtil.getModuleName() + " version name not changed"); 3428 } 3429 } 3430 unpackHap(String srcPath, String outPath)3431 private static void unpackHap(String srcPath, String outPath) throws BundleException { 3432 try (FileInputStream fis = new FileInputStream(srcPath); 3433 ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(fis))) { 3434 File destDir = new File(outPath); 3435 if (!destDir.exists()) { 3436 destDir.mkdirs(); 3437 } 3438 3439 ZipEntry entry; 3440 while ((entry = zipInputStream.getNextEntry()) != null) { 3441 String entryName = entry.getName(); 3442 File entryFile = new File(outPath, entryName); 3443 3444 if (entry.isDirectory()) { 3445 entryFile.mkdirs(); 3446 zipInputStream.closeEntry(); 3447 continue; 3448 } 3449 File parent = entryFile.getParentFile(); 3450 if (!parent.exists()) { 3451 parent.mkdirs(); 3452 } 3453 3454 writeToFile(zipInputStream, entryFile); 3455 3456 zipInputStream.closeEntry(); 3457 } 3458 } catch (IOException e) { 3459 LOG.error("unpack hap failed IOException " + e.getMessage()); 3460 throw new BundleException("unpack hap failed IOException " + e.getMessage()); 3461 } 3462 } 3463 writeToFile(ZipInputStream zipInputStream, File entryFile)3464 private static void writeToFile(ZipInputStream zipInputStream, File entryFile) throws IOException { 3465 try (FileOutputStream fos = new FileOutputStream(entryFile)) { 3466 byte[] buffer = new byte[BUFFER_SIZE]; 3467 int bytesRead; 3468 while ((bytesRead = zipInputStream.read(buffer)) != -1) { 3469 fos.write(buffer, 0, bytesRead); 3470 } 3471 } 3472 } 3473 getFileNameByPath(String path)3474 private static String getFileNameByPath(String path) { 3475 Path filePath = Paths.get(path); 3476 return filePath.getFileName().toString(); 3477 } 3478 packEncryptJsonFile(Utility utility)3479 private void packEncryptJsonFile(Utility utility) throws BundleException { 3480 if (!utility.getEncryptPath().isEmpty()) { 3481 pathToFile(utility, utility.getEncryptPath(), NULL_DIR_NAME, false); 3482 } else { 3483 LOG.info("Compressor::packEncryptJsonFile has no encrypt.json"); 3484 } 3485 } 3486 } 3487