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