• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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