• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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.nio.charset.StandardCharsets;
31 import java.nio.file.attribute.FileTime;
32 import java.security.MessageDigest;
33 import java.security.NoSuchAlgorithmException;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Enumeration;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Optional;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43 import java.util.zip.CRC32;
44 import java.util.zip.CheckedOutputStream;
45 import java.util.zip.ZipInputStream;
46 import java.util.zip.ZipEntry;
47 import java.util.zip.ZipFile;
48 import java.util.zip.ZipOutputStream;
49 import com.alibaba.fastjson.JSON;
50 import com.alibaba.fastjson.JSONObject;
51 import com.alibaba.fastjson.serializer.SerializerFeature;
52 
53 /**
54  * bundle compressor class, compress file and directory.
55  *
56  */
57 public class Compressor {
58     private static final String HAP_SUFFIX = ".hap";
59     private static final String HSP_SUFFIX = ".hsp";
60     private static final String PNG_SUFFIX = ".png";
61     private static final String UPPERCASE_PNG_SUFFIX = ".PNG";
62     private static final String CONFIG_JSON = "config.json";
63     private static final String MODULE_JSON = "module.json";
64     private static final String PATCH_JSON = "patch.json";
65     private static final String NAME = "name";
66     private static final String NULL_DIR_NAME = "";
67     private static final String RES_DIR_NAME = "res/";
68     private static final String RESOURCES_DIR_NAME = "resources/";
69     private static final String LIBS_DIR_NAME = "libs/";
70     private static final String AN_DIR_NAME = "an/";
71     private static final String AP_PATH_NAME = "ap/";
72     private static final String ASSETS_DIR_NAME = "assets/";
73     private static final String SO_DIR_NAME = "maple/";
74     private static final String SO_ARM64_DIR_NAME = "maple/arm64/";
75     private static final String LINUX_FILE_SEPARATOR = "/";
76     private static final String DISTRO = "distro";
77     private static final String FORMS = "forms";
78     private static final String MODULE_NAME = "module-name";
79     private static final String MODULE_NAME_NEW = "moduleName";
80     private static final String JSON_END = "}";
81     private static final String SEMICOLON = "\"";
82     private static final String COMPRESS_NATIVE_LIBS = "compressNativeLibs";
83     private static final String SHARED_LIBS_DIR_NAME = "shared_libs/";
84     private static final String DEVICE_TYPE = "deviceType";
85     private static final String DEVICE_TYPE_FITNESSWATCH = "fitnessWatch";
86     private static final String DEVICE_TYPE_FITNESSWATCH_NEW = "liteWearable";
87     private static final String ENTRYCARD_NAME = "EntryCard/";
88     private static final String PACKINFO_NAME = "pack.info";
89     private static final String ENTRYCARD_BASE_NAME = "base";
90     private static final String ENTRYCARD_SNAPSHOT_NAME = "snapshot";
91     private static final String PIC_1X2 = "1x2";
92     private static final String PIC_2X2 = "2x2";
93     private static final String PIC_2X4 = "2x4";
94     private static final String PIC_4X4 = "4x4";
95     private static final String REGEX_LANGUAGE = "^[a-z]{2}$";
96     private static final String REGEX_SCRIPT = "^[A-Z][a-z]{3}$";
97     private static final String REGEX_COUNTRY = "^[A-Z]{2,3}|[0-9]{3}$";
98     private static final String REGEX_ORIENTATION = "^vertical|horizontal$";
99     private static final String REGEX_DEVICE_TYPE = "^phone|tablet|car|tv|wearable|liteWearable|2in1$";
100     private static final String REGEX_SCREEN_DENSITY = "^sdpi|mdpi|ldpi|xldpi|xxldpi$";
101     private static final String REGEX_COLOR_MODE = "^light|dark$";
102     private static final String REGEX_SHAPE = "^circle$";
103     private static final String JS_PATH = "js/";
104     private static final String ETS_PATH = "ets/";
105     private static final String TEMP_HAP_DIR = "tempHapDir";
106     private static final String TEMP_HSP_DIR = "tempHspDir";
107     private static final String TEMP_SELECTED_HAP_DIR = "tempSelectedHapDir";
108     private static final String EMPTY_STRING = "";
109     private static final String RELEASE = "Release";
110     private static final String TYPE_SHARED = "shared";
111     private static final String APP = "app";
112     private static final String MODULE = "module";
113     private static final String GENERATE_BUILD_HASH = "generateBuildHash";
114     private static final String BUILD_HASH = "buildHash";
115     private static final String TEMP_DIR = "temp";
116     private static final String SHA_256 = "SHA-256";
117     private static final String JSON_SUFFIX = ".json";
118     private static final String ATOMIC_SERVICE = "atomicService";
119     private static final String RAW_FILE_PATH = "resources/rawfile";
120 
121     // set timestamp to get fixed MD5
122     private static final long FILE_TIME = 1546272000000L;
123     private static final int ENTRY_FILE_LIMIT_DEFAULT = 2;
124     private static final int NOT_ENTRY_FILE_LIMIT_DEFAULT = 2;
125     private static final int TOTAL_FILE_LIMIT_DEFAULT = 10;
126     private static final int FILE_LIMIT = 10;
127     private static final int SHA256_BASE = 0xff;
128     private static final int SHA256_OFFSET = 0x100;
129     private static final int RADIX = 16;
130     private static final int BEGIN_INDEX = 1;
131     private static final int BUFFER_BYTE_SIZE = 1024;
132     private static final int BUFFER_WRITE_SIZE = 1444;
133 
134     // set buffer size of each read
135     private static final int BUFFER_SIZE = 10 * 1024;
136     private static final Log LOG = new Log(Compressor.class.toString());
137 
138     private static int entryModuleSizeLimit = 2;
139     private static int notEntryModuleSizeLimit = 2;
140     private static int sumModuleSizeLimit = 10;
141     private static boolean isOverlay = false;
142 
143     private ZipOutputStream zipOut = null;
144     private boolean mIsContain2x2EntryCard = true;
145     private List<String> list = new ArrayList<String>();
146     private List<String> formNamesList = new ArrayList<String>();
147     private List<String> fileNameList = new ArrayList<String>();
148     private List<String> supportDimensionsList = Arrays.asList(PIC_1X2, PIC_2X2, PIC_2X4, PIC_4X4);
149 
getEntryModuleSizeLimit()150     public static int getEntryModuleSizeLimit() {
151         return entryModuleSizeLimit;
152     }
153 
setEntryModuleSizeLimit(int entry)154     public static void setEntryModuleSizeLimit(int entry) {
155         entryModuleSizeLimit = entry;
156     }
157 
getNotEntryModuleSizeLimit()158     public static int getNotEntryModuleSizeLimit() {
159         return notEntryModuleSizeLimit;
160     }
161 
setNotEntryModuleSizeLimit(int notEntry)162     public static void setNotEntryModuleSizeLimit(int notEntry) {
163         notEntryModuleSizeLimit = notEntry;
164     }
165 
getSumModuleSizeLimit()166     public static int getSumModuleSizeLimit() {
167         return sumModuleSizeLimit;
168     }
169 
setSumModuleSizeLimit(int sumModule)170     public static void setSumModuleSizeLimit(int sumModule) {
171         sumModuleSizeLimit = sumModule;
172     }
173 
174     /**
175      * parse file size limit from utility.
176      *
177      * @param utility Indicates the utility.
178      */
parseFileSizeLimit(Utility utility)179     public void parseFileSizeLimit(Utility utility) throws BundleException {
180         int sumLimit = TOTAL_FILE_LIMIT_DEFAULT;
181         String totalLimit = utility.getTotalLimit();
182         if (!totalLimit.isEmpty()) {
183             try {
184                 sumLimit = Integer.parseInt(totalLimit);
185             } catch (NumberFormatException e) {
186                 LOG.error("parseFileSizeLimit failed, input total-limit invalid.");
187                 throw new BundleException("parseFileSizeLimit failed, input total-limit invalid.");
188             }
189             if (sumLimit <= 0 || sumLimit > FILE_LIMIT) {
190                 LOG.error("parseFileSizeLimit failed, input total-limit invalid.");
191                 throw new BundleException("parseFileSizeLimit failed, input total-limit invalid.");
192             }
193         }
194         String normalLimit = utility.getNormalModuleLimit();
195         int notEntry = NOT_ENTRY_FILE_LIMIT_DEFAULT;
196         if (!normalLimit.isEmpty()) {
197             try {
198                 notEntry = Integer.parseInt(normalLimit);
199             } catch (NumberFormatException e) {
200                 LOG.error("parseFileSizeLimit failed, input normal-module-limit invalid.");
201                 throw new BundleException("parseFileSizeLimit failed, input normal-module-limit invalid.");
202             }
203             if (notEntry <= 0 || notEntry > sumLimit || notEntry > FILE_LIMIT) {
204                 LOG.error("parseFileSizeLimit failed, input normal-module-limit invalid.");
205                 throw new BundleException("parseFileSizeLimit failed, input normal-module-limit invalid.");
206             }
207         }
208         String mainLimit = utility.getMainModuleLimit();
209         int entryLimit = ENTRY_FILE_LIMIT_DEFAULT;
210         if (!mainLimit.isEmpty()) {
211             try {
212                 entryLimit = Integer.parseInt(mainLimit);
213             } catch (NumberFormatException e) {
214                 LOG.error("parseFileSizeLimit failed, input main-module-limit invalid.");
215                 throw new BundleException("parseFileSizeLimit failed, input main-module-limit invalid.");
216             }
217             if (entryLimit <= 0 || entryLimit > sumLimit || entryLimit > FILE_LIMIT) {
218                 LOG.error("parseFileSizeLimit failed, input main-module-limit invalid.");
219                 throw new BundleException("parseFileSizeLimit failed, input main-module-limit invalid.");
220             }
221         }
222         setEntryModuleSizeLimit(entryLimit);
223         setNotEntryModuleSizeLimit(notEntry);
224         setSumModuleSizeLimit(sumLimit);
225     }
226 
227     /**
228      * check path if is a module.json file
229      *
230      * @param path   path input
231      * @return true if path is a module file
232      */
isModuleJSON(String path)233     private static boolean isModuleJSON(String path)
234     {
235         File file = new File(path);
236         if ((file.isFile()) && MODULE_JSON.equals(file.getName())) {
237             return true;
238         }
239         return false;
240     }
241 
242     /**
243      * start compress.
244      * file orders as follows:
245      * for hap: 1.config.json 2.lib 3.res 4.assets 5.*.so 6.*.dex 7.*.apk 8.resources.index
246      * for app: 1.certificate 2.signature 3.pack.info 4.hap (1 and 2 may not be used)
247      *
248      * @param utility common data
249      * @return compressProcess if compress succeed
250      */
compressProcess(Utility utility)251     public boolean compressProcess(Utility utility) {
252         boolean compressResult = true;
253         File destFile = new File(utility.getOutPath());
254 
255         // if out file directory not exist, mkdirs.
256         File outParentFile = destFile.getParentFile();
257         if ((outParentFile != null) && (!outParentFile.exists())) {
258             if (!outParentFile.mkdirs()) {
259                 LOG.error("Compressor::compressProcess create out file parent directory failed.");
260                 return false;
261             }
262         }
263 
264         FileOutputStream fileOut = null;
265         CheckedOutputStream checkedOut = null;
266         try {
267             fileOut = new FileOutputStream(destFile);
268             checkedOut = new CheckedOutputStream(fileOut, new CRC32());
269             zipOut = new ZipOutputStream(checkedOut);
270             compressExcute(utility);
271         } catch (FileNotFoundException exception) {
272             compressResult = false;
273             LOG.error("Compressor::compressProcess file not found exception" + exception.getMessage());
274         } catch (BundleException ignored) {
275             compressResult = false;
276             LOG.error("Compressor::compressProcess Bundle exception.");
277         } finally {
278             closeZipOutputStream();
279             Utility.closeStream(zipOut);
280             Utility.closeStream(checkedOut);
281             Utility.closeStream(fileOut);
282         }
283         // if compress failed, delete out file.
284         if (!compressResult) {
285             LOG.error("Compressor::compressProcess compress failed.");
286             if (!destFile.delete()) {
287                 LOG.error("Compressor::compressProcess delete dest file failed.");
288             }
289         }
290         return compressResult;
291     }
292 
compressExcute(Utility utility)293     private void compressExcute(Utility utility) throws BundleException {
294         switch (utility.getMode()) {
295             case Utility.MODE_HAP:
296                 compressHap(utility);
297                 break;
298             case Utility.MODE_HAR:
299                 compressHarMode(utility);
300                 break;
301             case Utility.MODE_APP:
302                 compressAppMode(utility);
303                 break;
304             case Utility.MODE_MULTI_APP:
305                 compressAppModeForMultiProject(utility);
306                 break;
307             case Utility.MODE_HQF:
308                 compressHQFMode(utility);
309                 break;
310             case Utility.MODE_APPQF:
311                 compressAPPQFMode(utility);
312                 break;
313             case Utility.MODE_HSP:
314                 compressHsp(utility);
315                 break;
316             default:
317                 compressPackResMode(utility);
318         }
319     }
320 
compressHsp(Utility utility)321     private void compressHsp(Utility utility) throws BundleException {
322         setGenerateBuildHash(utility);
323         if (isModuleJSON(utility.getJsonPath())) {
324             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
325             String jsonString = optional.get();
326             if (!checkStageAtomicService(jsonString)) {
327                 LOG.error("checkStageAtomicService failed.");
328                 throw new BundleException("checkStageAtomicService failed.");
329             }
330             // check whether is an overlay hsp or not
331             if (!checkStageOverlayCfg(jsonString)) {
332                 LOG.error("checkStageOverlayCfg failed.");
333                 throw new BundleException("checkStageOverlayCfg failed.");
334             }
335             String moduleType = ModuleJsonUtil.parseModuleType(jsonString);
336             if (!TYPE_SHARED.equals(moduleType)) {
337                 LOG.error("module type must be shared.");
338                 throw new BundleException("compressHsp failed.");
339             }
340         }
341         compressHSPMode(utility);
342         buildHash(utility);
343     }
344 
compressHap(Utility utility)345     private void compressHap(Utility utility) throws BundleException {
346         if (utility.getJsonPath().isEmpty() && !utility.getBinPath().isEmpty()) {
347             // only for slim device
348             compressHapMode(utility);
349             return;
350         }
351         setGenerateBuildHash(utility);
352         if (isModuleJSON(utility.getJsonPath())) {
353             if (!checkStageHap(utility)) {
354                 LOG.error("checkStageHap failed.");
355                 throw new BundleException("checkStageHap failed.");
356             }
357             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
358             String jsonString = optional.get();
359             String moduleType = ModuleJsonUtil.parseModuleType(jsonString);
360             if (TYPE_SHARED.equals(moduleType)) {
361                 LOG.warning("Compress mode is hap, but module type is shared.");
362             }
363             String bundleType = ModuleJsonUtil.parseStageBundleType(jsonString);
364             if (TYPE_SHARED.equals(bundleType)) {
365                 LOG.warning("Compress mode is hap, but app type is shared.");
366             }
367             compressHapModeForModule(utility);
368             buildHash(utility);
369         } else {
370             if (!checkFAHap(utility)) {
371                 LOG.error("checkFAHap failed.");
372                 throw new BundleException("checkStageHap failed.");
373             }
374             compressHapMode(utility);
375             buildHash(utility);
376         }
377     }
378 
hasGenerateBuildHash(Utility utility)379     private static boolean hasGenerateBuildHash(Utility utility) throws BundleException {
380         File file = new File(utility.getJsonPath());
381         if (!file.exists()) {
382             LOG.error("Compressor::hasGenerateBuildHash failed for json file not exist");
383             throw new BundleException("Compressor::hasGenerateBuildHash failed for json file not exist");
384         }
385         InputStream json = null;
386         boolean res = false;
387         try {
388             json = new FileInputStream(file);
389             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
390             if (!jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) {
391                 LOG.error("json file is invalid.");
392                 throw new BundleException("json file is invalid.");
393             }
394             JSONObject appJson = jsonObject.getJSONObject(APP);
395             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
396             if (appJson.containsKey(GENERATE_BUILD_HASH) || moduleJson.containsKey(GENERATE_BUILD_HASH)) {
397                 res = true;
398             }
399         } catch (BundleException | IOException exception) {
400             LOG.error("Compressor::hasGenerateBuildHash failed.");
401             throw new BundleException("Compressor::hasGenerateBuildHash failed.");
402         } finally {
403             FileUtils.closeStream(json);
404         }
405         return res;
406     }
407 
setGenerateBuildHash(Utility utility)408     private static void setGenerateBuildHash(Utility utility) throws BundleException {
409         if (utility.isBuildHashFinish() || !hasGenerateBuildHash(utility)) {
410             return;
411         }
412         copyFileToTempDir(utility);
413         File file = new File(utility.getJsonPath());
414         if (!file.exists()) {
415             LOG.error("Compressor::setGenerateBuildHash failed for json file not exist");
416             throw new BundleException("Compressor::setGenerateBuildHash failed for json file not exist");
417         }
418         InputStream json = null;
419         BufferedWriter bw = null;
420         try {
421             json = new FileInputStream(file);
422             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
423             if (!jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) {
424                 LOG.error("json file is invalid.");
425                 throw new BundleException("json file is invalid.");
426             }
427             JSONObject appJson = jsonObject.getJSONObject(APP);
428             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
429             if (appJson.containsKey(GENERATE_BUILD_HASH) && appJson.getBoolean(GENERATE_BUILD_HASH)) {
430                 utility.setGenerateBuildHash(true);
431             } else {
432                 if (moduleJson.containsKey(GENERATE_BUILD_HASH) && moduleJson.getBoolean(GENERATE_BUILD_HASH)) {
433                     utility.setGenerateBuildHash(true);
434                 }
435             }
436             appJson.remove(GENERATE_BUILD_HASH);
437             moduleJson.remove(GENERATE_BUILD_HASH);
438             String pretty = JSON.toJSONString(jsonObject, new SerializerFeature[]{
439                     SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
440                     SerializerFeature.WriteDateUseDateFormat});
441             bw = new BufferedWriter(new FileWriter(utility.getJsonPath()));
442             bw.write(pretty);
443         } catch (BundleException | IOException exception) {
444             LOG.error("Compressor::setGenerateBuildHash failed.");
445             throw new BundleException("Compressor::setGenerateBuildHash failed.");
446         } finally {
447             FileUtils.closeStream(json);
448             if (bw != null) {
449                 try {
450                     bw.flush();
451                     bw.close();
452                 } catch (IOException e) {
453                     LOG.error("Compressor::setGenerateBuildHash failed for IOException " + e.getMessage());
454                     throw new BundleException("Compressor::setGenerateBuildHash failed.");
455                 }
456             }
457         }
458     }
459 
copyFileToTempDir(Utility utility)460     private static void copyFileToTempDir(Utility utility) throws BundleException {
461         String jsonPath = utility.getJsonPath();
462         File oldfile = new File(jsonPath);
463         if (!oldfile.exists()) {
464             LOG.error("Compressor::copyFileToTempDir failed for json file not found.");
465             throw new BundleException("Compressor::copyFileToTempDir failed for json file not found.");
466         }
467         String oldfileParent = oldfile.getParent();
468         mkdir(new File(oldfileParent + File.separator + TEMP_DIR));
469         String fileName;
470         if (isModuleJSON(utility.getJsonPath())) {
471             fileName = MODULE_JSON;
472         } else {
473             fileName = CONFIG_JSON;
474         }
475         String tempPath = oldfileParent + File.separator + TEMP_DIR + File.separator + fileName;
476 
477         try (InputStream inStream = new FileInputStream(jsonPath);
478              FileOutputStream fs = new FileOutputStream(tempPath)) {
479                 byte[] buffer = new byte[BUFFER_WRITE_SIZE];
480                 int byteread;
481                 while ((byteread = inStream.read(buffer)) != -1) {
482                     fs.write(buffer, 0, byteread);
483                 }
484                 utility.setJsonPath(tempPath);
485         } catch (IOException e) {
486             LOG.error("Compressor::copyFileToTempDir failed, IOException: " + e.getMessage());
487             throw new BundleException("Compressor::copyFileToTempDir failed.");
488         }
489     }
490 
mkdir(File file)491     private static void mkdir(File file) {
492         if (null != file && !file.exists()) {
493             mkdir(file.getParentFile());
494             file.mkdir();
495         }
496     }
497 
buildHash(Utility utility)498     private static void buildHash(Utility utility) throws BundleException {
499         if (utility.isBuildHashFinish() || (!utility.getGenerateBuildHash())) {
500             return;
501         }
502         String filePath = utility.getOutPath();
503         String hash = getSHA256(filePath);
504         try {
505             putBuildHash(utility, hash);
506         } catch (IOException e) {
507             LOG.error("Compressor::putBuildHash failed, " + e.getMessage());
508             throw new BundleException("Compressor::putBuildHash failed.");
509         }
510     }
511 
checkSum(String filename)512     private static byte[] checkSum(String filename) throws BundleException {
513         try (InputStream fis = new FileInputStream(filename)) {
514             byte[] buffer = new byte[BUFFER_BYTE_SIZE];
515             MessageDigest complete = MessageDigest.getInstance(SHA_256);
516             int numRead;
517             do {
518                 numRead = fis.read(buffer);
519                 if (numRead > 0) {
520                     complete.update(buffer, 0, numRead);
521                 }
522             } while (numRead != -1);
523             return complete.digest();
524         } catch (IOException | NoSuchAlgorithmException e) {
525             LOG.error("Compressor::checkSum failed, IOException or NoSuchAlgorithmException: " + e.getMessage());
526             throw new BundleException("Compressor::checkSum failed.");
527         }
528     }
529 
530     /**
531      * get SHA256 of hap or hsp
532      *
533      * @param filePath the path of hap or hsp.
534      */
getSHA256(String filePath)535     public static String getSHA256(String filePath) throws BundleException {
536         byte[] byteSum = checkSum(filePath);
537         StringBuilder temp = new StringBuilder();
538         for (int i = 0; i < byteSum.length; i++) {
539             temp.append(
540                     Integer.toString((byteSum[i] & SHA256_BASE) + SHA256_OFFSET, RADIX).substring(BEGIN_INDEX));
541         }
542         return temp.toString();
543     }
544 
putBuildHash(Utility utility, String hash)545     private static void putBuildHash(Utility utility, String hash) throws BundleException, IOException {
546         if (utility.isBuildHashFinish()) {
547             return;
548         }
549         String jsonPath = utility.getJsonPath();
550         File file = new File(jsonPath);
551         if (!file.exists()) {
552             LOG.error("Compressor::putBuildHash failed for json file not exist");
553             throw new BundleException("Compressor::putBuildHash failed for json file not exist");
554         }
555         InputStream json = null;
556         BufferedWriter bw = null;
557         try {
558             json = new FileInputStream(file);
559             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
560             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
561             moduleJson.put(BUILD_HASH, hash);
562             String pretty = JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat,
563                     SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
564             bw = new BufferedWriter(new FileWriter(jsonPath));
565             bw.write(pretty);
566         } catch (IOException e) {
567             LOG.error("Compressor::putBuildHash failed, IOException: " + e.getMessage());
568             throw new BundleException("Compressor::putBuildHash failed.");
569         } finally {
570             FileUtils.closeStream(json);
571             if (bw != null) {
572                 bw.flush();
573                 bw.close();
574             }
575         }
576         utility.setBuildHashFinish(true);
577     }
578 
checkStageHap(Utility utility)579     private static boolean checkStageHap(Utility utility) throws BundleException {
580         Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
581         String jsonString = optional.get();
582         if (!checkStageAsanEnabledValid(jsonString)) {
583             LOG.error("checkStageAsanEnabledValid failed.");
584             return false;
585         }
586         // check atomicService in module.json
587         if (!checkStageAtomicService(jsonString)) {
588             LOG.error("checkStageAtomicService failed.");
589             return false;
590         }
591         return true;
592     }
593 
checkStageAsanEnabledValid(String jsonString)594     private static boolean checkStageAsanEnabledValid(String jsonString) throws BundleException {
595         boolean asanEnabled = ModuleJsonUtil.getStageAsanEnabled(jsonString);
596         boolean debug = ModuleJsonUtil.getDebug(jsonString);
597         if (asanEnabled && !debug) {
598             LOG.error("asanEnabled is not supported for Release.");
599             return false;
600         }
601         return true;
602     }
603 
checkStageAtomicService(String jsonString)604     private static boolean checkStageAtomicService(String jsonString) throws BundleException {
605         // check consistency of atomicService
606         if (!ModuleJsonUtil.isModuleAtomicServiceValid(jsonString)) {
607             LOG.error("check module atomicService failed.");
608             return false;
609         }
610         // check entry module must have ability
611         if (!ModuleJsonUtil.checkEntryInAtomicService(jsonString)) {
612             LOG.error("checkEntryInAtomicService failed.");
613             return false;
614         }
615         // check installationFree
616         if (!ModuleJsonUtil.checkAtomicServiceInstallationFree(jsonString)) {
617             LOG.error("check atomic service installationFree failed.");
618             return false;
619         }
620 
621         return true;
622     }
623 
checkStageOverlayCfg(String jsonString)624     private static boolean checkStageOverlayCfg(String jsonString) throws BundleException {
625         // check module
626         String targetModuleName = ModuleJsonUtil.getStageTargetModuleName(jsonString);
627         if (!targetModuleName.isEmpty()) {
628             // check targetModuleName and requestPermission
629             if (ModuleJsonUtil.isExistedStageRequestPermissions(jsonString)) {
630                 LOG.error("targetModuleName cannot be existed with requestPermissions.");
631                 return false;
632             }
633             // check targetModuleName and name
634             if (targetModuleName.equals(ModuleJsonUtil.parseStageModuleName(jsonString))) {
635                 LOG.error("targetModuleName cannot be same with name in the overlay module.");
636                 return false;
637             }
638         } else {
639             if (ModuleJsonUtil.isExistedStageModuleTargetPriority(jsonString)) {
640                 LOG.error("targetPriority cannot be existed without the targetModuleName in module.json.");
641                 return false;
642             }
643         }
644         // check app
645         String targetBundleName = ModuleJsonUtil.getStageTargetBundleName(jsonString);
646         if (!targetBundleName.isEmpty()) {
647             if (targetModuleName.isEmpty()) {
648                 LOG.error("targetModuleName is necessary in the overlay bundle.");
649                 return false;
650             }
651             if (targetBundleName.equals(ModuleJsonUtil.parseBundleName(jsonString))) {
652                 LOG.error("targetBundleName cannot be same with the bundleName.");
653                 return false;
654             }
655         } else {
656             if (ModuleJsonUtil.isExistedStageAppTargetPriority(jsonString)) {
657                 LOG.error("targetPriority cannot be existed without the targetBundleName in app.json.");
658                 return false;
659             }
660         }
661         return true;
662     }
663 
checkFAHap(Utility utility)664     private static boolean checkFAHap(Utility utility) throws BundleException {
665         Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
666         String jsonString = optional.get();
667         return checkFAAsanEnabledValid(jsonString);
668     }
669 
checkFAAsanEnabledValid(String jsonString)670     private static boolean checkFAAsanEnabledValid(String jsonString) throws BundleException {
671         boolean asanEnabled = ModuleJsonUtil.getFAAsanEnabled(jsonString);
672         boolean debug = ModuleJsonUtil.getFADebug(jsonString);
673         if (asanEnabled && !debug) {
674             LOG.error("asanEnabled is not supported for Release.");
675             return false;
676         }
677         return true;
678     }
679 
680     /**
681      * compress in hap mode.
682      *
683      * @param utility common data
684      * @throws BundleException FileNotFoundException|IOException.
685      */
compressHapMode(Utility utility)686     private void compressHapMode(Utility utility) throws BundleException {
687         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
688 
689         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
690 
691         if (!utility.getIndexPath().isEmpty() && !utility.getModuleName().isEmpty()) {
692             String assetsPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR;
693             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
694         }
695 
696         if (!utility.getLibPath().isEmpty()) {
697             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
698         }
699 
700         if (!utility.getFilePath().isEmpty()) {
701             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
702         }
703 
704         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
705             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
706                     + RESOURCES_DIR_NAME;
707             String deviceTypes = utility.getDeviceType().replace("\"", "").trim();
708             if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) ||
709                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) {
710                 resPath = RES_DIR_NAME;
711             }
712             pathToFile(utility, utility.getResPath(), resPath, false);
713         }
714 
715         if (!utility.getResourcesPath().isEmpty() && !utility.getModuleName().isEmpty()) {
716             String resourcesPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
717                     + RESOURCES_DIR_NAME;
718             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
719         }
720 
721         if (!utility.getRpcidPath().isEmpty()) {
722             String rpcidPath = NULL_DIR_NAME;
723             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
724         }
725 
726         if (!utility.getPackInfoPath().isEmpty()) {
727             String packInfoPath = NULL_DIR_NAME;
728             pathToFile(utility, utility.getPackInfoPath(), packInfoPath, false);
729         }
730 
731         if (!utility.getAssetsPath().isEmpty()) {
732             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
733         }
734 
735         if (!utility.getBinPath().isEmpty()) {
736             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
737         }
738         // pack --dir-list
739         if (!utility.getFormatedDirList().isEmpty()) {
740             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
741                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
742                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
743             }
744         }
745 
746         compressHapModeMultiple(utility);
747     }
748 
749     /**
750      * compress in hap mode for module.json.
751      *
752      * @param utility common data
753      * @throws BundleException FileNotFoundException|IOException.
754      */
compressHapModeForModule(Utility utility)755     private void compressHapModeForModule(Utility utility) throws BundleException {
756         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
757 
758         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
759 
760         if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
761             String assetsPath = NULL_DIR_NAME;
762             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
763         }
764 
765         if (!utility.getLibPath().isEmpty()) {
766             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
767         }
768 
769         if (!utility.getANPath().isEmpty()) {
770             pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false);
771         }
772 
773         if (!utility.getAPPath().isEmpty()) {
774             pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false);
775         }
776 
777         if (!utility.getFilePath().isEmpty()) {
778             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
779         }
780 
781         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
782             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
783                     + RESOURCES_DIR_NAME;
784             String deviceTypes = utility.getDeviceType().replace("\"", "").trim();
785             if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) ||
786                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) {
787                 resPath = RES_DIR_NAME;
788             }
789             pathToFile(utility, utility.getResPath(), resPath, false);
790         }
791 
792         if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
793             String resourcesPath = RESOURCES_DIR_NAME;
794             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
795         }
796         if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
797             String jsPath = JS_PATH;
798             pathToFile(utility, utility.getJsPath(), jsPath, false);
799         }
800 
801         if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
802             String etsPath = ETS_PATH;
803             pathToFile(utility, utility.getEtsPath(), etsPath, false);
804         }
805 
806         if (!utility.getRpcidPath().isEmpty()) {
807             String rpcidPath = NULL_DIR_NAME;
808             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
809         }
810 
811         if (!utility.getAssetsPath().isEmpty()) {
812             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
813         }
814 
815         if (!utility.getBinPath().isEmpty()) {
816             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
817         }
818 
819         if (!utility.getPackInfoPath().isEmpty()) {
820             pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false);
821         }
822 
823         // pack --dir-list
824         if (!utility.getFormatedDirList().isEmpty()) {
825             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
826                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
827                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
828             }
829         }
830 
831         compressHapModeMultiple(utility);
832     }
833 
834     /**
835      * compress in hap mode multiple path.
836      *
837      * @param utility common data
838      * @throws BundleException FileNotFoundException|IOException.
839      */
compressHapModeMultiple(Utility utility)840     private void compressHapModeMultiple(Utility utility) throws BundleException {
841         for (String soPathItem : utility.getFormattedSoPathList()) {
842             pathToFile(utility, soPathItem, SO_ARM64_DIR_NAME, false);
843         }
844 
845         if (utility.getFormattedSoPathList().size() == 0 && !utility.getSoDir().isEmpty()) {
846             pathToFile(utility, utility.getSoDir(), SO_DIR_NAME, false);
847         }
848 
849         for (String soPathItem : utility.getFormattedAbilitySoPathList()) {
850             pathToFile(utility, soPathItem, NULL_DIR_NAME, false);
851         }
852 
853         for (String dexPathItem : utility.getFormattedDexPathList()) {
854             pathToFile(utility, dexPathItem, NULL_DIR_NAME, false);
855         }
856 
857         for (String abcPathItem : utility.getFormattedAbcPathList()) {
858             pathToFile(utility, abcPathItem, NULL_DIR_NAME, false);
859         }
860 
861         for (String apkPathItem : utility.getFormattedApkPathList()) {
862             pathToFile(utility, apkPathItem, NULL_DIR_NAME, false);
863         }
864 
865         for (String jarPathItem : utility.getFormattedJarPathList()) {
866             pathToFile(utility, jarPathItem, NULL_DIR_NAME, false);
867         }
868 
869         for (String txtPathItem : utility.getFormattedTxtPathList()) {
870             pathToFile(utility, txtPathItem, NULL_DIR_NAME, false);
871         }
872 
873         if (!utility.getSharedLibsPath().isEmpty()) {
874             pathToFile(utility, utility.getSharedLibsPath(), SHARED_LIBS_DIR_NAME, utility.isCompressNativeLibs());
875         }
876     }
877 
878     /**
879      * compress in har mode.
880      *
881      * @param utility common data
882      * @throws BundleException FileNotFoundException|IOException.
883      */
compressHarMode(Utility utility)884     private void compressHarMode(Utility utility) throws BundleException {
885         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
886 
887         if (!utility.getLibPath().isEmpty()) {
888             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
889         }
890 
891         if (!utility.getResPath().isEmpty()) {
892             pathToFile(utility, utility.getResPath(), RESOURCES_DIR_NAME, false);
893         }
894 
895         if (!utility.getResourcesPath().isEmpty()) {
896             pathToFile(utility, utility.getResourcesPath(), RESOURCES_DIR_NAME, false);
897         }
898 
899         if (!utility.getAssetsPath().isEmpty()) {
900             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
901         }
902 
903         for (String jarPathItem : utility.getFormattedJarPathList()) {
904             pathToFile(utility, jarPathItem, NULL_DIR_NAME, false);
905         }
906 
907         for (String txtPathItem : utility.getFormattedTxtPathList()) {
908             pathToFile(utility, txtPathItem, NULL_DIR_NAME, false);
909         }
910     }
911 
912     /**
913      * compress in app mode.
914      *
915      * @param utility common data
916      * @throws BundleException FileNotFoundException|IOException.
917      */
compressAppMode(Utility utility)918     private void compressAppMode(Utility utility) throws BundleException {
919         List<String> fileList = new ArrayList<>();
920         File appOutputFile = new File(utility.getOutPath().trim());
921         String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR;
922         String hspTempDirPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HSP_DIR;
923         try {
924             pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
925 
926             if (!utility.getCertificatePath().isEmpty()) {
927                 pathToFile(utility, utility.getCertificatePath(), NULL_DIR_NAME, false);
928             }
929 
930             if (!utility.getSignaturePath().isEmpty()) {
931                 pathToFile(utility, utility.getSignaturePath(), NULL_DIR_NAME, false);
932             }
933 
934             File tempDir = new File(tempPath);
935             if (!tempDir.exists()) {
936                 tempDir.mkdirs();
937             }
938 
939             for (String hapPathItem : utility.getFormattedHapPathList()) {
940                 File hapFile = new File(hapPathItem.trim());
941                 String hapTempPath = tempDir + File.separator + hapFile.getName();
942                 fileList.add(hapTempPath);
943                 try {
944                     compressPackinfoIntoHap(hapPathItem, hapTempPath, utility.getPackInfoPath());
945                 } catch (IOException e) {
946                     LOG.error("Compressor::compressAppMode compress pack.info into hap failed.");
947                     throw new BundleException("Compressor::compressAppMode compress pack.info into hap failed.");
948                 }
949             }
950 
951             File hspTempDir = new File(hspTempDirPath);
952             if (!hspTempDir.exists()) {
953                 hspTempDir.mkdirs();
954             }
955             for (String hspPathItem : utility.getFormattedHspPathList()) {
956                 File hspFile = new File(hspPathItem.trim());
957                 String hspTempPath = hspTempDir + File.separator + hspFile.getName();
958                 fileList.add(hspTempPath);
959                 try {
960                     compressPackinfoIntoHap(hspPathItem, hspTempPath, utility.getPackInfoPath());
961                 } catch (IOException e) {
962                     LOG.error("Compressor::compressAppMode compress pack.info into hsp failed.");
963                     throw new BundleException("Compressor::compressAppMode compress pack.info into hsp failed.");
964                 }
965             }
966             parseFileSizeLimit(utility);
967             // check hap is valid
968             if (!checkHapIsValid(fileList, utility.getSharedApp())) {
969                 throw new BundleException("Compressor::compressFile verify failed, check version, " +
970                         "apiVersion,moduleName,packageName.");
971             }
972             for (String hapPath : fileList) {
973                 pathToFile(utility, hapPath, NULL_DIR_NAME, false);
974             }
975 
976             if (!utility.getEntryCardPath().isEmpty()) {
977                 String entryCardPath = ENTRYCARD_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
978                         + ENTRYCARD_BASE_NAME + ENTRYCARD_SNAPSHOT_NAME;
979                 for (String entryCardPathItem : utility.getformattedEntryCardPathList()) {
980                     pathToFile(utility, entryCardPathItem, entryCardPath, true);
981                 }
982             }
983 
984             if (!utility.getPackResPath().isEmpty()) {
985                 pathToFile(utility, utility.getPackResPath(), NULL_DIR_NAME, false);
986             }
987             File file = new File(utility.getPackInfoPath());
988             compressFile(utility, file, NULL_DIR_NAME, false);
989         } catch (BundleException e) {
990             LOG.error("Compressor::compressAppMode compress failed.");
991             throw new BundleException("Compressor::compressAppMode compress failed.");
992         } finally {
993             // delete temp file
994             for (String path : fileList) {
995                 deleteFile(path);
996             }
997             deleteFile(tempPath);
998             deleteFile(hspTempDirPath);
999         }
1000     }
1001 
1002     /**
1003      * compress in app mode for multi project.
1004      *
1005      * @param utility common data
1006      * @throws BundleException FileNotFoundException|IOException.
1007      */
compressAppModeForMultiProject(Utility utility)1008     private void compressAppModeForMultiProject(Utility utility) throws BundleException {
1009         List<String> fileList = new ArrayList<>();
1010         File appOutputFile = new File(utility.getOutPath().trim());
1011         String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR;
1012         String tempSelectedHapPath = appOutputFile.getParentFile().getParent() +File.separator + TEMP_SELECTED_HAP_DIR;
1013         try {
1014             File tempSelectedHapDir = new File(tempSelectedHapPath);
1015             FileUtils.makeDir(tempSelectedHapDir);
1016             File tempHapDir = new File(tempPath);
1017             FileUtils.makeDir(tempHapDir);
1018             // pack app and dispose conflict
1019             // save hap name into list
1020             List<String> seletedHaps = new ArrayList<>();
1021             String finalPackInfoStr = disposeApp(utility, seletedHaps, tempSelectedHapPath);
1022             // pack hap and dispose conflict
1023             finalPackInfoStr = disposeHap(utility, seletedHaps, tempSelectedHapPath, finalPackInfoStr);
1024 
1025             // save final pack.info file
1026             String finalPackInfoPath = tempSelectedHapDir.getPath() + File.separator + PACKINFO_NAME;
1027             writePackInfo(finalPackInfoPath, finalPackInfoStr);
1028             // pack haps
1029             for (String selectedHapName : seletedHaps) {
1030                 String hapPathItem = tempSelectedHapDir.getPath() + File.separator + selectedHapName;
1031                 File hapFile = new File(hapPathItem.trim());
1032                 String hapTempPath = tempHapDir.getPath() + File.separator + hapFile.getName();
1033                 fileList.add(hapTempPath);
1034                 compressPackinfoIntoHap(hapPathItem, hapTempPath, finalPackInfoPath);
1035             }
1036             // check hap is valid
1037             if (!checkHapIsValid(fileList, false)) {
1038                 String errMsg = "Compressor::compressAppModeForMultiProject There are some " +
1039                         "haps with different version code or duplicated moduleName or packageName.";
1040                 throw new BundleException(errMsg);
1041             }
1042             for (String hapPath : fileList) {
1043                 pathToFile(utility, hapPath, NULL_DIR_NAME, false);
1044             }
1045             File file = new File(finalPackInfoPath);
1046             compressFile(utility, file, NULL_DIR_NAME, false);
1047         } catch (BundleException | IOException exception) {
1048             String errMsg = "Compressor::compressAppModeForMultiProject file failed.";
1049             LOG.error(errMsg);
1050             throw new BundleException(errMsg);
1051         } finally {
1052             deleteFile(tempPath);
1053             deleteFile(tempSelectedHapPath);
1054         }
1055     }
1056 
1057     /**
1058      * pack hap in app to selectedHaps
1059      *
1060      * @param utility is common data
1061      * @param seletedHaps is seleted haps should be pack into app
1062      * @return the final pack.info string after dispose app
1063      * @throws BundleException FileNotFoundException|IOException.
1064      */
disposeApp(Utility utility, List<String> seletedHaps, String tempDir)1065     private static String disposeApp(Utility utility, List<String> seletedHaps,
1066         String tempDir) throws BundleException {
1067         // dispose app conflict
1068         if (utility.getFormattedAppList().isEmpty()) {
1069             return "";
1070         }
1071         String finalAppPackInfo = "";
1072         try {
1073             for (String appPath : utility.getFormattedAppList()) {
1074                 // select hap in app
1075                 finalAppPackInfo = selectHapInApp(appPath, seletedHaps, tempDir, finalAppPackInfo);
1076             }
1077         } catch (BundleException | IOException e) {
1078             String errMsg = "Compressor:disposeApp disposeApp failed.";
1079             LOG.error(errMsg);
1080             throw new BundleException(errMsg);
1081         }
1082         return finalAppPackInfo;
1083     }
1084 
1085     /**
1086      * select hap from app file list
1087      *
1088      * @param appPath is common data
1089      * @param selectedHaps is list of packInfos
1090      * @throws BundleException FileNotFoundException|IOException.
1091      */
selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, String finalAppPackInfo)1092     private static String selectHapInApp(String appPath, List<String> selectedHaps, String tempDir,
1093                                          String finalAppPackInfo) throws BundleException, IOException {
1094         List<String> selectedHapsInApp = new ArrayList<>();
1095         // classify hap in app
1096         copyHapAndHspFromApp(appPath, selectedHapsInApp, selectedHaps, tempDir);
1097         // rebuild pack.info
1098         String packInfoStr = FileUtils.getJsonInZips(new File(appPath), PACKINFO_NAME);
1099         if (packInfoStr.isEmpty()) {
1100             String errorMsg = "Compressor:selectHapInApp failed, app has no pack.info.";
1101             LOG.error(errorMsg);
1102             throw new BundleException(errorMsg);
1103         }
1104         if (finalAppPackInfo.isEmpty()) {
1105             finalAppPackInfo = packInfoStr;
1106             return finalAppPackInfo;
1107         }
1108         // read selected module in temp hap
1109         HashMap<String, String> packagePair = new HashMap<>();
1110         for (String hapName : selectedHapsInApp) {
1111             packagePair.put(hapName, readModlueNameFromHap(tempDir + File.separator + hapName));
1112         }
1113         return ModuleJsonUtil.mergeTwoPackInfoByPackagePair(finalAppPackInfo, packInfoStr, packagePair);
1114     }
1115 
1116     /**
1117      * copy hap from app file
1118      *
1119      * @param appPath is common data
1120      * @param selectedHapsInApp is list of haps and hsps selected in app file
1121      * @param selectedHaps is the list of haps and hsps selected in input
1122      * @throws BundleException FileNotFoundException|IOException.
1123      */
copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, String tempDir)1124     private static void copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps,
1125         String tempDir) throws BundleException {
1126         ZipInputStream zipInput = null;
1127         ZipFile zipFile = null;
1128         OutputStream outputStream = null;
1129         InputStream inputStream = null;
1130         ZipEntry zipEntry = null;
1131         try {
1132             zipInput = new ZipInputStream(new FileInputStream(appPath));
1133             zipFile = new ZipFile(appPath);
1134             while ((zipEntry = zipInput.getNextEntry()) != null) {
1135                 File file = null;
1136                 if (!zipEntry.getName().endsWith(HAP_SUFFIX) && !zipEntry.getName().endsWith(HSP_SUFFIX)) {
1137                     continue;
1138                 }
1139                 // copy duplicated hap to duplicated dir and get moduleName of duplicated hap
1140                 if (selectedHaps.contains(zipEntry.getName())) {
1141                     LOG.error("Compressor::copyHapFromApp file duplicated, file is " + zipEntry.getName() + ".");
1142                     throw new BundleException("Compressor::copyHapFromApp file duplicated, file is "
1143                             + zipEntry.getName() + ".");
1144                 } else {
1145                     // copy selectedHap to tempDir
1146                     file = new File(tempDir + File.separator + zipEntry.getName());
1147                     selectedHaps.add(file.getName());
1148                     selectedHapsInApp.add(file.getName());
1149                 }
1150                 outputStream = new FileOutputStream(file);
1151                 inputStream = zipFile.getInputStream(zipEntry);
1152                 int len;
1153                 while ((len = inputStream.read()) != -1) {
1154                     outputStream.write(len);
1155                 }
1156                 outputStream.close();
1157                 inputStream.close();
1158             }
1159         } catch (IOException e) {
1160             String errMsg = "Compressor:copyHapFromApp app path not found.";
1161             LOG.error(errMsg);
1162             throw new BundleException(errMsg);
1163         } finally {
1164             Utility.closeStream(zipInput);
1165             Utility.closeStream(zipFile);
1166             Utility.closeStream(outputStream);
1167             Utility.closeStream(inputStream);
1168         }
1169     }
1170 
1171     /**
1172      * read moduleName in hap
1173      *
1174      * @param hapPath is path of hap file
1175      * @throws BundleException FileNotFoundException|IOException.
1176      */
readModlueNameFromHap(String hapPath)1177     private static String readModlueNameFromHap(String hapPath) throws BundleException {
1178         String moduleName = "";
1179         File hapFile = new File(hapPath);
1180         if (isModuleHap(hapPath)) {
1181             String jsonString = FileUtils.getJsonInZips(hapFile, MODULE_JSON);
1182             moduleName = ModuleJsonUtil.parseStageModuleName(jsonString);
1183         } else {
1184             String jsonString = FileUtils.getJsonInZips(hapFile, CONFIG_JSON);
1185             moduleName = ModuleJsonUtil.parseFaModuleName(jsonString);
1186         }
1187         return moduleName;
1188     }
1189 
1190     /**
1191      * dispose input of hap
1192      *
1193      * @param utility is common data
1194      * @param seletedHaps is the selected  haps of all input
1195      * @param tempDir is the path of temp directory
1196      * @param finalPackInfoStr is the pack.info of the final app
1197      * @throws BundleException FileNotFoundException|IOException.
1198      */
disposeHap(Utility utility, List<String> seletedHaps, String tempDir, String finalPackInfoStr)1199     private static String disposeHap(Utility utility, List<String> seletedHaps, String tempDir,
1200                                    String finalPackInfoStr) throws BundleException, IOException {
1201         // dispose hap conflict
1202         if (utility.getFormattedHapList().isEmpty()) {
1203             return finalPackInfoStr;
1204         }
1205         for (String hapPath : utility.getFormattedHapList()) {
1206             if (seletedHaps.contains(new File(hapPath).getName())) {
1207                 LOG.error("Compressor::disposeHap file duplicated, file is " + new File(hapPath).getName() + ".");
1208                 throw new BundleException("Compressor::disposeHap file duplicated, file is "
1209                         + new File(hapPath).getName() + ".");
1210             }
1211             File hapFile = new File(hapPath);
1212             seletedHaps.add(hapFile.getName());
1213             // copy hap to tempDir
1214             FileUtils.copyFile(hapFile, new File((tempDir +File.separator + hapFile.getName())));
1215             String packInfo = FileUtils.getJsonInZips(hapFile, PACKINFO_NAME);
1216 
1217             if (packInfo.isEmpty()) {
1218                 String errMsg = "Compressor::disposeHap failed, hap has no pack.info.";
1219                 LOG.error(errMsg);
1220                 throw new BundleException(errMsg);
1221             }
1222             if (finalPackInfoStr.isEmpty()) {
1223                 finalPackInfoStr = packInfo;
1224             } else {
1225                 finalPackInfoStr = ModuleJsonUtil.mergeTwoPackInfo(finalPackInfoStr, packInfo);
1226             }
1227         }
1228         return finalPackInfoStr;
1229     }
1230 
1231     /**
1232      * write string to pack.info
1233      *
1234      * @param filePath pack.info file path
1235      * @param packInfoStr is the string of pack.info
1236      * @throws BundleException FileNotFoundException|IOException.
1237      */
writePackInfo(String filePath, String packInfoStr)1238     private void writePackInfo(String filePath, String packInfoStr) throws BundleException, IOException {
1239         FileWriter fwriter = null;
1240         try {
1241             fwriter = new FileWriter(filePath);
1242             fwriter.write(packInfoStr);
1243         } catch (IOException e) {
1244             String errMsg = "Compressor:writePackInfo failed.";
1245             LOG.error(errMsg);
1246             throw new BundleException(errMsg);
1247         } finally {
1248             if (fwriter != null) {
1249                 fwriter.flush();
1250                 fwriter.close();
1251             }
1252         }
1253     }
1254 
copy(InputStream input, OutputStream output)1255     private void copy(InputStream input, OutputStream output) throws IOException {
1256         int bytesRead;
1257         byte[] data = new byte[BUFFER_SIZE];
1258         while ((bytesRead = input.read(data, 0, BUFFER_SIZE)) != -1) {
1259             output.write(data, 0, bytesRead);
1260         }
1261     }
1262 
compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo)1263     private void compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo)
1264             throws IOException, BundleException {
1265         ZipFile sourceHapFile = new ZipFile(hapPathItem);
1266         ZipOutputStream append = new ZipOutputStream(new FileOutputStream(outPathString));
1267         try {
1268             Enumeration<? extends ZipEntry> entries = sourceHapFile.entries();
1269             while (entries.hasMoreElements()) {
1270                 ZipEntry zipEntry = entries.nextElement();
1271                 if (PACKINFO_NAME.equals(zipEntry.getName())) {
1272                     continue;
1273                 }
1274                 ZipEntry newEntry = new ZipEntry(zipEntry);
1275                 append.putNextEntry(newEntry);
1276                 if (!zipEntry.isDirectory()) {
1277                     copy(sourceHapFile.getInputStream(zipEntry), append);
1278                 }
1279                 append.closeEntry();
1280             }
1281             File packInfoFile = new File(packInfo);
1282             ZipEntry zipEntry = getStoredZipEntry(packInfoFile, PACKINFO_NAME);
1283             append.putNextEntry(zipEntry);
1284             FileInputStream in = new FileInputStream(packInfoFile);
1285             try {
1286                 byte[] buf = new byte[BUFFER_SIZE];
1287                 int len;
1288                 while ((len = in.read(buf)) != -1) {
1289                     append.write(buf, 0, len);
1290                 }
1291             } finally {
1292                 in.close();
1293             }
1294             append.closeEntry();
1295         } catch (IOException exception) {
1296             LOG.error("Compressor::compressPackinfoIntoHap io exception.");
1297             throw new BundleException("Compressor::compressPackinfoIntoHap io exception.");
1298         } finally {
1299             sourceHapFile.close();
1300             append.close();
1301         }
1302     }
1303 
1304     /**
1305      * delete file
1306      *
1307      * @param path file path which will be deleted
1308      */
deleteFile(final String path)1309     private static void deleteFile(final String path) {
1310         File file = new File(path);
1311         if (file.exists()) {
1312             if (file.isDirectory()) {
1313                 File[] files = file.listFiles();
1314                 for (int i = 0; i < files.length; i++) {
1315                     deleteFile(files[i].toString());
1316                 }
1317             }
1318             file.delete();
1319         }
1320     }
1321 
1322     /**
1323      * compress in res mode.
1324      *
1325      * @param utility common data
1326      * @throws BundleException FileNotFoundException|IOException.
1327      */
compressPackResMode(Utility utility)1328     private void compressPackResMode(Utility utility) throws BundleException {
1329         if (!utility.getPackInfoPath().isEmpty()) {
1330             File file = new File(utility.getPackInfoPath());
1331             infoSpecialProcess(utility, file);
1332         }
1333         if (!utility.getEntryCardPath().isEmpty()) {
1334             getFileList(utility.getEntryCardPath());
1335             if (!mIsContain2x2EntryCard) {
1336                 LOG.error("Compressor::compressPackResMode No 2x2 resource file exists.");
1337                 throw new BundleException("No 2x2 resource file exists.");
1338             }
1339             for (String fileName : fileNameList) {
1340                 if (fileName.endsWith(PNG_SUFFIX) || fileName.endsWith(UPPERCASE_PNG_SUFFIX)) {
1341                     String fName = fileName.trim();
1342                     String[] temp = fName.replace("\\", "/").split("/");
1343                     if (temp.length < 4) {
1344                         LOG.error("Compressor::compressPackResMode the hap file path is invalid, length: "
1345                             + temp.length + ".");
1346                         continue;
1347                     }
1348                     String moduleName = temp[temp.length - 4];
1349                     if (!isModelName(moduleName)) {
1350                         String errMessage = "Compressor::compressProcess compress pack.res failed, " +
1351                                 "please check the related configurations in module " + moduleName + ".";
1352                         LOG.error(errMessage);
1353                         throw new BundleException(errMessage);
1354                     }
1355                     String fileLanguageCountryName = temp[temp.length - 3];
1356                     if (!isThirdLevelDirectoryNameValid(fileLanguageCountryName)) {
1357                         LOG.error("Compressor::compressProcess compress failed third level directory name: "
1358                             + fileLanguageCountryName + " is invalid, please check it with reference to this example: "
1359                             + "zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi.");
1360                         throw new BundleException("Compress failed third level directory name Error.");
1361                     }
1362                     String filePicturingName = temp[temp.length - 1];
1363                     if (!isPicturing(filePicturingName, utility)) {
1364                         LOG.error("Compressor::compressProcess Compress pack.res failed, Invalid resource file" +
1365                             " name: " + filePicturingName + ", correct format example is formName-2x2.png.");
1366                         throw new BundleException("Compress pack.res failed, Invalid resource file name: "
1367                             + filePicturingName + ", correct format example is formName-2x2.png.");
1368                     }
1369 
1370                 } else {
1371                     LOG.error("Compressor::compressProcess compress failed No image in PNG format is found.");
1372                     throw new BundleException("Compress pack.res failed, compress failed No image in"
1373                         + " PNG format is found.");
1374                 }
1375             }
1376             pathToFile(utility, utility.getEntryCardPath(), ENTRYCARD_NAME, false);
1377         }
1378     }
1379 
1380     /**
1381      * Check whether modelname meets specifications.
1382      *
1383      * @param name modelName
1384      * @return false and true
1385      */
isModelName(String name)1386     private boolean isModelName(String name) {
1387         for (String listName : list) {
1388             if (name.equals(listName)) {
1389                 return true;
1390             }
1391         }
1392         return false;
1393     }
1394 
isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName)1395     private boolean isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName) {
1396         if (thirdLevelDirectoryName == null || thirdLevelDirectoryName.isEmpty()) {
1397             return false;
1398         }
1399         if (ENTRYCARD_BASE_NAME.equals(thirdLevelDirectoryName)) {
1400             return true;
1401         }
1402         // example: zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi
1403         int firstDelimiterIndex = thirdLevelDirectoryName.indexOf("_");
1404         if (firstDelimiterIndex < 0) {
1405             return false;
1406         }
1407         String language = thirdLevelDirectoryName.substring(0, firstDelimiterIndex);
1408         int secondDelimiterIndex = thirdLevelDirectoryName.indexOf("_", firstDelimiterIndex + 1);
1409         if (secondDelimiterIndex < 0) {
1410             return false;
1411         }
1412         String script = thirdLevelDirectoryName.substring(firstDelimiterIndex + 1, secondDelimiterIndex);
1413         int thirdDelimiterIndex = thirdLevelDirectoryName.indexOf("-", secondDelimiterIndex + 1);
1414         if (thirdDelimiterIndex < 0) {
1415             return false;
1416         }
1417         String country = thirdLevelDirectoryName.substring(secondDelimiterIndex + 1, thirdDelimiterIndex);
1418         if (!checkLanguage(language) || !checkScript(script) || !checkCountry(country)) {
1419             return false;
1420         }
1421         int forthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", thirdDelimiterIndex + 1);
1422         if (forthDelimiterIndex < 0) {
1423             return false;
1424         }
1425         String orientation = thirdLevelDirectoryName.substring(thirdDelimiterIndex + 1, forthDelimiterIndex);
1426         int fifthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", forthDelimiterIndex + 1);
1427         if (fifthDelimiterIndex < 0) {
1428             return false;
1429         }
1430         String deviceType = thirdLevelDirectoryName.substring(forthDelimiterIndex + 1, fifthDelimiterIndex);
1431         if (!checkOrientation(orientation) || !checkDeviceType(deviceType)) {
1432             return false;
1433         }
1434         int sixthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", fifthDelimiterIndex + 1);
1435         if (sixthDelimiterIndex < 0) {
1436             String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1,
1437                     thirdLevelDirectoryName.length());
1438             return checkScreenDensity(screenDensity);
1439         } else {
1440             String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, sixthDelimiterIndex);
1441             if (!checkScreenDensity(screenDensity)) {
1442                 return false;
1443             }
1444         }
1445         int seventhDelimiterIndex = thirdLevelDirectoryName.indexOf("-", sixthDelimiterIndex + 1);
1446         if (seventhDelimiterIndex < 0) {
1447             String tmp = thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, thirdLevelDirectoryName.length());
1448             return checkColorModeOrShape(tmp);
1449         }
1450         if (!checkColorMode(thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, seventhDelimiterIndex))) {
1451             return false;
1452         }
1453         String shape = thirdLevelDirectoryName.substring(seventhDelimiterIndex + 1, thirdLevelDirectoryName.length());
1454         return checkShape(shape);
1455     }
1456 
checkLanguage(String language)1457     private boolean checkLanguage(String language) {
1458         if (!Pattern.compile(REGEX_LANGUAGE).matcher(language).matches()) {
1459             LOG.error("Compressor::compressProcess language " + language + " is not in ISO 639-1 list.");
1460             return false;
1461         }
1462         return true;
1463     }
1464 
checkScript(String script)1465     private boolean checkScript(String script) {
1466         if (!Pattern.compile(REGEX_SCRIPT).matcher(script).matches()) {
1467             LOG.error("Compressor::compressProcess script " + script + " is not in ISO 15924 list.");
1468             return false;
1469         }
1470         return true;
1471     }
1472 
checkCountry(String country)1473     private boolean checkCountry(String country) {
1474         if (!Pattern.compile(REGEX_COUNTRY).matcher(country).matches()) {
1475             LOG.error("Compressor::compressProcess country " + country + " is not in ISO 3166-1 list.");
1476             return false;
1477         }
1478         return true;
1479     }
1480 
checkOrientation(String orientation)1481     private boolean checkOrientation(String orientation) {
1482         if (!Pattern.compile(REGEX_ORIENTATION).matcher(orientation).matches()) {
1483             LOG.error("Compressor::compressProcess orientation " + orientation +
1484                 " is not in {vertical, horizontal} list.");
1485             return false;
1486         }
1487         return true;
1488     }
1489 
checkDeviceType(String deviceType)1490     private boolean checkDeviceType(String deviceType) {
1491         if (!Pattern.compile(REGEX_DEVICE_TYPE).matcher(deviceType).matches()) {
1492             LOG.error("Compressor::compressProcess deviceType " + deviceType +
1493                     " is not in {phone, tablet, car, tv, wearable, liteWearable, 2in1} list.");
1494             return false;
1495         }
1496         return true;
1497     }
1498 
checkScreenDensity(String screenDensity)1499     private boolean checkScreenDensity(String screenDensity) {
1500         if (!Pattern.compile(REGEX_SCREEN_DENSITY).matcher(screenDensity).matches()) {
1501             LOG.error("Compressor::compressProcess screenDensity " + screenDensity +
1502                     " is not in {sdpi, mdpi, ldpi, xldpi, xxldpi} list.");
1503             return false;
1504         }
1505         return true;
1506     }
1507 
checkColorMode(String colorMode)1508     private boolean checkColorMode(String colorMode) {
1509         if (!Pattern.compile(REGEX_COLOR_MODE).matcher(colorMode).matches()) {
1510             LOG.error("Compressor::compressProcess colorMode " + colorMode +
1511                     " is not in {light, dark} list.");
1512             return false;
1513         }
1514         return true;
1515     }
1516 
checkColorModeOrShape(String tmp)1517     private boolean checkColorModeOrShape(String tmp) {
1518         if (Pattern.compile(REGEX_COLOR_MODE).matcher(tmp).matches() ||
1519             Pattern.compile(REGEX_SHAPE).matcher(tmp).matches()) {
1520             return true;
1521         }
1522         LOG.error("Compressor::compressProcess " + tmp +
1523                 " is neither in colorMode list {light, dark} nor in shape list {circle}.");
1524         return false;
1525     }
1526 
checkShape(String shape)1527     private boolean checkShape(String shape) {
1528         if (Pattern.compile(REGEX_SHAPE).matcher(shape).matches()) {
1529             return true;
1530         }
1531         LOG.error("Compressor::compressProcess shape" + shape + " is not in {circle} list.");
1532         return false;
1533     }
1534 
1535     /**
1536      * Check whether picturingName meets specifications.
1537      *
1538      * @param name picturingName
1539      * @param utility common data
1540      * @return false and true
1541      */
isPicturing(String name, Utility utility)1542     private boolean isPicturing(String name, Utility utility) {
1543         boolean isSpecifications = false;
1544         if (name == null || name.isEmpty()) {
1545             return isSpecifications;
1546         }
1547         if (!name.endsWith(PNG_SUFFIX) && !name.endsWith(UPPERCASE_PNG_SUFFIX)) {
1548             LOG.error("isPicturing: the suffix is not .png or .PNG.");
1549             return false;
1550         }
1551         int delimiterIndex = name.lastIndexOf("-");
1552         if (delimiterIndex < 0) {
1553             LOG.error("isPicturing: the entry card naming format is invalid and should be separated by '-'.");
1554             return false;
1555         }
1556         String formName = name.substring(0, delimiterIndex);
1557         if (!utility.getFormNameList().contains(formName)) {
1558             LOG.error("isPicturing: the name is not same as formName, name: " + formName + " is not in " +
1559                 utility.getFormNameList().toString() + ".");
1560             return false;
1561         }
1562         String dimension = name.substring(delimiterIndex + 1, name.lastIndexOf("."));
1563         if (!supportDimensionsList.contains(dimension)) {
1564             LOG.error("isPicturing: the dimension: " + dimension + " is invalid, is not in the following list: "
1565                 + "{1X2, 2X2, 2X4, 4X4}.");
1566             return false;
1567         }
1568         return true;
1569     }
1570 
getFileList(final String filePath)1571     private void getFileList(final String filePath) throws BundleException {
1572         File file = new File(filePath);
1573         if (!file.exists()) {
1574             LOG.error("getFileList: file is not exists.");
1575             return;
1576         }
1577         File[] files = file.listFiles();
1578         if (files == null) {
1579             LOG.error("getFileList: no file in this file path.");
1580             return;
1581         }
1582         for (File f : files) {
1583             try {
1584                 if (f.isFile()) {
1585                     if (f.getName().endsWith(".DS_Store")) {
1586                         deleteFile(f.getCanonicalPath());
1587                         continue;
1588                     }
1589                     String snapshotDirectoryName = f.getParentFile().getName();
1590                     if (!ENTRYCARD_SNAPSHOT_NAME.equals(snapshotDirectoryName)) {
1591                         LOG.error("The level-4 directory of EntryCard must be named as snapshot" +
1592                             ", but current is: " + snapshotDirectoryName + ".");
1593                         throw new BundleException("The level-4 directory of EntryCard must be named as snapshot" +
1594                             ", but current is: " + snapshotDirectoryName + ".");
1595                     }
1596                     checkContain2x2EntryCard(f.getParentFile());
1597                     fileNameList.add(f.getCanonicalPath());
1598                 } else if (f.isDirectory()) {
1599                     getFileList(f.getCanonicalPath());
1600                 } else {
1601                     LOG.error("It's not file or directory.");
1602                 }
1603             } catch (IOException msg) {
1604                 LOG.error("IOException error: " + msg.getMessage());
1605                 return;
1606             }
1607         }
1608     }
1609 
checkContain2x2EntryCard(final File snapshotDirectory)1610     private void checkContain2x2EntryCard(final File snapshotDirectory) throws IOException, BundleException {
1611         if (!snapshotDirectory.exists()) {
1612             LOG.error("checkContain2x2EntryCard: file is not exist: " + snapshotDirectory.getName() + ".");
1613             throw new BundleException("checkContain2x2EntryCard: file is not exist.");
1614         }
1615         File[] files = snapshotDirectory.listFiles();
1616         if (files == null) {
1617             LOG.error("checkContain2x2EntryCard: no file in this file path.");
1618             throw new BundleException("checkContain2x2EntryCard: no file in this file path.");
1619         }
1620 
1621         for (File entryCardFile : files) {
1622             if (entryCardFile.isFile() && entryCardFile.getName().contains(PIC_2X2)) {
1623                 return;
1624             }
1625         }
1626         mIsContain2x2EntryCard = false;
1627         LOG.error("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in "
1628             + snapshotDirectory.getCanonicalPath() + ".");
1629         throw new BundleException("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in "
1630             + snapshotDirectory.getCanonicalPath() + ".");
1631     }
1632 
1633     /**
1634      * compress file or directory.
1635      *
1636      * @param utility       common data
1637      * @param path          create new file by path
1638      * @param baseDir       base path for file
1639      * @param isCompression if need compression
1640      * @throws BundleException FileNotFoundException|IOException.
1641      */
pathToFile(Utility utility, String path, String baseDir, boolean isCompression)1642     private void pathToFile(Utility utility, String path, String baseDir, boolean isCompression)
1643             throws BundleException {
1644         if (path.isEmpty()) {
1645             return;
1646         }
1647         File fileItem = new File(path);
1648         if (fileItem.isDirectory()) {
1649             File[] files = fileItem.listFiles();
1650             if (files == null) {
1651                 return;
1652             }
1653             for (File file : files) {
1654                 if (file.isDirectory()) {
1655                     compressDirectory(utility, file, baseDir, isCompression);
1656                 } else if (isCompression) {
1657                     compressFile(utility, file, baseDir, isCompression);
1658                 } else {
1659                     compressFile(utility, file, baseDir, isCompression);
1660                 }
1661             }
1662         } else {
1663             compressFile(utility, fileItem, baseDir, isCompression);
1664         }
1665     }
1666 
1667     /**
1668      * compress file directory.
1669      *
1670      * @param utility       common data
1671      * @param dir           file directory
1672      * @param baseDir       current directory name
1673      * @param isCompression if need compression
1674      * @throws BundleException FileNotFoundException|IOException.
1675      */
compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)1676     private void compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)
1677             throws BundleException {
1678         File[] files = dir.listFiles();
1679         if (files == null) {
1680             return;
1681         }
1682         for (File file : files) {
1683             if (file.isDirectory()) {
1684                 compressDirectory(utility, file, baseDir + dir.getName() + File.separator, isCompression);
1685             } else {
1686                 compressFile(utility, file, baseDir + dir.getName() + File.separator, isCompression);
1687             }
1688         }
1689     }
1690 
1691     /**
1692      * compress pack.info
1693      *
1694      * @param sourceFile source
1695      * @param zipOutputStream ZipOutputStream
1696      * @param name filename
1697      * @param KeepDirStructure Empty File
1698      */
compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure)1699     private void compress(File sourceFile, ZipOutputStream zipOutputStream, String name,
1700                                 boolean KeepDirStructure) {
1701         FileInputStream in = null;
1702         try {
1703             byte[] buf = new byte[BUFFER_SIZE];
1704             if (sourceFile.isFile()) {
1705                 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name);
1706                 zipOutputStream.putNextEntry(zipEntry);
1707                 in = new FileInputStream(sourceFile);
1708                 int len;
1709                 while ((len = in.read(buf)) != -1) {
1710                     zipOutputStream.write(buf, 0, len);
1711                 }
1712                 zipOutputStream.closeEntry();
1713             } else {
1714                 File[] listFiles = sourceFile.listFiles();
1715                 if (listFiles == null || listFiles.length == 0) {
1716                     if (KeepDirStructure) {
1717                         if (!name.isEmpty()) {
1718                             ZipEntry zipEntry = getStoredZipEntry(sourceFile, name + "/");
1719                             zipOutputStream.putNextEntry(zipEntry);
1720                         } else {
1721                             ZipEntry zipEntry = getStoredZipEntry(sourceFile, name);
1722                             zipOutputStream.putNextEntry(zipEntry);
1723                         }
1724                         zipOutputStream.closeEntry();
1725                     }
1726                 } else {
1727                     for (File file : listFiles) {
1728                         if (KeepDirStructure) {
1729                             isNameEmpty(zipOutputStream, name, KeepDirStructure, file);
1730                         } else {
1731                             compress(file, zipOutputStream, file.getName(), KeepDirStructure);
1732                         }
1733                     }
1734                 }
1735             }
1736         } catch (FileNotFoundException ignored) {
1737             LOG.error("Compressor::compressFile file not found exception.");
1738         } catch (IOException exception) {
1739             LOG.error("Compressor::compressFile io exception: " + exception.getMessage());
1740         } catch (BundleException bundleException) {
1741             LOG.error("Compressor::compressFile bundle exception" + bundleException.getMessage());
1742         } finally {
1743             Utility.closeStream(in);
1744         }
1745     }
1746 
getStoredZipEntry(File sourceFile, String name)1747     private ZipEntry getStoredZipEntry(File sourceFile, String name) throws BundleException {
1748         ZipEntry zipEntry = new ZipEntry(name);
1749         zipEntry.setMethod(ZipEntry.STORED);
1750         zipEntry.setCompressedSize(sourceFile.length());
1751         zipEntry.setSize(sourceFile.length());
1752         CRC32 crc = getCrcFromFile(sourceFile);
1753         zipEntry.setCrc(crc.getValue());
1754         FileTime fileTime = FileTime.fromMillis(FILE_TIME);
1755         zipEntry.setLastAccessTime(fileTime);
1756         zipEntry.setLastModifiedTime(fileTime);
1757         return zipEntry;
1758     }
1759 
getCrcFromFile(File file)1760     private CRC32 getCrcFromFile(File file) throws BundleException {
1761         FileInputStream fileInputStream = null;
1762         CRC32 crc = new CRC32();
1763         try {
1764             fileInputStream = new FileInputStream(file);
1765             byte[] buffer = new byte[BUFFER_SIZE];
1766 
1767             int count = fileInputStream.read(buffer);
1768             while (count > 0) {
1769                 crc.update(buffer, 0, count);
1770                 count = fileInputStream.read(buffer);
1771             }
1772         } catch (FileNotFoundException ignored) {
1773             LOG.error("Uncompressor::getCrcFromFile file not found exception.");
1774             throw new BundleException("Get Crc from file failed.");
1775         } catch (IOException exception) {
1776             LOG.error("Uncompressor::getCrcFromFile io exception: " + exception.getMessage());
1777             throw new BundleException("Get Crc from file failed.");
1778         } finally {
1779             Utility.closeStream(fileInputStream);
1780         }
1781         return crc;
1782     }
1783 
1784     /**
1785      * isNameEmpty
1786      *
1787      * @param zipOutputStream ZipOutputStream
1788      * @param name filename
1789      * @param KeepDirStructure KeepDirStructure
1790      * @param file file
1791      */
isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure, File file)1792     private void isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean KeepDirStructure, File file) {
1793         if (!name.isEmpty()) {
1794             compress(file, zipOutputStream, name + "/" + file.getName(), KeepDirStructure);
1795         } else {
1796             compress(file, zipOutputStream, file.getName(), KeepDirStructure);
1797         }
1798     }
1799 
1800     /**
1801      * compress process.
1802      *
1803      * @param utility       common data
1804      * @param srcFile       source file to zip
1805      * @param baseDir       current directory name of file
1806      * @param isCompression if need compression
1807      * @throws BundleException FileNotFoundException|IOException.
1808      */
compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)1809     private void compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)
1810             throws BundleException {
1811         BufferedInputStream bufferedInputStream = null;
1812         FileInputStream fileInputStream = null;
1813         try {
1814             String entryName = (baseDir + srcFile.getName()).replace(File.separator, LINUX_FILE_SEPARATOR);
1815             ZipEntry zipEntry = new ZipEntry(entryName);
1816             if (!entryName.contains(RAW_FILE_PATH) &&
1817                     srcFile.getName().toLowerCase(Locale.ENGLISH).endsWith(JSON_SUFFIX)) {
1818                 zipEntry.setMethod(ZipEntry.STORED);
1819                 jsonSpecialProcess(utility, srcFile, zipEntry);
1820                 return;
1821             }
1822 
1823             if (isCompression) {
1824                 zipEntry.setMethod(ZipEntry.DEFLATED);
1825             } else {
1826                 zipEntry.setMethod(ZipEntry.STORED);
1827 
1828                 // update size
1829                 zipEntry.setCompressedSize(srcFile.length());
1830                 zipEntry.setSize(srcFile.length());
1831 
1832                 // update crc
1833                 CRC32 crc = getCrcFromFile(utility, srcFile);
1834                 zipEntry.setCrc(crc.getValue());
1835             }
1836 
1837             // update fileTime
1838             FileTime fileTime = FileTime.fromMillis(FILE_TIME);
1839             zipEntry.setLastAccessTime(fileTime);
1840             zipEntry.setLastModifiedTime(fileTime);
1841 
1842             zipOut.putNextEntry(zipEntry);
1843             byte[] data = new byte[BUFFER_SIZE];
1844             fileInputStream = new FileInputStream(srcFile);
1845             bufferedInputStream = new BufferedInputStream(fileInputStream);
1846 
1847             int count = bufferedInputStream.read(data);
1848             while (count > 0) {
1849                 zipOut.write(data, 0, count);
1850                 count = bufferedInputStream.read(data);
1851             }
1852         } catch (FileNotFoundException ignored) {
1853             throw new BundleException("CoompressFile failed.");
1854         } catch (IOException exception) {
1855             LOG.error("Compressor::compressFile io exception: " + exception.getMessage());
1856             throw new BundleException("CoompressFile failed.");
1857         } finally {
1858             Utility.closeStream(bufferedInputStream);
1859             Utility.closeStream(fileInputStream);
1860         }
1861     }
1862 
1863     /**
1864      * check hap type for pack app.
1865      *
1866      * @param hapPath source file to zip
1867      * @return true is for is stage type and false is for FA type
1868      * @throws BundleException FileNotFoundException|IOException.
1869      */
isModuleHap(String hapPath)1870     public static boolean isModuleHap(String hapPath) throws BundleException {
1871         if (!hapPath.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)) {
1872             return true;
1873         }
1874 
1875         FileInputStream zipInput = null;
1876         ZipInputStream zin = null;
1877         ZipEntry entry = null;
1878         try {
1879             zipInput = new FileInputStream(hapPath);
1880             zin = new ZipInputStream(zipInput);
1881             while ((entry = zin.getNextEntry()) != null) {
1882                 if (MODULE_JSON.equals(entry.getName().toLowerCase())) {
1883                     return true;
1884                 }
1885             }
1886         } catch (IOException exception) {
1887             LOG.error("Compressor::isModuleHap io exception: " + exception.getMessage());
1888             throw new BundleException("Compressor::isModuleHap failed.");
1889         } finally {
1890             Utility.closeStream(zipInput);
1891             Utility.closeStream(zin);
1892         }
1893         return false;
1894     }
1895 
1896     /**
1897      * get CRC32 from file.
1898      *
1899      * @param utility common data
1900      * @param file    source file
1901      * @return CRC32
1902      * @throws BundleException FileNotFoundException|IOException.
1903      */
getCrcFromFile(Utility utility, File file)1904     private CRC32 getCrcFromFile(Utility utility, File file) throws BundleException {
1905         FileInputStream fileInputStream = null;
1906         CRC32 crc = new CRC32();
1907         try {
1908             fileInputStream = new FileInputStream(file);
1909             byte[] buffer = new byte[BUFFER_SIZE];
1910 
1911             int count = fileInputStream.read(buffer);
1912             while (count > 0) {
1913                 crc.update(buffer, 0, count);
1914                 count = fileInputStream.read(buffer);
1915             }
1916         } catch (FileNotFoundException ignored) {
1917             throw new BundleException("Get Crc from file failed.");
1918         } catch (IOException exception) {
1919             LOG.error("Compressor::getCrcFromFile io exception: " + exception.getMessage());
1920             throw new BundleException("Get Crc from file failed.");
1921         } finally {
1922             Utility.closeStream(fileInputStream);
1923         }
1924         return crc;
1925     }
1926 
infoSpecialProcess(Utility utility, File srcFile)1927     private void infoSpecialProcess(Utility utility, File srcFile)
1928             throws BundleException {
1929         FileInputStream fileInputStream = null;
1930         BufferedReader bufferedReader = null;
1931         InputStreamReader inputStreamReader = null;
1932 
1933         try {
1934             fileInputStream = new FileInputStream(srcFile);
1935             inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
1936             bufferedReader = new BufferedReader(inputStreamReader);
1937             bufferedReader.mark((int) srcFile.length() + 1);
1938             // parse moduleName from pack.info
1939             parsePackModuleName(bufferedReader, utility);
1940             bufferedReader.reset();
1941             parsePackFormName(bufferedReader, utility);
1942             bufferedReader.reset();
1943             parseDeviceType(bufferedReader, utility);
1944             bufferedReader.reset();
1945 
1946             Pattern pattern = Pattern.compile(System.lineSeparator());
1947             String str = bufferedReader.readLine();
1948             StringBuilder builder = new StringBuilder();
1949             while (str != null) {
1950                 Matcher matcher = pattern.matcher(str.trim());
1951                 String dest = matcher.replaceAll("");
1952                 builder.append(dest);
1953                 str = bufferedReader.readLine();
1954             }
1955         } catch (IOException exception) {
1956             LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage());
1957             throw new BundleException("Json special process failed.");
1958         } finally {
1959             Utility.closeStream(bufferedReader);
1960             Utility.closeStream(inputStreamReader);
1961             Utility.closeStream(fileInputStream);
1962         }
1963     }
1964 
1965     /**
1966      * trim and remove "\r\n" in *.json file.
1967      *
1968      * @param utility common data
1969      * @param srcFile file input
1970      * @param entry   zip file entry
1971      * @throws BundleException FileNotFoundException|IOException.
1972      */
jsonSpecialProcess(Utility utility, File srcFile, ZipEntry entry)1973     private void jsonSpecialProcess(Utility utility, File srcFile, ZipEntry entry)
1974             throws BundleException {
1975         FileInputStream fileInputStream = null;
1976         BufferedReader bufferedReader = null;
1977         InputStreamReader inputStreamReader = null;
1978 
1979         try {
1980             fileInputStream = new FileInputStream(srcFile);
1981             inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
1982             bufferedReader = new BufferedReader(inputStreamReader);
1983             bufferedReader.mark((int) srcFile.length() + 1);
1984             bufferedReader.reset();
1985             String srcName = srcFile.getName().toLowerCase(Locale.ENGLISH);
1986             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
1987             String jsonString = optional.get();
1988             String jsonName = new File(utility.getJsonPath()).getName().toLowerCase(Locale.ENGLISH);
1989             if (CONFIG_JSON.equals(jsonName)) {
1990                 parseCompressNativeLibs(bufferedReader, utility);
1991                 utility.setModuleName(ModuleJsonUtil.parseFaModuleName(jsonString));
1992             } else if (MODULE_JSON.equals(jsonName)) {
1993                 utility.setIsCompressNativeLibs(ModuleJsonUtil.stageIsCompressNativeLibs(jsonString));
1994                 utility.setModuleName(ModuleJsonUtil.parseStageModuleName(jsonString));
1995             } else if (PATCH_JSON.equals(jsonName)) {
1996                 utility.setModuleName(ModuleJsonUtil.parsePatchModuleName(jsonString));
1997             }
1998             bufferedReader.reset();
1999             parseDeviceType(bufferedReader, utility);
2000             bufferedReader.reset();
2001 
2002             Pattern pattern = Pattern.compile(System.lineSeparator());
2003             String str = bufferedReader.readLine();
2004             StringBuilder builder = new StringBuilder();
2005             while (str != null) {
2006                 Matcher matcher = pattern.matcher(str.trim());
2007                 String dest = matcher.replaceAll("");
2008                 builder.append(dest);
2009                 str = bufferedReader.readLine();
2010             }
2011             byte[] trimJson = builder.toString().getBytes(StandardCharsets.UTF_8);
2012 
2013             // update crc
2014             CRC32 crc = new CRC32();
2015             crc.update(trimJson);
2016             entry.setCrc(crc.getValue());
2017 
2018             // update size
2019             entry.setSize(trimJson.length);
2020             entry.setCompressedSize(trimJson.length);
2021 
2022             // update fileTime
2023             FileTime fileTime = FileTime.fromMillis(FILE_TIME);
2024             entry.setLastAccessTime(fileTime);
2025             entry.setLastModifiedTime(fileTime);
2026 
2027             // compress data
2028             zipOut.putNextEntry(entry);
2029             zipOut.write(trimJson);
2030         } catch (IOException exception) {
2031             LOG.error("Compressor::jsonSpecialProcess io exception: " + exception.getMessage());
2032             throw new BundleException("Json special process failed.");
2033         } finally {
2034             Utility.closeStream(bufferedReader);
2035             Utility.closeStream(inputStreamReader);
2036             Utility.closeStream(fileInputStream);
2037         }
2038     }
2039 
2040     /**
2041      * Parse module name from pack.info
2042      *
2043      * @param bufferedReader pack.info buffered Reader
2044      * @param utility        common data
2045      * @throws BundleException IOException
2046      */
parsePackModuleName(BufferedReader bufferedReader, Utility utility)2047     private void parsePackModuleName(BufferedReader bufferedReader, Utility utility) throws BundleException {
2048         String lineStr = null;
2049         try {
2050             while ((lineStr = bufferedReader.readLine()) != null) {
2051                 if (lineStr.contains(DISTRO)) {
2052                     continue;
2053                 }
2054                 if (lineStr.contains(JSON_END)) {
2055                     continue;
2056                 }
2057                 if (lineStr.contains(MODULE_NAME_NEW) || lineStr.contains(MODULE_NAME)) {
2058                     getModuleNameFromString(lineStr, utility);
2059                 }
2060             }
2061         } catch (IOException exception) {
2062             LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage());
2063             throw new BundleException("Parse module name failed.");
2064         }
2065     }
2066 
2067     /**
2068      * Parse Forms name from pack.info
2069      *
2070      * @param bufferedReader pack.info buffered Reader
2071      * @param utility        common data
2072      * @throws BundleException IOException
2073      */
parsePackFormName(BufferedReader bufferedReader, Utility utility)2074     private void parsePackFormName(BufferedReader bufferedReader, Utility utility) throws BundleException {
2075         String lineStr = null;
2076         try {
2077             while ((lineStr = bufferedReader.readLine()) != null) {
2078                 if (lineStr.contains("abilities")) {
2079                     continue;
2080                 }
2081                 if (lineStr.contains(FORMS)) {
2082                     continue;
2083                 }
2084                 if (lineStr.contains(JSON_END)) {
2085                     continue;
2086                 }
2087                 if (lineStr.contains(NAME)) {
2088                     getNameFromString(lineStr, utility);
2089                 }
2090             }
2091         } catch (IOException exception) {
2092             LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage());
2093             throw new BundleException("Parse module name failed.");
2094         }
2095     }
2096 
2097 
2098     /**
2099      * Get name from line string
2100      *
2101      * @param lineStr line string
2102      * @param utility common data
2103      * @throws BundleException StringIndexOutOfBoundsException
2104      */
getNameFromString(String lineStr, Utility utility)2105     private void getNameFromString(String lineStr, Utility utility) throws BundleException {
2106         try {
2107             int endIndex = lineStr.lastIndexOf(SEMICOLON);
2108             if (endIndex <= 0) {
2109                 LOG.error("Compressor::getModuleNameFromString field the json is not standard.");
2110                 throw new BundleException("Parse module name failed, module-name is invalid.");
2111             }
2112             int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1;
2113             String formName = lineStr.substring(startIndex, endIndex);
2114             if (formName == null || formName.isEmpty()) {
2115                 LOG.error("Compressor::getModuleNameFromString field module-name is empty.");
2116                 throw new BundleException("Parse module name failed, module-name is empty.");
2117             }
2118             String[] nameList = formName.split("\\.");
2119             if (nameList.length <= 1) {
2120                 formNamesList.add(formName);
2121                 utility.addFormNameList(formName);
2122             }
2123         } catch (StringIndexOutOfBoundsException exception) {
2124             LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage());
2125             throw new BundleException("Parse module name failed, module-name is invalid.");
2126         }
2127     }
2128 
2129     /**
2130      * Get module name from line string
2131      *
2132      * @param lineStr line string
2133      * @param utility common data
2134      * @throws BundleException StringIndexOutOfBoundsException
2135      */
getModuleNameFromString(String lineStr, Utility utility)2136     private void getModuleNameFromString(String lineStr, Utility utility) throws BundleException {
2137         try {
2138             int endIndex = lineStr.lastIndexOf(SEMICOLON);
2139             if (endIndex <= 0) {
2140                 LOG.error("Compressor::getModuleNameFromString field the json is not standard.");
2141                 throw new BundleException("Parse module name failed, module-name is invalid.");
2142             }
2143             int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1;
2144             String moduleName = lineStr.substring(startIndex, endIndex);
2145             list.add(moduleName);
2146             if (moduleName == null || moduleName.isEmpty()) {
2147                 LOG.error("Compressor::getModuleNameFromString field module-name is empty.");
2148                 throw new BundleException("Parse module name failed, module-name is empty.");
2149             }
2150             utility.setModuleName(moduleName);
2151         } catch (StringIndexOutOfBoundsException exception) {
2152             LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage());
2153             throw new BundleException("Parse module name failed, module-name is invalid.");
2154         }
2155     }
2156 
parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility)2157     private void parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility) throws BundleException {
2158         String lineStr = null;
2159         try {
2160             while ((lineStr = bufferedReader.readLine()) != null) {
2161                 if (lineStr.contains(COMPRESS_NATIVE_LIBS)) {
2162                     if (lineStr.contains(Utility.FALSE_STRING)) {
2163                         utility.setIsCompressNativeLibs(false);
2164                         break;
2165                     }
2166                 }
2167             }
2168         } catch (IOException exception) {
2169             LOG.error("Compressor::parseCompressNativeLibs io exception: " + exception.getMessage());
2170             throw new BundleException("Parse compress native libs failed.");
2171         }
2172     }
2173 
2174     /**
2175      * ZipOutputStream flush, closeEntry and finish.
2176      */
closeZipOutputStream()2177     private void closeZipOutputStream() {
2178         try {
2179             if (zipOut != null) {
2180                 zipOut.flush();
2181             }
2182         } catch (IOException exception) {
2183             LOG.error("Compressor::closeZipOutputStream flush exception " + exception.getMessage());
2184         }
2185         try {
2186             if (zipOut != null) {
2187                 zipOut.closeEntry();
2188             }
2189         } catch (IOException exception) {
2190             LOG.error("Compressor::closeZipOutputStream close entry io exception " + exception.getMessage());
2191         }
2192         try {
2193             if (zipOut != null) {
2194                 zipOut.finish();
2195             }
2196         } catch (IOException exception) {
2197             LOG.error("Compressor::closeZipOutputStream finish exception " + exception.getMessage());
2198         }
2199     }
2200 
2201     /**
2202      * Parse device type from config.json
2203      *
2204      * @param bufferedReader config.json buffered Reader
2205      * @param utility        common data
2206      * @throws BundleException IOException
2207      */
parseDeviceType(BufferedReader bufferedReader, Utility utility)2208     private void parseDeviceType(BufferedReader bufferedReader, Utility utility) throws BundleException {
2209         String lineStr = null;
2210         boolean isDeviceTypeStart = false;
2211         try {
2212             while ((lineStr = bufferedReader.readLine()) != null) {
2213                 if (!isDeviceTypeStart) {
2214                     if (lineStr.contains(DEVICE_TYPE)) {
2215                         isDeviceTypeStart = true;
2216                     }
2217                     continue;
2218                 }
2219                 if (lineStr.contains(JSON_END)) {
2220                     break;
2221                 }
2222                 utility.setDeviceType(lineStr);
2223                 break;
2224             }
2225         } catch (IOException exception) {
2226             LOG.error("Compressor::parseDeviceType io exception: " + exception.getMessage());
2227             throw new BundleException("Parse device type failed.");
2228         }
2229     }
2230 
2231     /**
2232      * check hap and hsp is valid in haps when pack app, check type has bundleName,
2233      * vendor, version, apiVersion moduleName, packageName.
2234      *
2235      * @param fileLists is the list of hapPath.
2236      * @return true is for successful and false is for failed
2237      * @throws BundleException FileNotFoundException|IOException.
2238      */
checkHapIsValid(List<String> fileLists, boolean isSharedApp)2239     private boolean checkHapIsValid(List<String> fileLists, boolean isSharedApp) throws BundleException {
2240         List<HapVerifyInfo> hapVerifyInfos = new ArrayList<>();
2241         for (String hapPath : fileLists) {
2242             if (hapPath.isEmpty()) {
2243                 LOG.error("Compressor::checkHapIsValid input wrong hap file.");
2244                 throw new BundleException("Compressor::checkHapIsValid input wrong hap file.");
2245             }
2246             File srcFile = new File(hapPath);
2247             String fileStr = srcFile.getName();
2248             if (fileStr.isEmpty()) {
2249                 LOG.error("Compressor::checkHapIsValid get file name failed.");
2250                 throw new BundleException("Compressor::checkHapIsValid get file name failed.");
2251             }
2252             if (!fileStr.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)
2253                     && !fileStr.toLowerCase(Locale.ENGLISH).endsWith(HSP_SUFFIX)) {
2254                 LOG.error("Compressor::checkHapIsValid input wrong hap file.");
2255                 throw new BundleException("Compressor::checkHapIsValid input wrong hap file.");
2256             }
2257             if (isModuleHap(hapPath)) {
2258                 hapVerifyInfos.add(parseStageHapVerifyInfo(hapPath));
2259             } else {
2260                 hapVerifyInfos.add(parseFAHapVerifyInfo(hapPath));
2261             }
2262         }
2263         if (isSharedApp) {
2264             boolean res = checkSharedAppIsValid(hapVerifyInfos);
2265             if (!res) {
2266                 return false;
2267             }
2268             if (!isOverlay) {
2269                 return true;
2270             }
2271         } else {
2272             for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
2273                 String bundleType = hapVerifyInfo.getBundleType();
2274                 if (TYPE_SHARED.equals(bundleType)) {
2275                     LOG.error("Compressor::checkHapIsValid shared app should not be included in --hsp-path.");
2276                     return false;
2277                 }
2278             }
2279         }
2280         setAtomicServiceFileSizeLimit(hapVerifyInfos);
2281         if (!HapVerify.checkHapIsValid(hapVerifyInfos)) {
2282             return false;
2283         }
2284         return true;
2285     }
2286 
2287     /**
2288      * parse stage file to hap verify info from hap path.
2289      *
2290      * @param filePath is the hap path
2291      * @return hapVerifyInfo
2292      */
parseStageHapVerifyInfo(String filePath)2293     public static HapVerifyInfo parseStageHapVerifyInfo(String filePath) throws BundleException {
2294         HapVerifyInfo hapVerifyInfo = readStageHapVerifyInfo(filePath);
2295         hapVerifyInfo.setStageModule(true);
2296         ModuleJsonUtil.parseStageHapVerifyInfo(hapVerifyInfo);
2297         hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath));
2298         return hapVerifyInfo;
2299     }
2300 
2301     /**
2302      * set file size limit for each HapVerifyInfo.
2303      *
2304      * @param hapVerifyInfos Indicates hapVerifyInfo list.
2305      */
setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos)2306     public static void setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos) {
2307         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
2308             if (!hapVerifyInfo.getBundleType().equals(ATOMIC_SERVICE)) {
2309                 continue;
2310             }
2311             hapVerifyInfo.setEntrySizeLimit(getEntryModuleSizeLimit());
2312             hapVerifyInfo.setNotEntrySizeLimit(getNotEntryModuleSizeLimit());
2313             hapVerifyInfo.setSumSizeLimit(getSumModuleSizeLimit());
2314         }
2315     }
2316 
2317     /**
2318      * parse fa file to hap verify info from hap path.
2319      *
2320      * @param filePath is the hap path
2321      * @return hapVerifyInfo
2322      */
parseFAHapVerifyInfo(String filePath)2323     public static HapVerifyInfo parseFAHapVerifyInfo(String filePath) throws BundleException {
2324         HapVerifyInfo hapVerifyInfo = readFAHapVerifyInfo(filePath);
2325         hapVerifyInfo.setStageModule(false);
2326         hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath));
2327         ModuleJsonUtil.parseFAHapVerifyInfo(hapVerifyInfo);
2328         return hapVerifyInfo;
2329     }
2330 
2331     /**
2332      * read stage hap verify info from hap file.
2333      *
2334      * @param srcPath source file to zip
2335      * @return HapVerifyInfo of parse result
2336      * @throws BundleException FileNotFoundException|IOException.
2337      */
readStageHapVerifyInfo(String srcPath)2338     public static HapVerifyInfo readStageHapVerifyInfo(String srcPath) throws BundleException {
2339         HapVerifyInfo hapVerifyInfo = new HapVerifyInfo();
2340         ZipFile zipFile = null;
2341         try {
2342             File srcFile = new File(srcPath);
2343             zipFile = new ZipFile(srcFile);
2344             hapVerifyInfo.setResourceMap(FileUtils.getProfileJson(zipFile));
2345             hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(MODULE_JSON, zipFile));
2346         } catch (IOException e) {
2347             LOG.error("FileUtil::parseStageHapVerifyInfo file not available.");
2348             throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available.");
2349         } finally {
2350             Utility.closeStream(zipFile);
2351         }
2352         return hapVerifyInfo;
2353     }
2354 
2355     /**
2356      * read fa hap verify info from hap file.
2357      *
2358      * @param srcPath source file to zip
2359      * @return HapVerifyInfo of parse result
2360      * @throws BundleException FileNotFoundException|IOException.
2361      */
readFAHapVerifyInfo(String srcPath)2362     public static HapVerifyInfo readFAHapVerifyInfo(String srcPath) throws BundleException {
2363         HapVerifyInfo hapVerifyInfo = new HapVerifyInfo();
2364         ZipFile zipFile = null;
2365         try {
2366             File srcFile = new File(srcPath);
2367             zipFile = new ZipFile(srcFile);
2368             hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(CONFIG_JSON, zipFile));
2369         } catch (IOException e) {
2370             LOG.error("FileUtil::parseStageHapVerifyInfo file not available.");
2371             throw new BundleException("FileUtil::parseStageHapVerifyInfo file not available.");
2372         } finally {
2373             Utility.closeStream(zipFile);
2374         }
2375         return hapVerifyInfo;
2376     }
2377 
2378     /**
2379      * compress in hqf mode.
2380      *
2381      * @param utility common data
2382      * @throws BundleException FileNotFoundException|IOException.
2383      */
compressHQFMode(Utility utility)2384     private void compressHQFMode(Utility utility) throws BundleException {
2385         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
2386 
2387         if (!utility.getEtsPath().isEmpty()) {
2388             pathToFile(utility, utility.getEtsPath(), ETS_PATH, false);
2389         }
2390         if (!utility.getLibPath().isEmpty()) {
2391             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, false);
2392         }
2393     }
2394 
2395     /**
2396      * compress in appqf mode.
2397      *
2398      * @param utility common data
2399      * @throws BundleException FileNotFoundException|IOException.
2400      */
compressAPPQFMode(Utility utility)2401     private void compressAPPQFMode(Utility utility) throws BundleException {
2402         List<String> fileList = utility.getFormatedHQFList();
2403         if (!checkHQFIsValid(fileList)) {
2404             LOG.error("checkHQFIsValid failed when pack appqf file.");
2405             throw new BundleException("checkHQFIsValid failed when pack appqf file.");
2406         }
2407         for (String hapPath : fileList) {
2408             pathToFile(utility, hapPath, NULL_DIR_NAME, false);
2409         }
2410     }
2411 
2412     /**
2413      * check input hqf is valid.
2414      *
2415      * @param fileList is input path of hqf files
2416      * @throws BundleException FileNotFoundException|IOException.
2417      */
checkHQFIsValid(List<String> fileList)2418     private boolean checkHQFIsValid(List<String> fileList) throws BundleException {
2419         List<HQFInfo> hqfVerifyInfos = new ArrayList<>();
2420         for (String file : fileList) {
2421             hqfVerifyInfos.add(ModuleJsonUtil.parseHQFInfo(file));
2422         }
2423         if (!HQFVerify.checkHQFIsValid(hqfVerifyInfos)) {
2424             LOG.error("input hqf is invalid.");
2425             return false;
2426         }
2427         return true;
2428     }
2429 
compressHSPMode(Utility utility)2430     private void compressHSPMode(Utility utility) throws BundleException {
2431         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
2432 
2433         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
2434 
2435         if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
2436             String assetsPath = NULL_DIR_NAME;
2437             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
2438         }
2439 
2440         if (!utility.getLibPath().isEmpty()) {
2441             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
2442         }
2443 
2444         if (!utility.getANPath().isEmpty()) {
2445             pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false);
2446         }
2447 
2448         if (!utility.getAPPath().isEmpty()) {
2449             pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false);
2450         }
2451 
2452         if (!utility.getFilePath().isEmpty()) {
2453             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
2454         }
2455 
2456         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
2457             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
2458                     + RESOURCES_DIR_NAME;
2459             if (DEVICE_TYPE_FITNESSWATCH.equals(
2460                     utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim()) ||
2461                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(
2462                             utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim())) {
2463                 resPath = RES_DIR_NAME;
2464             }
2465             pathToFile(utility, utility.getResPath(), resPath, false);
2466         }
2467 
2468         if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
2469             String resourcesPath = RESOURCES_DIR_NAME;
2470             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
2471         }
2472         if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
2473             String jsPath = JS_PATH;
2474             pathToFile(utility, utility.getJsPath(), jsPath, false);
2475         }
2476 
2477         if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
2478             String etsPath = ETS_PATH;
2479             pathToFile(utility, utility.getEtsPath(), etsPath, false);
2480         }
2481 
2482         if (!utility.getRpcidPath().isEmpty()) {
2483             String rpcidPath = NULL_DIR_NAME;
2484             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
2485         }
2486 
2487         if (!utility.getAssetsPath().isEmpty()) {
2488             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
2489         }
2490 
2491         if (!utility.getBinPath().isEmpty()) {
2492             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
2493         }
2494 
2495         if (!utility.getPackInfoPath().isEmpty()) {
2496             pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false);
2497         }
2498 
2499         // pack --dir-list
2500         if (!utility.getFormatedDirList().isEmpty()) {
2501             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
2502                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
2503                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
2504             }
2505         }
2506 
2507         compressHapModeMultiple(utility);
2508     }
2509 
checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos)2510     private static boolean checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
2511         if (hapVerifyInfos.isEmpty()) {
2512             LOG.error("no module included");
2513             return false;
2514         }
2515         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
2516             if (!hapVerifyInfo.getTargetBundleName().isEmpty()) {
2517                 isOverlay = true;
2518                 return true;
2519             }
2520         }
2521         return HapVerify.checkSharedApppIsValid(hapVerifyInfos);
2522     }
2523 }
2524