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