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