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