• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package ohos;
17 
18 import java.io.BufferedInputStream;
19 import java.io.BufferedReader;
20 import java.io.BufferedWriter;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.concurrent.ExecutionException;
40 import java.util.Enumeration;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.List;
44 import java.util.Locale;
45 import java.util.Optional;
46 import java.util.concurrent.LinkedBlockingQueue;
47 import java.util.concurrent.ThreadPoolExecutor;
48 import java.util.concurrent.TimeUnit;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51 import java.util.UUID;
52 import java.util.stream.Stream;
53 import java.util.zip.CRC32;
54 import java.util.zip.CheckedOutputStream;
55 import java.util.zip.ZipInputStream;
56 import java.util.zip.ZipEntry;
57 import java.util.zip.ZipFile;
58 import java.util.zip.ZipOutputStream;
59 import com.alibaba.fastjson.JSON;
60 import com.alibaba.fastjson.JSONArray;
61 import com.alibaba.fastjson.JSONException;
62 import com.alibaba.fastjson.JSONObject;
63 import com.alibaba.fastjson.serializer.SerializerFeature;
64 import org.apache.commons.compress.archivers.zip.DefaultBackingStoreSupplier;
65 import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
66 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
67 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
68 import org.apache.commons.compress.parallel.InputStreamSupplier;
69 
70 /**
71  * bundle compressor class, compress file and directory.
72  *
73  */
74 public class Compressor {
75     private static final String HAP_SUFFIX = ".hap";
76     private static final String HSP_SUFFIX = ".hsp";
77     private static final String PNG_SUFFIX = ".png";
78     private static final String UPPERCASE_PNG_SUFFIX = ".PNG";
79     private static final String CONFIG_JSON = "config.json";
80     private static final String MODULE_JSON = "module.json";
81     private static final String PATCH_JSON = "patch.json";
82     private static final String ADDITION_JSON = "addition.json";
83     private static final String PKG_CONTEXT_INFO = "pkgContextInfo.json";
84     private static final String NAME = "name";
85     private static final String NULL_DIR_NAME = "";
86     private static final String RES_DIR_NAME = "res/";
87     private static final String RESOURCES_DIR_NAME = "resources/";
88     private static final String LIBS_DIR_NAME = "libs/";
89     private static final String AN_DIR_NAME = "an/";
90     private static final String AP_PATH_NAME = "ap/";
91     private static final String ASSETS_DIR_NAME = "assets/";
92     private static final String SO_DIR_NAME = "maple/";
93     private static final String SO_ARM64_DIR_NAME = "maple/arm64/";
94     private static final String LINUX_FILE_SEPARATOR = "/";
95     private static final String DISTRO = "distro";
96     private static final String FORMS = "forms";
97     private static final String MODULE_NAME = "module-name";
98     private static final String MODULE_NAME_NEW = "moduleName";
99     private static final String JSON_END = "}";
100     private static final String SEMICOLON = "\"";
101     private static final String COMPRESS_NATIVE_LIBS = "compressNativeLibs";
102     private static final String SHARED_LIBS_DIR_NAME = "shared_libs/";
103     private static final String DEVICE_TYPE = "deviceType";
104     private static final String DEVICE_TYPE_FITNESSWATCH = "fitnessWatch";
105     private static final String DEVICE_TYPE_FITNESSWATCH_NEW = "liteWearable";
106     private static final String ENTRYCARD_NAME = "EntryCard/";
107     private static final String PACKINFO_NAME = "pack.info";
108     private static final String ENTRYCARD_BASE_NAME = "base";
109     private static final String ENTRYCARD_SNAPSHOT_NAME = "snapshot";
110     private static final String PIC_1X2 = "1x2";
111     private static final String PIC_2X2 = "2x2";
112     private static final String PIC_2X4 = "2x4";
113     private static final String PIC_4X4 = "4x4";
114     private static final String PIC_1X1 = "1x1";
115     private static final String PIC_6X4 = "6x4";
116     private static final String REGEX_LANGUAGE = "^[a-z]{2}$";
117     private static final String REGEX_SCRIPT = "^[A-Z][a-z]{3}$";
118     private static final String REGEX_COUNTRY = "^[A-Z]{2,3}|[0-9]{3}$";
119     private static final String REGEX_ORIENTATION = "^vertical|horizontal$";
120     private static final String REGEX_DEVICE_TYPE = "^phone|tablet|car|tv|wearable|liteWearable|2in1$";
121     private static final String REGEX_SCREEN_DENSITY = "^sdpi|mdpi|ldpi|xldpi|xxldpi$";
122     private static final String REGEX_COLOR_MODE = "^light|dark$";
123     private static final String REGEX_SHAPE = "^circle$";
124     private static final String JS_PATH = "js/";
125     private static final String ETS_PATH = "ets/";
126     private static final String HNP_PATH = "hnp/";
127     private static final String TEMP_HAP_DIR = "tempHapDir";
128     private static final String TEMP_HSP_DIR = "tempHspDir";
129     private static final String TEMP_SELECTED_HAP_DIR = "tempSelectedHapDir";
130     private static final String EMPTY_STRING = "";
131     private static final String RELEASE = "Release";
132     private static final String TYPE_SHARED = "shared";
133     private static final String APP = "app";
134     private static final String TYPE_APP_PLUGIN = "appPlugin";
135     private static final String REQUEST_PERMISSIONS = "requestPermissions";
136     private static final String PERMISSION_SUPPORT_PLUGIN = "ohos.permission.kernel.SUPPORT_PLUGIN";
137     private static final String EXTENSION_ABILITIES = "extensionAbilities";
138     private static final String MODULE = "module";
139     private static final String MODULES = "modules";
140     private static final String GENERATE_BUILD_HASH = "generateBuildHash";
141     private static final String BUILD_HASH = "buildHash";
142     private static final String TEMP_DIR = "temp";
143     private static final String SHA_256 = "SHA-256";
144     private static final String JSON_SUFFIX = ".json";
145     private static final String ATOMIC_SERVICE = "atomicService";
146     private static final String RAW_FILE_PATH = "resources/rawfile";
147     private static final String RES_FILE_PATH = "resources/resfile";
148     private static final String SUMMARY = "summary";
149     private static final String VERSION_CODE = "versionCode";
150     private static final String VERSION_NAME = "versionName";
151     private static final String DEVICE_TYPES = "deviceTypes";
152     private static final String BUNDLE_NAME = "bundleName";
153     private static final String MIN_COMPATIBLE_VERSION_CODE = "minCompatibleVersionCode";
154     private static final String MIN_API_VERSION = "minAPIVersion";
155     private static final String TARGET_API_VERSION = "targetAPIVersion";
156     private static final String API_RELEASE_TYPE = "apiReleaseType";
157     private static final String BUNDLE_TYPE = "bundleType";
158     private static final String INSTALLATION_FREE = "installationFree";
159     private static final String DELIVERY_WITH_INSTALL = "deliveryWithInstall";
160     private static final String API_VERSION = "apiVersion";
161     private static final String RELEASE_TYPE = "releaseType";
162     private static final String TARGET = "target";
163     private static final String COMPATIBLE = "compatible";
164     private static final String PACKAGES = "packages";
165     private static final String VERSION = "version";
166     private static final String CODE = "code";
167     private static final String VERSION_RECORD = "version_record.json";
168     private static final String GENERAL_RECORD = "general_record.json";
169     private static final String RES_INDEX = "resources.index";
170     private static final String ETS_FILE_NAME = "ets";
171     private static final String HNP_FILE_NAME = "hnp";
172     private static final String DIR_FILE_NAME = "dir";
173     private static final String AN_FILE_NAME = "an";
174     private static final String AP_FILE_NAME = "ap";
175     private static final String RESOURCE_FILE_NAME = "resources";
176     private static final String JS_FILE_NAME = "js";
177     private static final String ASSETS_FILE_NAME = "assets";
178     private static final String MAPLE_FILE_NAME = "maple";
179     private static final String SHARED_LIBS_FILE_NAME = "shared_libs";
180     private static final String LIBS_DIR = "libs";
181     private static final String RPCID = "rpcid.sc";
182     private static final String HAPADDITION_FOLDER_NAME = "hapAddition";
183     private static final String TARGET_FILE_PATH = HAPADDITION_FOLDER_NAME + LINUX_FILE_SEPARATOR + "resources"
184             + LINUX_FILE_SEPARATOR + "base" + LINUX_FILE_SEPARATOR + "profile";
185     private static final String BACKUP_PREFIX = "backup";
186 
187     // set timestamp to get fixed MD5
188     private static final int ATOMIC_SERVICE_ENTRY_SIZE_LIMIT_DEFAULT =  2048; // 2MB;unit is KB
189     private static final int ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT_DEFAULT = 2048; // 2MB;unit is KB
190     private static final int ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_DEFAULT = 4194304; // 4GB;unit is KB
191     private static final int ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX = 4194304; // 4GB;unit is KB
192     private static final int SHA256_BASE = 0xff;
193     private static final int SHA256_OFFSET = 0x100;
194     private static final int RADIX = 16;
195     private static final int BEGIN_INDEX = 1;
196     private static final int BUFFER_BYTE_SIZE = 1024;
197     private static final int BUFFER_WRITE_SIZE = 1444;
198 
199     // set buffer size of each read
200     private static final int BUFFER_SIZE = 40 * 1024;
201     private static final Log LOG = new Log(Compressor.class.toString());
202     private static final int SHARED_APP_HSP_LIMIT = 1;
203 
204     private static int entryModuleSizeLimit = 2048;
205     private static int notEntryModuleSizeLimit = 2048;
206     private static int sumModuleSizeLimit = 10240;
207     private static final int INVALID_VERSION = -1;
208     private static boolean isOverlay = false;
209 
210     private ZipArchiveOutputStream zipOut = null;
211     private boolean mIsContain2x2EntryCard = true;
212     private boolean isEntryOpen = false;
213     private List<String> list = new ArrayList<String>();
214     private List<String> formNamesList = new ArrayList<String>();
215     private List<String> fileNameList = new ArrayList<String>();
216     private List<String> supportDimensionsList = Arrays.asList(PIC_1X2, PIC_2X2, PIC_2X4, PIC_4X4, PIC_1X1, PIC_6X4);
217     private HashMap<String, HapVerifyInfo> hapVerifyInfoMap = new HashMap<>();
218 
getEntryModuleSizeLimit()219     public static int getEntryModuleSizeLimit() {
220         return entryModuleSizeLimit;
221     }
222 
setEntryModuleSizeLimit(int entry)223     public static void setEntryModuleSizeLimit(int entry) {
224         entryModuleSizeLimit = entry;
225     }
226 
getNotEntryModuleSizeLimit()227     public static int getNotEntryModuleSizeLimit() {
228         return notEntryModuleSizeLimit;
229     }
230 
setNotEntryModuleSizeLimit(int notEntry)231     public static void setNotEntryModuleSizeLimit(int notEntry) {
232         notEntryModuleSizeLimit = notEntry;
233     }
234 
getSumModuleSizeLimit()235     public static int getSumModuleSizeLimit() {
236         return sumModuleSizeLimit;
237     }
238 
setSumModuleSizeLimit(int sumModule)239     public static void setSumModuleSizeLimit(int sumModule) {
240         sumModuleSizeLimit = sumModule;
241     }
242 
243     private static class VersionNormalizeUtil {
244         private int originVersionCode = INVALID_VERSION;
245         private String originVersionName = "";
246         private String moduleName = "";
247 
getOriginVersionCode()248         public int getOriginVersionCode() {
249             return originVersionCode;
250         }
251 
setOriginVersionCode(int originVersionCode)252         public void setOriginVersionCode(int originVersionCode) {
253             this.originVersionCode = originVersionCode;
254         }
255 
getModuleName()256         public String getModuleName() {
257             return moduleName;
258         }
259 
setModuleName(String name)260         public void setModuleName(String name) {
261             this.moduleName = name;
262         }
263 
getOriginVersionName()264         public String getOriginVersionName() {
265             return originVersionName;
266         }
267 
setOriginVersionName(String originVersionName)268         public void setOriginVersionName(String originVersionName) {
269             this.originVersionName = originVersionName;
270         }
271     }
272 
273     private static class GeneralNormalizeUtil {
274         private int originVersionCode = INVALID_VERSION;
275         private String originVersionName = "";
276         private String moduleName = "";
277         private String originBundleName = "";
278         private int originMinCompatibleVersionCode = INVALID_VERSION;
279         private int originMinAPIVersion = INVALID_VERSION;
280         private int originTargetAPIVersion = INVALID_VERSION;
281         private String originApiReleaseType = "";
282         private String originBundleType = "";
283         private boolean originInstallationFree = false;
284         private boolean originDeliveryWithInstall = false;
285         private String originDeviceTypes = "";
286         private boolean isInstallationFree = false;
287         private boolean isDeliveryWithInstall = false;
288 
setOriginVersionCode(int originVersionCode)289         public void setOriginVersionCode(int originVersionCode) {
290             this.originVersionCode = originVersionCode;
291         }
292 
setModuleName(String name)293         public void setModuleName(String name) {
294             this.moduleName = name;
295         }
296 
setOriginVersionName(String originVersionName)297         public void setOriginVersionName(String originVersionName) {
298             this.originVersionName = originVersionName;
299         }
300 
setOriginBundleName(String originBundleName )301         public void setOriginBundleName(String originBundleName ) {
302             this.originBundleName = originBundleName;
303         }
304 
setOriginMinCompatibleVersionCode(int originMinCompatibleVersionCode)305         public void setOriginMinCompatibleVersionCode(int originMinCompatibleVersionCode) {
306             this.originMinCompatibleVersionCode = originMinCompatibleVersionCode;
307         }
308 
setOriginMinAPIVersion(int originMinAPIVersion)309         public void setOriginMinAPIVersion(int originMinAPIVersion) {
310             this.originMinAPIVersion = originMinAPIVersion;
311         }
312 
setOriginTargetAPIVersion(int originTargetAPIVersion)313         public void setOriginTargetAPIVersion(int originTargetAPIVersion) {
314             this.originTargetAPIVersion = originTargetAPIVersion;
315         }
316 
setOriginApiReleaseType(String originApiReleaseType)317         public void setOriginApiReleaseType(String originApiReleaseType) {
318             this.originApiReleaseType = originApiReleaseType;
319         }
320 
setOriginBundleType(String originBundleType)321         public void setOriginBundleType(String originBundleType) {
322             this.originBundleType = originBundleType;
323         }
324 
setIsInstallationFree(boolean isInstallationFree)325         public void setIsInstallationFree(boolean isInstallationFree) {
326             this.isInstallationFree = isInstallationFree;
327         }
328 
setOriginInstallationFree(boolean originInstallationFree)329         public void setOriginInstallationFree(boolean originInstallationFree) {
330             this.originInstallationFree = originInstallationFree;
331         }
332 
setOriginDeliveryWithInstall(boolean originDeliveryWithInstall)333         public void setOriginDeliveryWithInstall(boolean originDeliveryWithInstall) {
334             this.originDeliveryWithInstall = originDeliveryWithInstall;
335         }
336 
setIsDeliveryWithInstall(boolean isDeliveryWithInstall)337         public void setIsDeliveryWithInstall(boolean isDeliveryWithInstall) {
338             this.isDeliveryWithInstall = isDeliveryWithInstall;
339         }
340 
setOriginDeviceTypes(String originDeviceTypes)341         public void setOriginDeviceTypes(String originDeviceTypes) {
342             this.originDeviceTypes = originDeviceTypes;
343         }
344     }
345 
346     /**
347      * Parse atomicService size limit parameter from utility.
348      *
349      * @param utility Indicates the utility.
350      */
parseAtomicServiceSizeLimit(Utility utility)351     public void parseAtomicServiceSizeLimit(Utility utility) throws BundleException {
352         parseAtomicServiceEntrySizeLimitParameter(utility);
353         parseAtomicServiceNonEntrySizeLimitParameter(utility);
354     }
355 
parseAtomicServiceSumSizeLimitParameter(Utility utility)356     private int parseAtomicServiceSumSizeLimitParameter(Utility utility) throws BundleException{
357         int sumLimit = ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_DEFAULT;
358         String totalLimit = utility.getAtomicServiceTotalSizeLimit();
359         if (!totalLimit.isEmpty()) {
360             try {
361                 sumLimit = Integer.parseInt(totalLimit);
362             } catch (NumberFormatException e) {
363                 String errMsg = "parseAtomicServiceSumSizeLimitParameter failed, input --atomic-service-total-size-limit invalid.";
364                 String solution = "Check the --atomic-service-total-size-limit parameter.";
365                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
366                 throw new BundleException("parseAtomicServiceSumSizeLimitParameter failed, " +
367                         "input --atomic-service-total-size-limit invalid.");
368             }
369             if (sumLimit < 0 || sumLimit > ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX) {
370                 String errMsg = "parseAtomicServiceSumSizeLimitParameter failed, " +
371                         "input --atomic-service-total-size-limit value out of range [0,4194304].";
372                 String solution = "Check the --atomic-service-total-size-limit parameter is " +
373                         "within the range of [0,4194304].";
374                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
375                 throw new BundleException("parseAtomicServiceSumSizeLimitParameter failed, " +
376                         "input --atomic-service-total-size-limit value out of range [0,4194304].");
377             }
378         }
379         setSumModuleSizeLimit(sumLimit);
380         return sumLimit;
381     }
382 
parseAtomicServiceEntrySizeLimitParameter(Utility utility)383     private void parseAtomicServiceEntrySizeLimitParameter(Utility utility) throws BundleException{
384         String entrySizeLimitParamValue = utility.getAtomicServiceEntrySizeLimit();
385         int entryLimit = ATOMIC_SERVICE_ENTRY_SIZE_LIMIT_DEFAULT;
386         if (!entrySizeLimitParamValue.isEmpty()) {
387             try {
388                 entryLimit = Integer.parseInt(entrySizeLimitParamValue);
389             } catch (NumberFormatException e) {
390                 String errMsg = "parseAtomicServiceEntrySizeLimitParameter failed, " +
391                         "input --atomic-service-entry-size-limit invalid.";
392                 String solution = "Check the --atomic-service-entry-size-limit parameter is invalid";
393                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
394                 throw new BundleException("parseAtomicServiceEntrySizeLimitParameter failed, " +
395                         "input --atomic-service-entry-size-limit invalid.");
396             }
397             if (entryLimit < 0 || entryLimit > ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX) {
398                 String errMsg = "parseAtomicServiceEntrySizeLimitParameter failed, " +
399                         "input --atomic-service-entry-size-limit value out of range [0,4194304].";
400                 String solution = "Check the --atomic-service-entry-size-limit parameter is " +
401                         "within the valid range [0,4194304].";
402                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
403                 throw new BundleException("parseAtomicServiceEntrySizeLimitParameter failed, " +
404                         "input --atomic-service-entry-size-limit value out of range [0,4194304].");
405             }
406         }
407         setEntryModuleSizeLimit(entryLimit);
408     }
409 
parseAtomicServiceNonEntrySizeLimitParameter(Utility utility)410     private void parseAtomicServiceNonEntrySizeLimitParameter(Utility utility) throws BundleException{
411         String nonEntryLimitParamValue = utility.getAtomicServiceNonEntrySizeLimit();
412         int notEntryLimit = ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT_DEFAULT;
413         if (!nonEntryLimitParamValue.isEmpty()) {
414             try {
415                 notEntryLimit = Integer.parseInt(nonEntryLimitParamValue);
416             } catch (NumberFormatException e) {
417                 String errMsg = "parseAtomicServiceSizeLimit failed, " +
418                         "input --atomic-service-non-entry-size-limit invalid.";
419                 String solution = "Check the --atomic-service-non-entry-size-limit parameter";
420                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
421                 throw new BundleException("parseAtomicServiceSizeLimit failed, " +
422                         "input --atomic-service-non-entry-size-limit invalid.");
423             }
424             if (notEntryLimit < 0 || notEntryLimit > ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX) {
425                 String errMsg = "parseAtomicServiceSizeLimit failed, " +
426                         "input --atomic-service-non-entry-size-limit value out of range [0,4194304].";
427                 String solution = "Check the --atomic-service-non-entry-size-limit parameter is " +
428                         "within the valid range [0,4194304].";
429                 LOG.error(PackingToolErrMsg.PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toString(errMsg, solution));
430                 throw new BundleException("parseAtomicServiceSizeLimit failed, " +
431                         "input --atomic-service-non-entry-size-limit value out of range [0,4194304].");
432             }
433         }
434         setNotEntryModuleSizeLimit(notEntryLimit);
435     }
436 
437     /**
438      * check path if is a module.json file
439      *
440      * @param path   path input
441      * @return true if path is a module file
442      */
isModuleJSON(String path)443     private static boolean isModuleJSON(String path)
444     {
445         File file = new File(path);
446         if ((file.isFile()) && MODULE_JSON.equals(file.getName())) {
447             return true;
448         }
449         return false;
450     }
451 
452     /**
453      * start compress.
454      * file orders as follows:
455      * for hap: 1.config.json 2.lib 3.res 4.assets 5.*.so 6.*.dex 7.*.apk 8.resources.index
456      * for app: 1.certificate 2.signature 3.pack.info 4.hap (1 and 2 may not be used)
457      *
458      * @param utility common data
459      * @return compressProcess if compress succeed
460      */
compressProcess(Utility utility)461     public boolean compressProcess(Utility utility) {
462         switch (utility.getMode()) {
463             case Utility.VERSION_NORMALIZE:
464                 versionNormalize(utility);
465                 return true;
466             case Utility.MODE_HAPADDITION:
467                 hapAddition(utility);
468                 return true;
469             case Utility.PACKAGE_NORMALIZE:
470                 return PackageNormalize.normalize(utility);
471             case Utility.GENERAL_NORMALIZE:
472                 generalNormalize(utility);
473                 return true;
474             default:
475                 return defaultProcess(utility);
476         }
477     }
478 
defaultProcess(Utility utility)479     private boolean defaultProcess(Utility utility) {
480         File destFile = new File(utility.getOutPath());
481         // if out file directory not exist, mkdirs.
482         File outParentFile = destFile.getParentFile();
483         if ((outParentFile != null) && (!outParentFile.exists())) {
484             if (!outParentFile.mkdirs()) {
485                 String errMsg = "Create output file's parent directory failed.";
486                 String solution = "Check the --out-path parameter.";
487                 LOG.error(PackingToolErrMsg.COMPRESS_PROCESS_FAILED.toString(errMsg, solution));
488                 return false;
489             }
490         }
491         boolean compressResult = true;
492         FileOutputStream fileOut = null;
493         CheckedOutputStream checkedOut = null;
494         try {
495             parseAtomicServiceSizeLimit(utility);
496 
497             fileOut = new FileOutputStream(destFile);
498             checkedOut = new CheckedOutputStream(fileOut, new CRC32());
499             zipOut = new ZipArchiveOutputStream(checkedOut);
500             zipOut.setLevel(utility.getCompressLevel());
501             compressExcute(utility);
502         } catch (FileNotFoundException exception) {
503             compressResult = false;
504             LOG.error(PackingToolErrMsg.FILE_NOT_FOUND.toString(
505                     "Compress exist FileNotFoundException: " + exception.getMessage()));
506         } catch (BundleException ex) {
507             compressResult = false;
508             LOG.error(PackingToolErrMsg.COMPRESS_PROCESS_EXCEPTION.toString(
509                     "Compress exist BundleException: " + ex.getMessage()));
510         } finally {
511             closeZipOutputStream();
512             Utility.closeStream(zipOut);
513             Utility.closeStream(checkedOut);
514             Utility.closeStream(fileOut);
515         }
516 
517         if (compressResult && !checkAppAtomicServiceCompressedSizeValid(utility)) {
518             compressResult = false;
519             String errMsg = "The size of a single module, or the size of a module plus its dependencies, " +
520                     "exceeds the maximum.";
521             LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_SIZE_FAILED.toString(errMsg));
522         }
523 
524         // if compress failed, delete out file.
525         if (!compressResult) {
526             String errMsg = "Compress process failed.";
527             String solution = "Please check the first error message for more details and modify accordingly.";
528             LOG.error(PackingToolErrMsg.COMPRESS_PROCESS_FAILED.toString(errMsg, solution));
529             if (!destFile.delete()) {
530                 errMsg = "Delete the output file " + utility.getOutPath() + " failed.";
531                 solution = "Try to close the output file using programme.";
532                 LOG.error(PackingToolErrMsg.FILE_DELETE_FAILED.toString(errMsg, solution));
533             }
534         }
535         return compressResult;
536     }
537 
compressExcute(Utility utility)538     private void compressExcute(Utility utility) throws BundleException {
539         switch (utility.getMode()) {
540             case Utility.MODE_HAP:
541                 compressHap(utility);
542                 break;
543             case Utility.MODE_HAR:
544                 compressHarMode(utility);
545                 break;
546             case Utility.MODE_APP:
547                 compressAppMode(utility);
548                 break;
549             case Utility.MODE_FAST_APP:
550                 compressFastAppMode(utility);
551                 break;
552             case Utility.MODE_MULTI_APP:
553                 compressAppModeForMultiProject(utility);
554                 break;
555             case Utility.MODE_HQF:
556                 compressHQFMode(utility);
557                 break;
558             case Utility.MODE_APPQF:
559                 compressAPPQFMode(utility);
560                 break;
561             case Utility.MODE_HSP:
562                 compressHsp(utility);
563                 break;
564             default:
565                 compressPackResMode(utility);
566         }
567     }
568 
compressHsp(Utility utility)569     private void compressHsp(Utility utility) throws BundleException {
570         setGenerateBuildHash(utility);
571         if (isModuleJSON(utility.getJsonPath())) {
572             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
573             String jsonString = optional.get();
574             if (!checkStageAsanTsanEnabledValid(jsonString)) {
575                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
576                         "Check the asanTsanEnabled parameter in the Stage module failed."));
577                 throw new BundleException("Compress hsp failed.");
578             }
579             if (!checkStageHwasanEnabledValid(jsonString)) {
580                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
581                         "Check the hwasanEnabled parameter in the Stage module failed."));
582                 throw new BundleException("Compress hsp failed.");
583             }
584             if (!checkStageUbsanEnabledValid(jsonString)) {
585                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
586                         "Check the ubsanEnabled parameter in the Stage module failed."));
587                 throw new BundleException("Compress hsp failed.");
588             }
589             if (!checkStageAtomicService(jsonString)) {
590                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
591                         "Check the atomicService parameter in the Stage module failed."));
592                 throw new BundleException("Check stage AtomicService failed.");
593             }
594             // check continueBundleName in module.json
595             if (!checkContinueBundleNameIsValid(jsonString)) {
596                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
597                         "Check the continueBundleName parameter in the Stage module failed."));
598                 throw new BundleException("Compress hsp failed.");
599             }
600             // check whether is an overlay hsp or not
601             if (!checkStageOverlayCfg(jsonString)) {
602                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString(
603                         "Check the overlay config in the Stage module failed."));
604                 throw new BundleException("Compress hsp failed.");
605             }
606             String moduleType = ModuleJsonUtil.parseModuleType(jsonString);
607             if (!TYPE_SHARED.equals(moduleType)) {
608                 LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString("Module type must be shared."));
609                 throw new BundleException("Compress hsp failed.");
610             }
611         }
612         if (!checkAppPlugin(utility)) {
613             LOG.error(PackingToolErrMsg.COMPRESS_HSP_FAILED.toString("plugin package packaging failed."));
614             throw new BundleException("Compress hsp failed.");
615         }
616         compressHSPMode(utility);
617         buildHash(utility);
618     }
619 
compressHap(Utility utility)620     private void compressHap(Utility utility) throws BundleException {
621         if (utility.getJsonPath().isEmpty() && !utility.getBinPath().isEmpty()) {
622             // only for slim device
623             compressHapMode(utility);
624             return;
625         }
626         setGenerateBuildHash(utility);
627         if (isModuleJSON(utility.getJsonPath())) {
628             if (!checkStageHap(utility)) {
629                 LOG.error(PackingToolErrMsg.COMPRESS_HAP_FAILED.toString(
630                         "Verify the module.json file of the Stage HAP package failed."));
631                 throw new BundleException("Verify the module.json file of the Stage HAP package failed.");
632             }
633             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
634             String jsonString = optional.get();
635             String moduleType = ModuleJsonUtil.parseModuleType(jsonString);
636             if (TYPE_SHARED.equals(moduleType)) {
637                 LOG.warning("Compress mode is hap, but module type is shared.");
638             }
639             String bundleType = ModuleJsonUtil.parseStageBundleType(jsonString);
640             if (TYPE_SHARED.equals(bundleType)) {
641                 LOG.warning("Compress mode is hap, but app type is shared.");
642             }
643             if (!TYPE_APP_PLUGIN.equals(bundleType)) {
644                 if (!isPluginHost(utility)) {
645                     if (!checkPkgContext(utility)) {
646                         LOG.error(PackingToolErrMsg.COMPRESS_HAP_FAILED.toString(
647                                 "plugin package packing failed, the pkgContextInfo.json not exist."));
648                         throw new BundleException("Compress hap failed.");
649                     }
650                 }
651             } else {
652                 LOG.error(PackingToolErrMsg.COMPRESS_HAP_FAILED.toString("hap can not plugin."));
653                 throw new BundleException("Compress hap failed.");
654             }
655             compressHapModeForModule(utility);
656             buildHash(utility);
657         } else {
658             compressHapMode(utility);
659             buildHash(utility);
660         }
661     }
662 
isPluginHost(Utility utility)663     private static boolean isPluginHost(Utility utility) throws BundleException {
664         File file = new File(utility.getJsonPath());
665         if (!file.exists()) {
666             String errMsg = "The --json-path file does not exist.";
667             LOG.error(PackingToolErrMsg.FILE_NOT_EXIST.toString(errMsg));
668             throw new BundleException("Verify has generate build hash failed for --json-path file does not exist.");
669         }
670         try (InputStream json = new FileInputStream(file)) {
671             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
672             if (jsonObject == null || !jsonObject.containsKey(MODULE)) {
673                 LOG.error(PackingToolErrMsg.IS_PLUGIN_HOST_FAILED.toString("The --json-path file is invalid."));
674                 throw new BundleException("Parse --json-path file is invalid.");
675             }
676             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
677             if (moduleJson.containsKey(REQUEST_PERMISSIONS)) {
678                 JSONArray requestPermissions = moduleJson.getJSONArray(REQUEST_PERMISSIONS);
679                 for (int i = 0; i < requestPermissions.size(); ++i) {
680                     JSONObject requestPermission = requestPermissions.getJSONObject(i);
681                     if (isPermissionSupportPlugin(requestPermission)) {
682                         return false;
683                     }
684                 }
685             }
686             return true;
687         } catch (IOException e) {
688             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
689                             "check is plugin host exist IOException: " + e.getMessage()));
690         }
691         return true;
692     }
693 
isPermissionSupportPlugin(JSONObject requestPermission)694     private static boolean isPermissionSupportPlugin(JSONObject requestPermission) throws BundleException {
695         String reqPermissionName = getJsonString(requestPermission, NAME);
696         if (reqPermissionName.equals(PERMISSION_SUPPORT_PLUGIN)) {
697             return true;
698         }
699         return false;
700     }
701 
checkPkgContext(Utility utility)702     private static boolean checkPkgContext(Utility utility) throws BundleException {
703         if (!utility.getPkgContextPath().isEmpty()) {
704             File file = new File(utility.getPkgContextPath());
705             if (!file.isFile() || !PKG_CONTEXT_INFO.equals(file.getName())) {
706                 String errMsg = "--pkg-context-path file must be the pkgContextInfo.json file.";
707                 LOG.error(PackingToolErrMsg.CHECK_PKG_CONTEXT_FAILED.toString(errMsg));
708                 return false;
709             }
710             return true;
711         }
712         LOG.error(PackingToolErrMsg.CHECK_PKG_CONTEXT_FAILED.toString("no have pkgContextInfo.json file."));
713         return false;
714     }
715 
checkAppPlugin(Utility utility)716     private static boolean checkAppPlugin(Utility utility) throws BundleException {
717         File file = new File(utility.getJsonPath());
718         if (!file.exists()) {
719             String errMsg = "The --json-path file does not exist.";
720             LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString(errMsg));
721             throw new BundleException("Verify has generate build hash failed for --json-path file does not exist.");
722         }
723         try (InputStream json = new FileInputStream(file)) {
724             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
725             if (jsonObject == null || !jsonObject.containsKey(MODULE)) {
726                 LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString("The --json-path file is invalid."));
727                 throw new BundleException("Parse --json-path file is invalid.");
728             }
729             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
730             String jsonString = optional.get();
731             String bundleType = ModuleJsonUtil.parseStageBundleType(jsonString);
732             if (!TYPE_APP_PLUGIN.equals(bundleType)) {
733                 if (isPluginHost(utility)) {
734                     return true;
735                 }
736                 if (!checkPkgContext(utility)) {
737                     LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString("checkPkgContext failed."));
738                     return false;
739                 }
740                 return true;
741             }
742             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
743             JSONArray extensionAbilityJsonList = moduleJson.getJSONArray(EXTENSION_ABILITIES);
744             if (extensionAbilityJsonList != null) {
745                 for (int j = 0; j < extensionAbilityJsonList.size(); j++) {
746                     JSONObject extensionAbilityJson = extensionAbilityJsonList.getJSONObject(j);
747                     if (extensionAbilityJson != null) {
748                         LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString("extensionAbilities is not null."));
749                         return false;
750                     }
751                 }
752             }
753             if (!checkPkgContext(utility)) {
754                 LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString("checkPkgContext failed."));
755                 return false;
756             }
757             if (!isPluginHost(utility)) {
758                 LOG.error(PackingToolErrMsg.CHECK_APP_PLUGIN_FAILED.toString("plugin package cannot be the host."));
759                 return false;
760             }
761             return true;
762         } catch (IOException e) {
763             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
764                             "check app plugin exist IOException: " + e.getMessage()));
765         }
766         return true;
767     }
768 
769     /**
770      * get the String from JSONObject by the key.
771      *
772      * @param jsonObject uncompress json object
773      * @param key value key
774      * @return the result
775      */
getJsonString(JSONObject jsonObject, String key)776     private static String getJsonString(JSONObject jsonObject, String key) {
777         String value = "";
778         if (jsonObject != null && jsonObject.containsKey(key) && jsonObject.get(key) != null) {
779             value = jsonObject.get(key).toString();
780         }
781         return value;
782     }
783 
hasGenerateBuildHash(Utility utility)784     private static boolean hasGenerateBuildHash(Utility utility) throws BundleException {
785         File file = new File(utility.getJsonPath());
786         if (!file.exists()) {
787             String errMsg = "The --json-path file does not exist.";
788             LOG.error(PackingToolErrMsg.HAS_GENERATE_BUILD_HASH.toString(errMsg));
789             throw new BundleException("Verify has generate build hash failed for --json-path file does not exist.");
790         }
791         InputStream json = null;
792         boolean res = false;
793         try {
794             json = new FileInputStream(file);
795             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
796             if (jsonObject == null || !jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) {
797                 LOG.error(PackingToolErrMsg.HAS_GENERATE_BUILD_HASH.toString("The --json-path file is invalid."));
798                 throw new BundleException("Parse --json-path file is invalid.");
799             }
800             JSONObject appJson = jsonObject.getJSONObject(APP);
801             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
802             if (appJson.containsKey(GENERATE_BUILD_HASH) || moduleJson.containsKey(GENERATE_BUILD_HASH)) {
803                 res = true;
804             }
805         } catch (BundleException exception) {
806             LOG.error(PackingToolErrMsg.HAS_GENERATE_BUILD_HASH.toString(
807                     "Verify has generate build hash exist BundleException: " + exception.getMessage()));
808             throw new BundleException("Verify has generate build hash failed.");
809         } catch (JSONException | IOException e) {
810             LOG.error(PackingToolErrMsg.HAS_GENERATE_BUILD_HASH.toString(
811                     "Verify has generate build hash exist Exception (JSONException | IOException): " +
812                     e.getMessage()));
813             throw new BundleException("Verify has generate build hash failed.");
814         } finally {
815             FileUtils.closeStream(json);
816         }
817 
818         return res;
819     }
820 
setGenerateBuildHash(Utility utility)821     private static void setGenerateBuildHash(Utility utility) throws BundleException {
822         if (utility.isBuildHashFinish() || !hasGenerateBuildHash(utility)) {
823             return;
824         }
825         copyFileToTempDir(utility);
826         File file = new File(utility.getJsonPath());
827         if (!file.exists()) {
828             String errMsg = "The --json-path file does not exist.";
829             LOG.error(PackingToolErrMsg.SET_GENERATE_BUILD_HASH.toString(errMsg));
830             throw new BundleException("Set generate build hash failed for --json-path file does not exist.");
831         }
832         // 1. 解析 JSON
833         JSONObject jsonObject;
834         try (InputStream json = Files.newInputStream(file.toPath())) {
835             jsonObject = JSON.parseObject(json, JSONObject.class);
836         } catch (IOException | JSONException e) {
837             LOG.error(PackingToolErrMsg.SET_GENERATE_BUILD_HASH.toString(
838                     "Failed to read JSON file: " + e.getMessage()));
839             throw new BundleException("Failed to read JSON file");
840         }
841         //2. 检查必要字段
842         if (jsonObject == null || !jsonObject.containsKey(APP) || !jsonObject.containsKey(MODULE)) {
843             LOG.error(PackingToolErrMsg.SET_GENERATE_BUILD_HASH.toString("Parse --json-path file is invalid."));
844             throw new BundleException("The --json-path file is invalid.");
845         }
846         //3. 处理 JSON 数据
847         processBuildHashFlags(utility, jsonObject);
848         //4. 写入修改后的 JSON
849         writeJsonFile(utility.getJsonPath(), jsonObject);
850     }
851 
processBuildHashFlags(Utility utility, JSONObject jsonObject)852     private static void processBuildHashFlags(Utility utility, JSONObject jsonObject) {
853         JSONObject appJson = jsonObject.getJSONObject(APP);
854         JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
855         if (appJson.containsKey(GENERATE_BUILD_HASH) && appJson.getBoolean(GENERATE_BUILD_HASH)) {
856             utility.setGenerateBuildHash(true);
857         } else if (moduleJson.containsKey(GENERATE_BUILD_HASH) && moduleJson.getBoolean(GENERATE_BUILD_HASH)) {
858             utility.setGenerateBuildHash(true);
859         }
860         appJson.remove(GENERATE_BUILD_HASH);
861         moduleJson.remove(GENERATE_BUILD_HASH);
862     }
863 
writeJsonFile(String filePath, JSONObject jsonObject)864     private static void writeJsonFile(String filePath, JSONObject jsonObject) throws BundleException {
865         try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
866                 Files.newOutputStream(Paths.get(filePath)), StandardCharsets.UTF_8))) {
867             String pretty = JSON.toJSONString(jsonObject, new SerializerFeature[]{
868                     SerializerFeature.PrettyFormat,
869                     SerializerFeature.WriteMapNullValue,
870                     SerializerFeature.WriteDateUseDateFormat});
871             bw.write(pretty);
872         } catch (IOException e) {
873             LOG.error(PackingToolErrMsg.SET_GENERATE_BUILD_HASH.toString(
874                     "Failed to write JSON file: " + e.getMessage()));
875             throw new BundleException("Failed to write JSON file");
876         }
877     }
878 
copyFileToTempDir(Utility utility)879     private static void copyFileToTempDir(Utility utility) throws BundleException {
880         String jsonPath = utility.getJsonPath();
881         File oldfile = new File(jsonPath);
882         if (!oldfile.exists()) {
883             String errMsg = "Parse --json-path file does not found, parse json path is " + jsonPath + ".";
884             LOG.error(PackingToolErrMsg.FILE_NOT_EXIST.toString(errMsg));
885             throw new BundleException("Copy file to temp dir failed for --json-path file not found.");
886         }
887         String oldFileParent = oldfile.getParent();
888         String tempDir = TEMP_DIR + File.separator + UUID.randomUUID();
889         mkdir(new File(oldFileParent + File.separator + tempDir));
890         String fileName;
891         if (isModuleJSON(utility.getJsonPath())) {
892             fileName = MODULE_JSON;
893         } else {
894             fileName = CONFIG_JSON;
895         }
896         String tempPath = oldFileParent + File.separator + tempDir + File.separator + fileName;
897 
898         try (InputStream inStream = new FileInputStream(jsonPath);
899              FileOutputStream fs = new FileOutputStream(tempPath)) {
900             byte[] buffer = new byte[BUFFER_WRITE_SIZE];
901             int byteread;
902             while ((byteread = inStream.read(buffer)) != -1) {
903                 fs.write(buffer, 0, byteread);
904             }
905             utility.setJsonPath(tempPath);
906         } catch (IOException e) {
907             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
908                     "Copy file to temp dir exist IOException:" + e.getMessage()));
909             throw new BundleException("Copy file to temp dir failed.");
910         }
911     }
912 
mkdir(File file)913     private static void mkdir(File file) {
914         if (null != file && !file.exists()) {
915             mkdir(file.getParentFile());
916             file.mkdir();
917         }
918     }
919 
buildHash(Utility utility)920     private static void buildHash(Utility utility) throws BundleException {
921         if (utility.isBuildHashFinish() || (!utility.getGenerateBuildHash())) {
922             return;
923         }
924         String filePath = utility.getOutPath();
925         String hash = getSHA256(filePath);
926         try {
927             putBuildHash(utility, hash);
928         } catch (IOException e) {
929             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Build hash exist IOException: " + e.getMessage()));
930             throw new BundleException("Put build hash failed.");
931         }
932     }
933 
checkSum(String filename)934     private static byte[] checkSum(String filename) throws BundleException {
935         try (InputStream fis = new FileInputStream(filename)) {
936             byte[] buffer = new byte[BUFFER_BYTE_SIZE];
937             MessageDigest complete = MessageDigest.getInstance(SHA_256);
938             int numRead;
939             do {
940                 numRead = fis.read(buffer);
941                 if (numRead > 0) {
942                     complete.update(buffer, 0, numRead);
943                 }
944             } while (numRead != -1);
945             return complete.digest();
946         } catch (IOException | NoSuchAlgorithmException e) {
947             LOG.error(PackingToolErrMsg.SHA256_CALCULATION_FAILED.toString("SHA-256 checksum calculation exist " +
948                     "Exception (IOException | NoSuchAlgorithmException): " + e.getMessage()));
949             throw new BundleException("Compressor::checkSum failed.");
950         }
951     }
952 
953     /**
954      * get SHA256 of hap or hsp
955      *
956      * @param filePath the path of hap or hsp.
957      */
getSHA256(String filePath)958     public static String getSHA256(String filePath) throws BundleException {
959         byte[] byteSum = checkSum(filePath);
960         StringBuilder temp = new StringBuilder();
961         for (int i = 0; i < byteSum.length; i++) {
962             temp.append(
963                     Integer.toString((byteSum[i] & SHA256_BASE) + SHA256_OFFSET, RADIX).substring(BEGIN_INDEX));
964         }
965         return temp.toString();
966     }
967 
putBuildHash(Utility utility, String hash)968     private static void putBuildHash(Utility utility, String hash) throws BundleException, IOException {
969         if (utility.isBuildHashFinish()) {
970             return;
971         }
972         String jsonPath = utility.getJsonPath();
973         File file = new File(jsonPath);
974         if (!file.exists()) {
975             String errMsg = "The --json-path file does not exist.";
976             LOG.error(PackingToolErrMsg.FILE_NOT_EXIST.toString(errMsg));
977             throw new BundleException("Put build hash failed for json file not exist.");
978         }
979         InputStream json = null;
980         BufferedWriter bw = null;
981         try {
982             json = new FileInputStream(file);
983             JSONObject jsonObject = JSON.parseObject(json, JSONObject.class);
984             JSONObject moduleJson = jsonObject.getJSONObject(MODULE);
985             moduleJson.put(BUILD_HASH, hash);
986             String pretty = JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat,
987                     SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
988             bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(jsonPath), StandardCharsets.UTF_8));
989             bw.write(pretty);
990         } catch (IOException e) {
991             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Put build hash exist IOException: " + e.getMessage()));
992             throw new BundleException("Put build hash failed.");
993         } catch (NullPointerException e) {
994             LOG.error(PackingToolErrMsg.NULL_POINTER_EXCEPTION.toString(
995                     "The json data err, exist NullPointerException: " + e.getMessage()));
996             throw new BundleException("Put build hash failed, json data err.");
997         } finally {
998             FileUtils.closeStream(json);
999             if (bw != null) {
1000                 bw.flush();
1001                 bw.close();
1002             }
1003         }
1004         utility.setBuildHashFinish(true);
1005     }
1006 
checkAppAtomicServiceCompressedSizeValid(Utility utility)1007     private boolean checkAppAtomicServiceCompressedSizeValid(Utility utility) {
1008         if (!utility.getMode().equals(Utility.MODE_APP) &&
1009                 !utility.getMode().equals(Utility.MODE_FAST_APP) &&
1010                 !utility.getMode().equals(Utility.MODE_MULTI_APP)) {
1011             return true;
1012         }
1013 
1014         File destFile = new File(utility.getOutPath());
1015         List<HapVerifyInfo> hapVerifyInfos = new ArrayList<>();
1016         try (ZipFile zipApp = new ZipFile(destFile)) {
1017             Enumeration<? extends ZipEntry> entries = zipApp.entries();
1018             while (entries.hasMoreElements()) {
1019                 ZipEntry entry = entries.nextElement();
1020                 if (entry != null && hapVerifyInfoMap.containsKey(entry.getName())) {
1021                     hapVerifyInfoMap.get(entry.getName()).setFileLength(entry.getCompressedSize());
1022                     hapVerifyInfos.add(hapVerifyInfoMap.get(entry.getName()));
1023                 }
1024             }
1025 
1026             if (hapVerifyInfos.isEmpty()) {
1027                 LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString("Hap verify infos is empty"));
1028                 return false;
1029             }
1030 
1031             String bundleType = hapVerifyInfos.get(0).getBundleType();
1032             if (!bundleType.equals(ATOMIC_SERVICE)) {
1033                 return true;
1034             }
1035             boolean isStage = hapVerifyInfos.get(0).isStageModule();
1036             if (!isStage) {
1037                 return true;
1038             }
1039 
1040             return HapVerify.checkFileSizeIsValid(hapVerifyInfos);
1041         } catch (IOException exception) {
1042             LOG.error(PackingToolErrMsg.APP_ATOMICSERVICE_COMPRESSED_SIZE_INVALID.toString(
1043                     "IOException: " + exception.getMessage()));
1044             return false;
1045         } catch (BundleException ignored) {
1046             LOG.error(PackingToolErrMsg.APP_ATOMICSERVICE_COMPRESSED_SIZE_INVALID.toString(
1047                     "BundleException: " + ignored.getMessage()));
1048             return false;
1049         }
1050     }
1051 
checkStageHap(Utility utility)1052     private static boolean checkStageHap(Utility utility) throws BundleException {
1053         Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
1054         String jsonString = optional.get();
1055         if (!checkStageAsanTsanEnabledValid(jsonString)) {
1056             LOG.error(PackingToolErrMsg.CHECK_STAGE_HAP_FAILED.toString(
1057                     "Check the asanTsanEnabled parameter in the Stage module failed."));
1058             return false;
1059         }
1060         if (!checkStageHwasanEnabledValid(jsonString)) {
1061             LOG.error(PackingToolErrMsg.CHECK_STAGE_HAP_FAILED.toString(
1062                     "Check the hwasanEnabled parameter in the Stage module failed."));
1063             return false;
1064         }
1065         if (!checkStageUbsanEnabledValid(jsonString)) {
1066             LOG.error(PackingToolErrMsg.CHECK_STAGE_HAP_FAILED.toString(
1067                     "Check the ubsanEnabled parameter in the Stage module failed."));
1068             return false;
1069         }
1070         // check atomicService in module.json
1071         if (!checkStageAtomicService(jsonString)) {
1072             LOG.error(PackingToolErrMsg.CHECK_STAGE_HAP_FAILED.toString(
1073                     "Check the atomicService parameter in the Stage module failed."));
1074             return false;
1075         }
1076         // check continueBundleName in module.json
1077         if (!checkContinueBundleNameIsValid(jsonString)) {
1078             LOG.error(PackingToolErrMsg.CHECK_STAGE_HAP_FAILED.toString(
1079                     "Check the continueBundleName parameter in the Stage module failed."));
1080             return false;
1081         }
1082         return true;
1083     }
1084 
checkStageAsanTsanEnabledValid(String jsonString)1085     private static boolean checkStageAsanTsanEnabledValid(String jsonString) throws BundleException {
1086         boolean asanEnabled = ModuleJsonUtil.getStageAsanEnabled(jsonString);
1087         boolean tsanEnabled = ModuleJsonUtil.getStageTsanEnabled(jsonString);
1088         if (asanEnabled && tsanEnabled) {
1089             LOG.error(PackingToolErrMsg.CHECK_AS_TSAN_ENABLED.toString(
1090                     "asanEnabled and tsanEnabled cannot be true at the same time."));
1091             return false;
1092         }
1093         return true;
1094     }
1095 
checkStageHwasanEnabledValid(String jsonString)1096     private static boolean checkStageHwasanEnabledValid(String jsonString) throws BundleException {
1097         boolean asanEnabled = ModuleJsonUtil.getStageAsanEnabled(jsonString);
1098         boolean tsanEnabled = ModuleJsonUtil.getStageTsanEnabled(jsonString);
1099         boolean gwpAsanEnabled = ModuleJsonUtil.getStageGwpAsanEnabled(jsonString);
1100         boolean hwasanEnabled = ModuleJsonUtil.getStageHwasanEnabled(jsonString);
1101         if (hwasanEnabled && asanEnabled) {
1102             LOG.error(PackingToolErrMsg.CHECK_HWASAN_ENABLED_INVALID.toString(
1103                     "hwasanEnabled and asanEnabled cannot be true at the same time."));
1104             return false;
1105         }
1106         if (hwasanEnabled && tsanEnabled) {
1107             LOG.error(PackingToolErrMsg.CHECK_HWASAN_ENABLED_INVALID.toString(
1108                     "hwasanEnabled and tsanEnabled cannot be true at the same time."));
1109             return false;
1110         }
1111         if (hwasanEnabled && gwpAsanEnabled) {
1112             LOG.error(PackingToolErrMsg.CHECK_HWASAN_ENABLED_INVALID.toString(
1113                     "hwasanEnabled and GWPAsanEnabled cannot be true at the same time."));
1114             return false;
1115         }
1116         return true;
1117     }
1118 
checkContinueBundleNameIsValid(String jsonString)1119     private static boolean checkContinueBundleNameIsValid(String jsonString) throws BundleException {
1120         Map<String, List<String>> continueBundleNameMap = ModuleJsonUtil.getAbilityContinueBundleNameMap(jsonString);
1121         String bundleName = ModuleJsonUtil.parseBundleName(jsonString);
1122         String moduleName = ModuleJsonUtil.parseStageModuleName(jsonString);
1123         for (Map.Entry<String, List<String>> entry : continueBundleNameMap.entrySet()) {
1124             List<String> continueBundleNameList = entry.getValue();
1125             if (continueBundleNameList == null) {
1126                 continue;
1127             }
1128             for (int i = 0; i < continueBundleNameList.size(); i++) {
1129                 if (bundleName.equals(continueBundleNameList.get(i))) {
1130                     LOG.error(PackingToolErrMsg.CHECK_CONTINUE_BUNDLENAME_INVALID.toString(
1131                             "Module(" + moduleName + ") continueBundleName include self."));
1132                     return false;
1133                 }
1134             }
1135         }
1136         return true;
1137     }
1138 
checkStageUbsanEnabledValid(String jsonString)1139     private static boolean checkStageUbsanEnabledValid(String jsonString) throws BundleException {
1140         boolean asanEnabled = ModuleJsonUtil.getStageAsanEnabled(jsonString);
1141         boolean tsanEnabled = ModuleJsonUtil.getStageTsanEnabled(jsonString);
1142         boolean hwasanEnabled = ModuleJsonUtil.getStageHwasanEnabled(jsonString);
1143         boolean ubsanEnabled = ModuleJsonUtil.getStageUbsanEnabled(jsonString);
1144         if (ubsanEnabled && asanEnabled) {
1145             LOG.error(PackingToolErrMsg.CHECK_UBASAN_ENABLED_INVALID.toString(
1146                     "ubsanEnabled and asanEnabled cannot be true at the same time."));
1147             return false;
1148         }
1149         if (ubsanEnabled && tsanEnabled) {
1150             LOG.error(PackingToolErrMsg.CHECK_UBASAN_ENABLED_INVALID.toString(
1151                     "ubsanEnabled and tsanEnabled cannot be true at the same time."));
1152             return false;
1153         }
1154         if (ubsanEnabled && hwasanEnabled) {
1155             LOG.error(PackingToolErrMsg.CHECK_UBASAN_ENABLED_INVALID.toString(
1156                     "ubsanEnabled and hwasanEnabled cannot be true at the same time."));
1157             return false;
1158         }
1159         return true;
1160     }
1161 
checkStageAtomicService(String jsonString)1162     private static boolean checkStageAtomicService(String jsonString) throws BundleException {
1163         // check consistency of atomicService
1164         if (!ModuleJsonUtil.isModuleAtomicServiceValid(jsonString)) {
1165             LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_FAILED.toString(
1166                     "Check consistency of atomicService failed."));
1167             return false;
1168         }
1169         // check entry module must have ability
1170         if (!ModuleJsonUtil.checkEntryInAtomicService(jsonString)) {
1171             LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_FAILED.toString(
1172                     "Check the atomicService entry module failed."));
1173             return false;
1174         }
1175         // check installationFree
1176         if (!ModuleJsonUtil.checkAtomicServiceInstallationFree(jsonString)) {
1177             LOG.error(PackingToolErrMsg.CHECK_ATOMIC_SERVICE_FAILED.toString(
1178                     "Check the installationFree parameter failed."));
1179             return false;
1180         }
1181 
1182         return true;
1183     }
1184 
checkStageOverlayCfg(String jsonString)1185     private static boolean checkStageOverlayCfg(String jsonString) throws BundleException {
1186         // check module
1187         String targetModuleName = ModuleJsonUtil.getStageTargetModuleName(jsonString);
1188         String moduleName = ModuleJsonUtil.parseStageModuleName(jsonString);
1189         if (!targetModuleName.isEmpty()) {
1190             // check targetModuleName and requestPermission
1191             if (ModuleJsonUtil.isExistedStageRequestPermissions(jsonString)) {
1192                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString(
1193                         "The module(" + moduleName + ") targetModuleName and requestPermissions " +
1194                         "cannot be configured at the same time."));
1195                 return false;
1196             }
1197             // check targetModuleName and name
1198             if (targetModuleName.equals(moduleName)) {
1199                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString(
1200                         "The targetModuleName of module(" + moduleName + ") cannot be itself."));
1201                 return false;
1202             }
1203         } else {
1204             if (ModuleJsonUtil.isExistedStageModuleTargetPriority(jsonString)) {
1205                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString(
1206                         "The targetPriority cannot be existed without the targetModuleName in module.json. " +
1207                         "The moduleName is " + moduleName + "."));
1208                 return false;
1209             }
1210         }
1211         // check app
1212         String targetBundleName = ModuleJsonUtil.getStageTargetBundleName(jsonString);
1213         if (!targetBundleName.isEmpty()) {
1214             if (targetModuleName.isEmpty()) {
1215                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString("The module(" + moduleName +
1216                         ") targetModuleName settings is necessary in the overlay bundle."));
1217                 return false;
1218             }
1219             if (targetBundleName.equals(ModuleJsonUtil.parseBundleName(jsonString))) {
1220                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString(
1221                         "The module(" + moduleName + ") targetBundleName cannot be same with the bundleName."));
1222                 return false;
1223             }
1224         } else {
1225             if (ModuleJsonUtil.isExistedStageAppTargetPriority(jsonString)) {
1226                 LOG.error(PackingToolErrMsg.CHECK_OVERLAY_CFG_FAILED.toString("The targetPriority cannot be existed " +
1227                         "without the targetBundleName in app.json. The moduleName is " + moduleName + "."));
1228                 return false;
1229             }
1230         }
1231         return true;
1232     }
1233 
checkFAHap(Utility utility)1234     private static boolean checkFAHap(Utility utility) throws BundleException {
1235         Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
1236         String jsonString = optional.get();
1237         return checkFAAsanEnabledValid(jsonString);
1238     }
1239 
checkFAAsanEnabledValid(String jsonString)1240     private static boolean checkFAAsanEnabledValid(String jsonString) throws BundleException {
1241         boolean asanEnabled = ModuleJsonUtil.getFAAsanEnabled(jsonString);
1242         boolean debug = ModuleJsonUtil.getFADebug(jsonString);
1243         if (asanEnabled && !debug) {
1244             LOG.error("asanEnabled is not supported for Release.");
1245             return false;
1246         }
1247         return true;
1248     }
1249 
1250     /**
1251      * compress in hap mode.
1252      *
1253      * @param utility common data
1254      * @throws BundleException FileNotFoundException|IOException.
1255      */
compressHapMode(Utility utility)1256     private void compressHapMode(Utility utility) throws BundleException {
1257         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
1258 
1259         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
1260 
1261         if (!utility.getIndexPath().isEmpty() && !utility.getModuleName().isEmpty()) {
1262             String assetsPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR;
1263             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
1264         }
1265 
1266         if (!utility.getLibPath().isEmpty()) {
1267             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
1268         }
1269 
1270         if (!utility.getFilePath().isEmpty()) {
1271             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
1272         }
1273 
1274         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
1275             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
1276                     + RESOURCES_DIR_NAME;
1277             String deviceTypes = utility.getDeviceType().replace("\"", "").trim();
1278             if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) ||
1279                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) {
1280                 resPath = RES_DIR_NAME;
1281             }
1282             pathToFile(utility, utility.getResPath(), resPath, false);
1283         }
1284 
1285         if (!utility.getResourcesPath().isEmpty() && !utility.getModuleName().isEmpty()) {
1286             String resourcesPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
1287                     + RESOURCES_DIR_NAME;
1288             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
1289         }
1290 
1291         if (!utility.getRpcidPath().isEmpty()) {
1292             String rpcidPath = NULL_DIR_NAME;
1293             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
1294         }
1295 
1296         if (!utility.getPackInfoPath().isEmpty()) {
1297             String packInfoPath = NULL_DIR_NAME;
1298             pathToFile(utility, utility.getPackInfoPath(), packInfoPath, false);
1299         }
1300 
1301         if (!utility.getAssetsPath().isEmpty()) {
1302             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
1303         }
1304 
1305         if (!utility.getBinPath().isEmpty()) {
1306             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
1307         }
1308         // pack --dir-list
1309         if (!utility.getFormatedDirList().isEmpty()) {
1310             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
1311                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
1312                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
1313             }
1314         }
1315 
1316         compressHapModeMultiple(utility);
1317     }
1318 
1319     /**
1320      * compress in hap mode for module.json.
1321      *
1322      * @param utility common data
1323      * @throws BundleException FileNotFoundException|IOException.
1324      */
compressHapModeForModule(Utility utility)1325     private void compressHapModeForModule(Utility utility) throws BundleException {
1326         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
1327 
1328         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
1329 
1330         if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
1331             String assetsPath = NULL_DIR_NAME;
1332             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
1333         }
1334 
1335         if (!utility.getLibPath().isEmpty()) {
1336             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
1337         }
1338 
1339         if (!utility.getANPath().isEmpty()) {
1340             pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false);
1341         }
1342 
1343         if (!utility.getAPPath().isEmpty()) {
1344             pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false);
1345         }
1346 
1347         if (!utility.getFilePath().isEmpty()) {
1348             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
1349         }
1350 
1351         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
1352             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
1353                     + RESOURCES_DIR_NAME;
1354             String deviceTypes = utility.getDeviceType().replace("\"", "").trim();
1355             if (DEVICE_TYPE_FITNESSWATCH.equals(deviceTypes) ||
1356                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(deviceTypes)) {
1357                 resPath = RES_DIR_NAME;
1358             }
1359             pathToFile(utility, utility.getResPath(), resPath, false);
1360         }
1361 
1362         if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
1363             String resourcesPath = RESOURCES_DIR_NAME;
1364             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
1365         }
1366         if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
1367             String jsPath = JS_PATH;
1368             pathToFile(utility, utility.getJsPath(), jsPath, false);
1369         }
1370 
1371         if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
1372             String etsPath = ETS_PATH;
1373             pathToFile(utility, utility.getEtsPath(), etsPath, false);
1374         }
1375 
1376         if (!utility.getHnpPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
1377             String hnpPath = HNP_PATH;
1378             pathToFile(utility, utility.getHnpPath(), hnpPath, false);
1379         }
1380 
1381         if (!utility.getRpcidPath().isEmpty()) {
1382             String rpcidPath = NULL_DIR_NAME;
1383             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
1384         }
1385 
1386         if (!utility.getAssetsPath().isEmpty()) {
1387             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
1388         }
1389 
1390         if (!utility.getBinPath().isEmpty()) {
1391             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
1392         }
1393 
1394         if (!utility.getPackInfoPath().isEmpty()) {
1395             pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false);
1396         }
1397 
1398         // pack --dir-list
1399         if (!utility.getFormatedDirList().isEmpty()) {
1400             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
1401                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
1402                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
1403             }
1404         }
1405         if (!utility.getPkgContextPath().isEmpty()) {
1406             pathToFile(utility, utility.getPkgContextPath(), NULL_DIR_NAME, false);
1407         }
1408 
1409         compressHapModeMultiple(utility);
1410     }
1411 
1412     /**
1413      * compress in hap mode multiple path.
1414      *
1415      * @param utility common data
1416      * @throws BundleException FileNotFoundException|IOException.
1417      */
compressHapModeMultiple(Utility utility)1418     private void compressHapModeMultiple(Utility utility) throws BundleException {
1419         for (String soPathItem : utility.getFormattedSoPathList()) {
1420             pathToFile(utility, soPathItem, SO_ARM64_DIR_NAME, false);
1421         }
1422 
1423         if (utility.getFormattedSoPathList().size() == 0 && !utility.getSoDir().isEmpty()) {
1424             pathToFile(utility, utility.getSoDir(), SO_DIR_NAME, false);
1425         }
1426 
1427         for (String soPathItem : utility.getFormattedAbilitySoPathList()) {
1428             pathToFile(utility, soPathItem, NULL_DIR_NAME, false);
1429         }
1430 
1431         for (String dexPathItem : utility.getFormattedDexPathList()) {
1432             pathToFile(utility, dexPathItem, NULL_DIR_NAME, false);
1433         }
1434 
1435         for (String abcPathItem : utility.getFormattedAbcPathList()) {
1436             pathToFile(utility, abcPathItem, NULL_DIR_NAME, false);
1437         }
1438 
1439         for (String apkPathItem : utility.getFormattedApkPathList()) {
1440             pathToFile(utility, apkPathItem, NULL_DIR_NAME, false);
1441         }
1442 
1443         for (String jarPathItem : utility.getFormattedJarPathList()) {
1444             pathToFile(utility, jarPathItem, NULL_DIR_NAME, false);
1445         }
1446 
1447         for (String txtPathItem : utility.getFormattedTxtPathList()) {
1448             pathToFile(utility, txtPathItem, NULL_DIR_NAME, false);
1449         }
1450 
1451         if (!utility.getSharedLibsPath().isEmpty()) {
1452             pathToFile(utility, utility.getSharedLibsPath(), SHARED_LIBS_DIR_NAME, utility.isCompressNativeLibs());
1453         }
1454     }
1455 
1456     /**
1457      * compress in har mode.
1458      *
1459      * @param utility common data
1460      * @throws BundleException FileNotFoundException|IOException.
1461      */
compressHarMode(Utility utility)1462     private void compressHarMode(Utility utility) throws BundleException {
1463         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
1464 
1465         if (!utility.getLibPath().isEmpty()) {
1466             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
1467         }
1468 
1469         if (!utility.getResPath().isEmpty()) {
1470             pathToFile(utility, utility.getResPath(), RESOURCES_DIR_NAME, false);
1471         }
1472 
1473         if (!utility.getResourcesPath().isEmpty()) {
1474             pathToFile(utility, utility.getResourcesPath(), RESOURCES_DIR_NAME, false);
1475         }
1476 
1477         if (!utility.getAssetsPath().isEmpty()) {
1478             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
1479         }
1480 
1481         for (String jarPathItem : utility.getFormattedJarPathList()) {
1482             pathToFile(utility, jarPathItem, NULL_DIR_NAME, false);
1483         }
1484 
1485         for (String txtPathItem : utility.getFormattedTxtPathList()) {
1486             pathToFile(utility, txtPathItem, NULL_DIR_NAME, false);
1487         }
1488     }
1489 
1490     /**
1491      * compress in app mode.
1492      *
1493      * @param utility common data
1494      * @throws BundleException FileNotFoundException|IOException.
1495      */
compressAppMode(Utility utility)1496     private void compressAppMode(Utility utility) throws BundleException {
1497         List<String> fileList = new ArrayList<>();
1498         File appOutputFile = new File(utility.getOutPath().trim());
1499         String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR;
1500         String hspTempDirPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HSP_DIR;
1501         try {
1502             pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
1503 
1504             if (!utility.getCertificatePath().isEmpty()) {
1505                 pathToFile(utility, utility.getCertificatePath(), NULL_DIR_NAME, false);
1506             }
1507 
1508             if (!utility.getSignaturePath().isEmpty()) {
1509                 pathToFile(utility, utility.getSignaturePath(), NULL_DIR_NAME, false);
1510             }
1511 
1512             File tempDir = new File(tempPath);
1513             if (!tempDir.exists()) {
1514                 tempDir.mkdirs();
1515             }
1516 
1517             for (String hapPathItem : utility.getFormattedHapPathList()) {
1518                 File hapFile = new File(hapPathItem.trim());
1519                 String hapTempPath = tempDir + File.separator + hapFile.getName();
1520                 fileList.add(hapTempPath);
1521                 try {
1522                     compressPackinfoIntoHap(hapPathItem, hapTempPath, utility.getPackInfoPath(),
1523                             utility.getCompressLevel());
1524                 } catch (IOException e) {
1525                     LOG.error(PackingToolErrMsg.COMPRESS_APP_IO_EXCEPTION.toString(
1526                             "Compress pack.info into hap exist IOException: " + e.getMessage()));
1527                     throw new BundleException("Compress pack.info into hap failed.");
1528                 }
1529             }
1530 
1531             File hspTempDir = new File(hspTempDirPath);
1532             if (!hspTempDir.exists()) {
1533                 hspTempDir.mkdirs();
1534             }
1535             for (String hspPathItem : utility.getFormattedHspPathList()) {
1536                 File hspFile = new File(hspPathItem.trim());
1537                 String hspTempPath = hspTempDir + File.separator + hspFile.getName();
1538                 fileList.add(hspTempPath);
1539                 try {
1540                     compressPackinfoIntoHap(hspPathItem, hspTempPath, utility.getPackInfoPath(),
1541                             utility.getCompressLevel());
1542                 } catch (IOException e) {
1543                     LOG.error(PackingToolErrMsg.COMPRESS_APP_IO_EXCEPTION.toString(
1544                             "Compress pack.info into hsp exist IOException: " + e.getMessage()));
1545                     throw new BundleException("Compress pack.info into hsp failed.");
1546                 }
1547             }
1548             // check hap is valid
1549             if (!checkHapIsValid(fileList, utility.getSharedApp())) {
1550                 throw new BundleException("Verify failed when compress app.");
1551             }
1552 
1553             for (String hapPath : fileList) {
1554                 HapVerifyInfo hapVerifyInfo = hapVerifyInfoMap.get(getFileNameByPath(hapPath));
1555                 if (hapVerifyInfo != null && !hapVerifyInfo.isDebug()) {
1556                     pathToFile(utility, hapPath, NULL_DIR_NAME, true);
1557                 } else {
1558                     pathToFile(utility, hapPath, NULL_DIR_NAME, false);
1559                 }
1560             }
1561 
1562             if (!utility.getEntryCardPath().isEmpty()) {
1563                 String entryCardPath = ENTRYCARD_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
1564                         + ENTRYCARD_BASE_NAME + ENTRYCARD_SNAPSHOT_NAME;
1565                 for (String entryCardPathItem : utility.getformattedEntryCardPathList()) {
1566                     pathToFile(utility, entryCardPathItem, entryCardPath, true);
1567                 }
1568             }
1569 
1570             if (!utility.getPackResPath().isEmpty()) {
1571                 pathToFile(utility, utility.getPackResPath(), NULL_DIR_NAME, false);
1572             }
1573             File file = new File(utility.getPackInfoPath());
1574             compressFile(utility, file, NULL_DIR_NAME, false);
1575             // pack encrypt.json file
1576             packEncryptJsonFile(utility);
1577             // pack pac.json file
1578             packPacJsonFile(utility);
1579         } catch (BundleException e) {
1580             LOG.error(PackingToolErrMsg.COMPRESS_APP_FAILED.toString(
1581           "Compress app file exist BundleException: " + e.getMessage()));
1582             throw new BundleException("Compress app failed.");
1583         } finally {
1584             // delete temp file
1585             for (String path : fileList) {
1586                 deleteFile(path);
1587             }
1588             deleteFile(tempPath);
1589             deleteFile(hspTempDirPath);
1590         }
1591     }
1592 
compressFastAppMode(Utility utility)1593     private void compressFastAppMode(Utility utility) throws BundleException {
1594         Path appOutPath = Paths.get(utility.getOutPath().trim());
1595         Path tmpDir = null;
1596         try {
1597             tmpDir = Files.createTempDirectory(appOutPath.getParent(), TEMP_DIR);
1598             Path appPackInfo = Paths.get(utility.getPackInfoPath());
1599             List<String> fileList = new ArrayList<>();
1600             for (String hapPath : utility.getFormattedHapPathList()) {
1601                 Path path = Paths.get(hapPath);
1602                 Path hap = PackageUtil.pack(path, appPackInfo, tmpDir, utility.getCompressLevel());
1603                 if (hap != null) {
1604                     fileList.add(hap.toString());
1605                 }
1606             }
1607             for (String hspPath : utility.getFormattedHspPathList()) {
1608                 Path path = Paths.get(hspPath);
1609                 Path hsp = PackageUtil.pack(path, appPackInfo, tmpDir, utility.getCompressLevel());
1610                 if (hsp != null) {
1611                     fileList.add(hsp.toString());
1612                 }
1613             }
1614             // check hap is valid
1615             if (!checkHapIsValid(fileList, utility.getSharedApp())) {
1616                 throw new BundleException("Verify failed when compress fast app.");
1617             }
1618             // packApp
1619             packFastApp(utility, fileList);
1620         } catch (IOException ex) {
1621             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Compress fast app exist IOException: " +
1622                     ex.getMessage()));
1623             throw new BundleException("Compressor::compressAppMode compress failed.");
1624         } finally {
1625             if (tmpDir != null) {
1626                 PackageUtil.rmdir(tmpDir);
1627             }
1628         }
1629     }
1630 
packFastApp(Utility utility, List<String> fileList)1631     private void packFastApp(Utility utility, List<String> fileList) throws BundleException {
1632         // pack.info
1633         pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false);
1634         // pack encrypt.json file
1635         packEncryptJsonFile(utility);
1636         // pack pac.json file
1637         packPacJsonFile(utility);
1638         // hap/hsp
1639         for (String hapPath : fileList) {
1640             HapVerifyInfo hapVerifyInfo = hapVerifyInfoMap.get(getFileNameByPath(hapPath));
1641             if (hapVerifyInfo != null && !hapVerifyInfo.isDebug()) {
1642                 pathToFile(utility, hapPath, NULL_DIR_NAME, true);
1643             } else {
1644                 pathToFile(utility, hapPath, NULL_DIR_NAME, false);
1645             }
1646         }
1647         // form/card
1648         if (!utility.getEntryCardPath().isEmpty()) {
1649             String entryCardPath = ENTRYCARD_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
1650                     + ENTRYCARD_BASE_NAME + ENTRYCARD_SNAPSHOT_NAME;
1651             for (String entryCardPathItem : utility.getformattedEntryCardPathList()) {
1652                 pathToFile(utility, entryCardPathItem, entryCardPath, true);
1653             }
1654         }
1655         if (!utility.getPackResPath().isEmpty()) {
1656             pathToFile(utility, utility.getPackResPath(), NULL_DIR_NAME, false);
1657         }
1658         // others
1659         if (!utility.getCertificatePath().isEmpty()) {
1660             pathToFile(utility, utility.getCertificatePath(), NULL_DIR_NAME, false);
1661         }
1662         if (!utility.getSignaturePath().isEmpty()) {
1663             pathToFile(utility, utility.getSignaturePath(), NULL_DIR_NAME, false);
1664         }
1665     }
1666 
1667     /**
1668      * compress in app mode for multi project.
1669      *
1670      * @param utility common data
1671      * @throws BundleException FileNotFoundException|IOException.
1672      */
compressAppModeForMultiProject(Utility utility)1673     private void compressAppModeForMultiProject(Utility utility) throws BundleException {
1674         List<String> fileList = new ArrayList<>();
1675         File appOutputFile = new File(utility.getOutPath().trim());
1676         String tempPath = appOutputFile.getParentFile().getParent() + File.separator + TEMP_HAP_DIR;
1677         String tempSelectedHapPath = appOutputFile.getParentFile().getParent() +File.separator + TEMP_SELECTED_HAP_DIR;
1678         try {
1679             File tempSelectedHapDir = new File(tempSelectedHapPath);
1680             FileUtils.makeDir(tempSelectedHapDir);
1681             File tempHapDir = new File(tempPath);
1682             FileUtils.makeDir(tempHapDir);
1683             // pack app and dispose conflict
1684             // save hap name into list
1685             List<String> seletedHaps = new ArrayList<>();
1686             String finalPackInfoStr = disposeApp(utility, seletedHaps, tempSelectedHapPath);
1687             // pack hap and dispose conflict
1688             finalPackInfoStr = disposeHap(utility, seletedHaps, tempSelectedHapPath, finalPackInfoStr);
1689 
1690             // save final pack.info file
1691             String finalPackInfoPath = tempSelectedHapDir.getPath() + File.separator + PACKINFO_NAME;
1692             writePackInfo(finalPackInfoPath, finalPackInfoStr);
1693             // pack haps
1694             for (String selectedHapName : seletedHaps) {
1695                 String hapPathItem = tempSelectedHapDir.getPath() + File.separator + selectedHapName;
1696                 File hapFile = new File(hapPathItem.trim());
1697                 String hapTempPath = tempHapDir.getPath() + File.separator + hapFile.getName();
1698                 fileList.add(hapTempPath);
1699                 compressPackinfoIntoHap(hapPathItem, hapTempPath, finalPackInfoPath, utility.getCompressLevel());
1700             }
1701             // check hap is valid
1702             if (!checkHapIsValid(fileList, false)) {
1703                 String errMsg = "Compressor::compressAppModeForMultiProject There are some " +
1704                         "haps with different version code or duplicated moduleName or packageName.";
1705                 throw new BundleException(errMsg);
1706             }
1707             for (String hapPath : fileList) {
1708                 pathToFile(utility, hapPath, NULL_DIR_NAME, false);
1709             }
1710             File file = new File(finalPackInfoPath);
1711             compressFile(utility, file, NULL_DIR_NAME, false);
1712             //pack encrypt.json file
1713             packEncryptJsonFile(utility);
1714             // pack pac.json file
1715             packPacJsonFile(utility);
1716         } catch (BundleException | IOException exception) {
1717             String errMsg = "Compress app mode for multi project file exist Exception (BundleException | IOException): "
1718                     + exception.getMessage();
1719             LOG.error(PackingToolErrMsg.COMPRESS_APP_MODE_FORMULTI_PROJECT_FAILED.toString(errMsg));
1720             throw new BundleException(errMsg);
1721         } finally {
1722             deleteFile(tempPath);
1723             deleteFile(tempSelectedHapPath);
1724         }
1725     }
1726 
1727     /**
1728      * compress in hapAddition mode.
1729      *
1730      * @param utility common data
1731      */
hapAddition(Utility utility)1732     private void hapAddition(Utility utility) {
1733         File hapPath = new File(utility.getAbsoluteHapPath());
1734         String hapFileName = hapPath.getName();
1735 
1736         File destFile = new File(utility.getOutPath() + LINUX_FILE_SEPARATOR + hapFileName);
1737         File outParentFile = destFile.getParentFile();
1738         if ((outParentFile != null) && (!outParentFile.exists())) {
1739             if (!outParentFile.mkdirs()) {
1740                 LOG.error(PackingToolErrMsg.HAP_ADDITION_FAILED.toString("Create out file parent directory failed."));
1741             }
1742         }
1743         FileOutputStream fileOut = null;
1744         CheckedOutputStream checkedOut = null;
1745         String currentDir = System.getProperty("user.dir");
1746         String hapAdditionPath = currentDir + LINUX_FILE_SEPARATOR + HAPADDITION_FOLDER_NAME;
1747         String backName = BACKUP_PREFIX + hapFileName;
1748         String hapPathOri = utility.getHapPath();
1749         try {
1750             copyHapFile(utility, backName);
1751             fileOut = new FileOutputStream(destFile);
1752             checkedOut = new CheckedOutputStream(fileOut, new CRC32());
1753             zipOut = new ZipArchiveOutputStream(checkedOut);
1754 
1755             compressHapAddition(utility, hapAdditionPath);
1756         } catch (BundleException | IOException exception) {
1757             LOG.error(PackingToolErrMsg.HAP_ADDITION_FAILED.toString("Hap addition exist Exception " +
1758                     "(BundleException | IOException): " + exception.getMessage()));
1759             copyFileSafely(backName, hapPathOri);
1760         } finally {
1761             closeZipOutputStream();
1762             Utility.closeStream(zipOut);
1763             Utility.closeStream(checkedOut);
1764             Utility.closeStream(fileOut);
1765             // delete packaging and unpacking process files
1766             deleteFile(backName);
1767             deleteFile(hapAdditionPath);
1768         }
1769     }
1770 
copyHapFile(Utility utility, String backName)1771     private void copyHapFile(Utility utility, String backName) throws IOException, BundleException {
1772         File hapFile = new File(utility.getAbsoluteHapPath());
1773         String currentDir = System.getProperty("user.dir");
1774         String backupPath = currentDir + LINUX_FILE_SEPARATOR + backName;
1775         File backupFile = new File(backupPath);
1776         FileUtils.copyFile(hapFile, backupFile);
1777         utility.setHapPath(backName);
1778     }
1779 
copyFileSafely(String source, String dest)1780     private void copyFileSafely(String source, String dest) {
1781         try {
1782             File sourceFile = new File(source);
1783             File destFile = new File(dest);
1784             FileUtils.copyFile(sourceFile, destFile);
1785         } catch (IOException | BundleException e) {
1786             LOG.error(PackingToolErrMsg.COPY_FILE_SAFELY_FAILED.toString("Copy file exist Exception " +
1787                     "(IOException | BundleException): " + e.getMessage()));
1788         }
1789     }
1790 
readerFile(String jsonPath)1791     private static String readerFile(String jsonPath) throws IOException {
1792         byte[] bytes = Files.readAllBytes(Paths.get(jsonPath));
1793         return new String(bytes, StandardCharsets.UTF_8);
1794     }
1795 
writeJsonFile(String dataJson, String targetPath)1796     private static void writeJsonFile(String dataJson, String targetPath) throws BundleException {
1797         try (FileWriter fileWriter = new FileWriter(targetPath)) {
1798             Object object = JSON.parse(dataJson);
1799             String jsonString = new String();
1800             if (object instanceof JSONArray) {
1801                 JSONArray jsonArray = JSONArray.parseArray(dataJson);
1802                 jsonString = JSON.toJSONString(
1803                         jsonArray, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue);
1804             } else {
1805                 JSONObject jsonObject = JSONObject.parseObject(dataJson);
1806                 jsonString = JSON.toJSONString(
1807                         jsonObject, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue);
1808             }
1809             fileWriter.write(jsonString);
1810             fileWriter.flush();
1811         } catch (IOException exception) {
1812             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Write json File exist IOException: " +
1813                     exception.getMessage()));
1814             throw new BundleException(exception.getMessage());
1815         } catch (JSONException e) {
1816             LOG.error(PackingToolErrMsg.WRITE_JSON_FILE_EXPECTION.toString("Write json file exist JSONException: " +
1817                     e.getMessage()));
1818             throw new BundleException(e.getMessage());
1819         }
1820     }
1821 
setUtilityParameter(String hapAdditionPath, Utility utility)1822     private static void setUtilityParameter(String hapAdditionPath, Utility utility) throws IOException {
1823         Path basePath = Paths.get(hapAdditionPath);
1824         try (Stream<Path> pathStream = Files.walk(basePath, 1)) {
1825             pathStream.forEach(path -> {
1826                 String fileName = path.getFileName().toString();
1827                 String filePath = path.toString();
1828                 switch (fileName) {
1829                     case ETS_FILE_NAME:
1830                         utility.setEtsPath(filePath);
1831                         break;
1832                     case DIR_FILE_NAME:
1833                         utility.setLibPath(filePath);
1834                         break;
1835                     case AN_FILE_NAME:
1836                         utility.setANPath(filePath);
1837                         break;
1838                     case AP_FILE_NAME:
1839                         utility.setAPPath(filePath);
1840                         break;
1841                     case RESOURCE_FILE_NAME:
1842                         utility.setResourcesPath(filePath);
1843                         break;
1844                     case JS_FILE_NAME:
1845                         utility.setJsPath(filePath);
1846                         break;
1847                     case ASSETS_FILE_NAME:
1848                         utility.setAssetsPath(filePath);
1849                         break;
1850                     case MAPLE_FILE_NAME:
1851                         utility.setSoDir(filePath);
1852                         break;
1853                     case SHARED_LIBS_FILE_NAME:
1854                         utility.setSharedLibsPath(filePath);
1855                         break;
1856                     case MODULE_JSON:
1857                         utility.setJsonPath(filePath);
1858                         break;
1859                     case RES_INDEX:
1860                         utility.setIndexPath(filePath);
1861                         break;
1862                     case PACKINFO_NAME:
1863                         utility.setPackInfoPath(filePath);
1864                         break;
1865                 }
1866             });
1867         }
1868     }
1869 
compressHapAddition(Utility utility, String hapAdditionPath)1870     private void compressHapAddition(Utility utility, String hapAdditionPath) throws BundleException {
1871         // decompression hap file to hapAddition
1872 
1873         unpackHap(utility.getHapPath(), hapAdditionPath);
1874 
1875         // generate addition.json file
1876         try {
1877             String data = readerFile(utility.getJsonPath());
1878             String currentDir = System.getProperty("user.dir");
1879             String targetParentPath = currentDir + LINUX_FILE_SEPARATOR + TARGET_FILE_PATH;
1880             File targetParent = new File(targetParentPath);
1881             if (!targetParent.exists()) {
1882                 if (!targetParent.mkdirs()) {
1883                     LOG.error(PackingToolErrMsg.COMPRESS_HAP_ADDITION_FAILED.toString(
1884                             "Create target file parent directory failed."));
1885                 }
1886             }
1887             String targetPath = targetParentPath + LINUX_FILE_SEPARATOR + ADDITION_JSON;
1888             writeJsonFile(data, targetPath);
1889         } catch (IOException | JSONException | BundleException e) {
1890             String errMsg = "Generate addition.json file exist Exception" +
1891                     " (IOException | JSONException | BundleException): " + e.getMessage();
1892             LOG.error(PackingToolErrMsg.COMPRESS_HAP_ADDITION_FAILED.toString(errMsg));
1893             throw new BundleException(errMsg);
1894         }
1895 
1896         // package a new hap file
1897         try {
1898             setUtilityParameter(hapAdditionPath, utility);
1899         } catch (IOException e) {
1900             String errMsg = "Set utility parameter exist IOException: " + e.getMessage();
1901             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(errMsg));
1902             throw new BundleException(errMsg);
1903         }
1904         if (utility.getHapPath().endsWith(HAP_SUFFIX)) {
1905             compressHap(utility);
1906         } else if (utility.getHapPath().endsWith(HSP_SUFFIX)) {
1907             compressHsp(utility);
1908         } else {
1909             String errMsg = "Compression add failed because file (" + utility.getHapPath() +
1910                     ") is an unsupported file type.";
1911             LOG.error(PackingToolErrMsg.COMPRESS_HAP_ADDITION_FAILED.toString(errMsg));
1912             throw new BundleException(errMsg);
1913         }
1914     }
1915 
1916     /**
1917      * pack hap in app to selectedHaps
1918      *
1919      * @param utility is common data
1920      * @param seletedHaps is seleted haps should be pack into app
1921      * @return the final pack.info string after dispose app
1922      * @throws BundleException FileNotFoundException|IOException.
1923      */
disposeApp(Utility utility, List<String> seletedHaps, String tempDir)1924     private static String disposeApp(Utility utility, List<String> seletedHaps,
1925                                      String tempDir) throws BundleException {
1926         // dispose app conflict
1927         if (utility.getFormattedAppList().isEmpty()) {
1928             return "";
1929         }
1930         String finalAppPackInfo = "";
1931         try {
1932             for (String appPath : utility.getFormattedAppList()) {
1933                 // select hap in app
1934                 finalAppPackInfo = selectHapInApp(appPath, seletedHaps, tempDir, finalAppPackInfo);
1935             }
1936         } catch (BundleException | IOException e) {
1937             String errMsg = "Dispose app exist Exception (BundleException | IOException): " + e.getMessage();
1938             LOG.error(PackingToolErrMsg.DISPOSE_APP_FAILED.toString(errMsg));
1939             throw new BundleException(errMsg);
1940         }
1941         return finalAppPackInfo;
1942     }
1943 
1944     /**
1945      * select hap from app file list
1946      *
1947      * @param appPath is common data
1948      * @param selectedHaps is list of packInfos
1949      * @throws BundleException FileNotFoundException|IOException.
1950      */
selectHapInApp(String appPath, List<String> selectedHaps, String tempDir, String finalAppPackInfo)1951     private static String selectHapInApp(String appPath, List<String> selectedHaps, String tempDir,
1952                                          String finalAppPackInfo) throws BundleException, IOException {
1953         List<String> selectedHapsInApp = new ArrayList<>();
1954         // classify hap in app
1955         copyHapAndHspFromApp(appPath, selectedHapsInApp, selectedHaps, tempDir);
1956         // rebuild pack.info
1957         String packInfoStr = FileUtils.getJsonInZips(new File(appPath), PACKINFO_NAME);
1958         if (packInfoStr.isEmpty()) {
1959             String errorMsg = "Select hap from app find that app does not have pack.info.";
1960             LOG.error(PackingToolErrMsg.NO_PACK_INFO.toString(errorMsg));
1961             throw new BundleException(errorMsg);
1962         }
1963         if (finalAppPackInfo.isEmpty()) {
1964             finalAppPackInfo = packInfoStr;
1965             return finalAppPackInfo;
1966         }
1967         // read selected module in temp hap
1968         HashMap<String, String> packagePair = new HashMap<>();
1969         for (String hapName : selectedHapsInApp) {
1970             packagePair.put(hapName, readModlueNameFromHap(tempDir + File.separator + hapName));
1971         }
1972         return ModuleJsonUtil.mergeTwoPackInfoByPackagePair(finalAppPackInfo, packInfoStr, packagePair);
1973     }
1974 
1975     /**
1976      * copy hap from app file
1977      *
1978      * @param appPath is common data
1979      * @param selectedHapsInApp is list of haps and hsps selected in app file
1980      * @param selectedHaps is the list of haps and hsps selected in input
1981      * @throws BundleException FileNotFoundException|IOException.
1982      */
copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps, String tempDir)1983     private static void copyHapAndHspFromApp(String appPath, List<String> selectedHapsInApp, List<String> selectedHaps,
1984                                              String tempDir) throws BundleException {
1985         ZipInputStream zipInput = null;
1986         ZipFile zipFile = null;
1987         OutputStream outputStream = null;
1988         InputStream inputStream = null;
1989         ZipEntry zipEntry = null;
1990         try {
1991             zipInput = new ZipInputStream(new FileInputStream(appPath));
1992             zipFile = new ZipFile(appPath);
1993             while ((zipEntry = zipInput.getNextEntry()) != null) {
1994                 File file = null;
1995                 if (!zipEntry.getName().endsWith(HAP_SUFFIX) && !zipEntry.getName().endsWith(HSP_SUFFIX)) {
1996                     continue;
1997                 }
1998                 // copy duplicated hap to duplicated dir and get moduleName of duplicated hap
1999                 if (selectedHaps.contains(zipEntry.getName())) {
2000                     LOG.error(PackingToolErrMsg.COMPRESS_FILE_DUPLICATE.toString("Copy hap from app file exist hap " +
2001                             "with duplicate file names. file is " + zipEntry.getName() + "."));
2002                     throw new BundleException("Compressor::copyHapFromApp file duplicated, file is "
2003                             + zipEntry.getName() + ".");
2004                 } else {
2005                     // copy selectedHap to tempDir
2006                     file = new File(tempDir + File.separator + zipEntry.getName());
2007                     selectedHaps.add(file.getName());
2008                     selectedHapsInApp.add(file.getName());
2009                 }
2010                 outputStream = new FileOutputStream(file);
2011                 inputStream = zipFile.getInputStream(zipEntry);
2012                 int len;
2013                 byte[] buf = new byte[BUFFER_SIZE];
2014                 while ((len = inputStream.read(buf)) != -1) {
2015                     outputStream.write(buf, 0, len);
2016                 }
2017                 outputStream.close();
2018                 inputStream.close();
2019             }
2020         } catch (IOException e) {
2021             String errMsg = "Copy hap and hsp from app exist IOException: " + e.getMessage();
2022             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(errMsg));
2023             throw new BundleException(errMsg);
2024         } finally {
2025             Utility.closeStream(zipInput);
2026             Utility.closeStream(zipFile);
2027             Utility.closeStream(outputStream);
2028             Utility.closeStream(inputStream);
2029         }
2030     }
2031 
2032     /**
2033      * read moduleName in hap
2034      *
2035      * @param hapPath is path of hap file
2036      * @throws BundleException FileNotFoundException|IOException.
2037      */
readModlueNameFromHap(String hapPath)2038     private static String readModlueNameFromHap(String hapPath) throws BundleException {
2039         String moduleName = "";
2040         File hapFile = new File(hapPath);
2041         if (isModuleHap(hapPath)) {
2042             String jsonString = FileUtils.getJsonInZips(hapFile, MODULE_JSON);
2043             moduleName = ModuleJsonUtil.parseStageModuleName(jsonString);
2044         } else {
2045             String jsonString = FileUtils.getJsonInZips(hapFile, CONFIG_JSON);
2046             moduleName = ModuleJsonUtil.parseFaModuleName(jsonString);
2047         }
2048         return moduleName;
2049     }
2050 
2051     /**
2052      * dispose input of hap
2053      *
2054      * @param utility is common data
2055      * @param seletedHaps is the selected  haps of all input
2056      * @param tempDir is the path of temp directory
2057      * @param finalPackInfoStr is the pack.info of the final app
2058      * @throws BundleException FileNotFoundException|IOException.
2059      */
disposeHap(Utility utility, List<String> seletedHaps, String tempDir, String finalPackInfoStr)2060     private static String disposeHap(Utility utility, List<String> seletedHaps, String tempDir,
2061                                      String finalPackInfoStr) throws BundleException, IOException {
2062         // dispose hap conflict
2063         if (utility.getFormattedHapList().isEmpty()) {
2064             return finalPackInfoStr;
2065         }
2066         for (String hapPath : utility.getFormattedHapList()) {
2067             if (seletedHaps.contains(new File(hapPath).getName())) {
2068                 LOG.error(PackingToolErrMsg.COMPRESS_FILE_DUPLICATE.toString("Dispose hap exist duplicate file " +
2069                         "names, file is " + new File(hapPath).getName() + "."));
2070                 throw new BundleException("Compressor::disposeHap file duplicated, file is "
2071                         + new File(hapPath).getName() + ".");
2072             }
2073             File hapFile = new File(hapPath);
2074             seletedHaps.add(hapFile.getName());
2075             // copy hap to tempDir
2076             FileUtils.copyFile(hapFile, new File((tempDir +File.separator + hapFile.getName())));
2077             String packInfo = FileUtils.getJsonInZips(hapFile, PACKINFO_NAME);
2078 
2079             if (packInfo.isEmpty()) {
2080                 String errMsg = "Hap does not have pack.info.";
2081                 LOG.error(PackingToolErrMsg.NO_PACK_INFO.toString(errMsg));
2082                 throw new BundleException(errMsg);
2083             }
2084             if (finalPackInfoStr.isEmpty()) {
2085                 finalPackInfoStr = packInfo;
2086             } else {
2087                 finalPackInfoStr = ModuleJsonUtil.mergeTwoPackInfo(finalPackInfoStr, packInfo);
2088             }
2089         }
2090         return finalPackInfoStr;
2091     }
2092 
2093     /**
2094      * write string to pack.info
2095      *
2096      * @param filePath pack.info file path
2097      * @param packInfoStr is the string of pack.info
2098      * @throws BundleException FileNotFoundException|IOException.
2099      */
writePackInfo(String filePath, String packInfoStr)2100     private void writePackInfo(String filePath, String packInfoStr) throws BundleException, IOException {
2101         FileWriter fwriter = null;
2102         try {
2103             fwriter = new FileWriter(filePath);
2104             fwriter.write(packInfoStr);
2105         } catch (IOException e) {
2106             String errMsg = "Write pack info exist IOException: " + e.getMessage();
2107             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(errMsg));
2108             throw new BundleException(errMsg);
2109         } finally {
2110             if (fwriter != null) {
2111                 fwriter.flush();
2112                 fwriter.close();
2113             }
2114         }
2115     }
2116 
copy(InputStream input, OutputStream output)2117     private void copy(InputStream input, OutputStream output) throws IOException {
2118         int bytesRead;
2119         byte[] data = new byte[BUFFER_SIZE];
2120         while ((bytesRead = input.read(data, 0, BUFFER_SIZE)) != -1) {
2121             output.write(data, 0, bytesRead);
2122         }
2123     }
2124 
compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo, int compressLevel)2125     private void compressPackinfoIntoHap(String hapPathItem, String outPathString, String packInfo, int compressLevel)
2126             throws IOException, BundleException {
2127         ZipFile sourceHapFile = new ZipFile(hapPathItem);
2128         ZipOutputStream append = new ZipOutputStream(new FileOutputStream(outPathString));
2129         append.setLevel(compressLevel);
2130         try {
2131             Enumeration<? extends ZipEntry> entries = sourceHapFile.entries();
2132             while (entries.hasMoreElements()) {
2133                 ZipEntry zipEntry = new ZipEntry(entries.nextElement());
2134                 if (PACKINFO_NAME.equals(zipEntry.getName())) {
2135                     continue;
2136                 }
2137                 ZipEntry newEntry = zipEntry.getMethod() ==
2138                         ZipEntry.STORED ? new ZipEntry(zipEntry) : new ZipEntry(zipEntry.getName());
2139                 append.putNextEntry(newEntry);
2140                 if (!zipEntry.isDirectory()) {
2141                     copy(sourceHapFile.getInputStream(zipEntry), append);
2142                 }
2143                 append.closeEntry();
2144             }
2145             File packInfoFile = new File(packInfo);
2146             ZipEntry zipEntry = getStoredZipEntry(packInfoFile, PACKINFO_NAME);
2147             append.putNextEntry(zipEntry);
2148             FileInputStream in = new FileInputStream(packInfoFile);
2149             try {
2150                 byte[] buf = new byte[BUFFER_SIZE];
2151                 int len;
2152                 while ((len = in.read(buf)) != -1) {
2153                     append.write(buf, 0, len);
2154                 }
2155             } finally {
2156                 in.close();
2157             }
2158             append.closeEntry();
2159         } catch (IOException exception) {
2160             LOG.error(PackingToolErrMsg.COMPRESS_FILE_EXCEPTION.toString(
2161                     "Compress pack.info into hap exist IOException: " + exception.getMessage()));
2162             throw new BundleException("Compress PackInfo into hap IOException.");
2163         } finally {
2164             sourceHapFile.close();
2165             append.close();
2166         }
2167     }
2168 
2169     /**
2170      * delete file
2171      *
2172      * @param path file path which will be deleted
2173      */
deleteFile(final String path)2174     private static void deleteFile(final String path) {
2175         File file = new File(path);
2176         if (file.exists()) {
2177             if (file.isDirectory()) {
2178                 File[] files = file.listFiles();
2179                 for (int i = 0; i < files.length; i++) {
2180                     deleteFile(files[i].toString());
2181                 }
2182             }
2183             file.delete();
2184         }
2185     }
2186 
2187     /**
2188      * compress in res mode.
2189      *
2190      * @param utility common data
2191      * @throws BundleException FileNotFoundException|IOException.
2192      */
compressPackResMode(Utility utility)2193     private void compressPackResMode(Utility utility) throws BundleException {
2194         if (!utility.getPackInfoPath().isEmpty()) {
2195             parsePackInfoJsonFile(utility, utility.getPackInfoPath());
2196             File file = new File(utility.getPackInfoPath());
2197             infoSpecialProcess(utility, file);
2198         }
2199         if (!utility.getEntryCardPath().isEmpty()) {
2200             getFileList(utility.getEntryCardPath());
2201             List<String> snapshotNameList = new ArrayList<>();
2202             for (String fileName : fileNameList) {
2203                 if (fileName.endsWith(PNG_SUFFIX) || fileName.endsWith(UPPERCASE_PNG_SUFFIX)) {
2204                     String fName = fileName.trim();
2205                     String[] temp = fName.replace("\\", "/").split("/");
2206                     if (temp.length < 4) {
2207                         LOG.error(PackingToolErrMsg.COMPRESS_PACK_RES_MODE_FAILED.toString("The hap file path is "
2208                                 + "invalid, length is " + temp.length + ", expected at least 4."));
2209                         continue;
2210                     }
2211                     String fileLanguageCountryName = temp[temp.length - 3];
2212                     if (!isThirdLevelDirectoryNameValid(fileLanguageCountryName)) {
2213                         LOG.error(PackingToolErrMsg.COMPRESS_PACK_RES_MODE_FAILED.toString("Third level "
2214                                 + "directory name: " + fileLanguageCountryName + " is invalid, please check it with "
2215                                 + "reference to this example: zh_Hani_CN-vertical-car-mdpi-dark or "
2216                                 + "zh_Hani_CN-vertical-car-mdpi."));
2217                         throw new BundleException("Compress failed third level directory name Error.");
2218                     }
2219                     String filePicturingName = temp[temp.length - 1];
2220                     if (!isPicturing(filePicturingName, utility)) {
2221                         LOG.error(PackingToolErrMsg.COMPRESS_PACK_RES_MODE_FAILED.toString("Invalid resource file"
2222                                 + " name: " + filePicturingName + ", correct format example is formName-2x2.png."));
2223                         throw new BundleException("Compress pack.res failed, Invalid resource file name: "
2224                                 + filePicturingName + ", correct format example is formName-2x2.png.");
2225                     }
2226 
2227                     String fullSnapshotName = temp[temp.length - 4] + "/" +
2228                             filePicturingName.substring(0, filePicturingName.lastIndexOf("."));
2229                     snapshotNameList.add(fullSnapshotName);
2230 
2231                 } else {
2232                     LOG.error(PackingToolErrMsg.COMPRESS_PACK_RES_MODE_FAILED.toString("No PNG format image found."));
2233                     throw new BundleException("Compress pack.res failed, compress failed No image in"
2234                             + " PNG format is found.");
2235                 }
2236             }
2237 
2238             for (String formName : formNamesList) {
2239                 if (!snapshotNameList.contains(formName)) {
2240                     LOG.error(PackingToolErrMsg.COMPRESS_PACK_RES_MODE_FAILED.toString("EntryCard has no related "
2241                             + "snapshot. " + formName + " has no related snapshot "
2242                             + snapshotNameList.toString() + "."));
2243                     throw new BundleException("Compress pack.res failed, compress failed entryCard has no related snapshot.");
2244                 }
2245             }
2246             pathToFileResMode(utility, utility.getEntryCardPath(), ENTRYCARD_NAME, false);
2247         }
2248     }
2249 
2250     /**
2251      * Check whether modelname meets specifications.
2252      *
2253      * @param name modelName
2254      * @return false and true
2255      */
isModelName(String name)2256     private boolean isModelName(String name) {
2257         for (String listName : list) {
2258             if (name.equals(listName)) {
2259                 return true;
2260             }
2261         }
2262         return false;
2263     }
2264 
isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName)2265     private boolean isThirdLevelDirectoryNameValid(String thirdLevelDirectoryName) {
2266         if (thirdLevelDirectoryName == null || thirdLevelDirectoryName.isEmpty()) {
2267             return false;
2268         }
2269         if (ENTRYCARD_BASE_NAME.equals(thirdLevelDirectoryName)) {
2270             return true;
2271         }
2272         // example: zh_Hani_CN-vertical-car-mdpi-dark or zh_Hani_CN-vertical-car-mdpi
2273         int firstDelimiterIndex = thirdLevelDirectoryName.indexOf("_");
2274         if (firstDelimiterIndex < 0) {
2275             return false;
2276         }
2277         String language = thirdLevelDirectoryName.substring(0, firstDelimiterIndex);
2278         int secondDelimiterIndex = thirdLevelDirectoryName.indexOf("_", firstDelimiterIndex + 1);
2279         if (secondDelimiterIndex < 0) {
2280             return false;
2281         }
2282         String script = thirdLevelDirectoryName.substring(firstDelimiterIndex + 1, secondDelimiterIndex);
2283         int thirdDelimiterIndex = thirdLevelDirectoryName.indexOf("-", secondDelimiterIndex + 1);
2284         if (thirdDelimiterIndex < 0) {
2285             return false;
2286         }
2287         String country = thirdLevelDirectoryName.substring(secondDelimiterIndex + 1, thirdDelimiterIndex);
2288         if (!checkLanguage(language) || !checkScript(script) || !checkCountry(country)) {
2289             return false;
2290         }
2291         int forthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", thirdDelimiterIndex + 1);
2292         if (forthDelimiterIndex < 0) {
2293             return false;
2294         }
2295         String orientation = thirdLevelDirectoryName.substring(thirdDelimiterIndex + 1, forthDelimiterIndex);
2296         int fifthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", forthDelimiterIndex + 1);
2297         if (fifthDelimiterIndex < 0) {
2298             return false;
2299         }
2300         String deviceType = thirdLevelDirectoryName.substring(forthDelimiterIndex + 1, fifthDelimiterIndex);
2301         if (!checkOrientation(orientation) || !checkDeviceType(deviceType)) {
2302             return false;
2303         }
2304         int sixthDelimiterIndex = thirdLevelDirectoryName.indexOf("-", fifthDelimiterIndex + 1);
2305         if (sixthDelimiterIndex < 0) {
2306             String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1,
2307                     thirdLevelDirectoryName.length());
2308             return checkScreenDensity(screenDensity);
2309         } else {
2310             String screenDensity = thirdLevelDirectoryName.substring(fifthDelimiterIndex + 1, sixthDelimiterIndex);
2311             if (!checkScreenDensity(screenDensity)) {
2312                 return false;
2313             }
2314         }
2315         int seventhDelimiterIndex = thirdLevelDirectoryName.indexOf("-", sixthDelimiterIndex + 1);
2316         if (seventhDelimiterIndex < 0) {
2317             String tmp = thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, thirdLevelDirectoryName.length());
2318             return checkColorModeOrShape(tmp);
2319         }
2320         if (!checkColorMode(thirdLevelDirectoryName.substring(sixthDelimiterIndex + 1, seventhDelimiterIndex))) {
2321             return false;
2322         }
2323         String shape = thirdLevelDirectoryName.substring(seventhDelimiterIndex + 1, thirdLevelDirectoryName.length());
2324         return checkShape(shape);
2325     }
2326 
checkLanguage(String language)2327     private boolean checkLanguage(String language) {
2328         if (!Pattern.compile(REGEX_LANGUAGE).matcher(language).matches()) {
2329             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The language " + language +
2330                     " is not in ISO 639-1 list."));
2331             return false;
2332         }
2333         return true;
2334     }
2335 
checkScript(String script)2336     private boolean checkScript(String script) {
2337         if (!Pattern.compile(REGEX_SCRIPT).matcher(script).matches()) {
2338             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The script " + script +
2339                     " is not in ISO 15924 list."));
2340             return false;
2341         }
2342         return true;
2343     }
2344 
checkCountry(String country)2345     private boolean checkCountry(String country) {
2346         if (!Pattern.compile(REGEX_COUNTRY).matcher(country).matches()) {
2347             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The country " + country +
2348                     " is not in ISO 3166-1 list."));
2349             return false;
2350         }
2351         return true;
2352     }
2353 
checkOrientation(String orientation)2354     private boolean checkOrientation(String orientation) {
2355         if (!Pattern.compile(REGEX_ORIENTATION).matcher(orientation).matches()) {
2356             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The orientation " + orientation +
2357                     " is not in {vertical, horizontal} list."));
2358             return false;
2359         }
2360         return true;
2361     }
2362 
checkDeviceType(String deviceType)2363     private boolean checkDeviceType(String deviceType) {
2364         if (!Pattern.compile(REGEX_DEVICE_TYPE).matcher(deviceType).matches()) {
2365             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The deviceType " + deviceType +
2366                     " is not in {phone, tablet, car, tv, wearable, liteWearable, 2in1} list."));
2367             return false;
2368         }
2369         return true;
2370     }
2371 
checkScreenDensity(String screenDensity)2372     private boolean checkScreenDensity(String screenDensity) {
2373         if (!Pattern.compile(REGEX_SCREEN_DENSITY).matcher(screenDensity).matches()) {
2374             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The screenDensity " +
2375                     screenDensity + " is not in {sdpi, mdpi, ldpi, xldpi, xxldpi} list."));
2376             return false;
2377         }
2378         return true;
2379     }
2380 
checkColorMode(String colorMode)2381     private boolean checkColorMode(String colorMode) {
2382         if (!Pattern.compile(REGEX_COLOR_MODE).matcher(colorMode).matches()) {
2383             LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The colorMode " + colorMode +
2384                     " is not in {light, dark} list."));
2385             return false;
2386         }
2387         return true;
2388     }
2389 
checkColorModeOrShape(String tmp)2390     private boolean checkColorModeOrShape(String tmp) {
2391         if (Pattern.compile(REGEX_COLOR_MODE).matcher(tmp).matches() ||
2392                 Pattern.compile(REGEX_SHAPE).matcher(tmp).matches()) {
2393             return true;
2394         }
2395         LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The colorMode or shape " + tmp +
2396                 " is neither in colorMode list {light, dark} nor in shape list {circle}."));
2397         return false;
2398     }
2399 
checkShape(String shape)2400     private boolean checkShape(String shape) {
2401         if (Pattern.compile(REGEX_SHAPE).matcher(shape).matches()) {
2402             return true;
2403         }
2404         LOG.error(PackingToolErrMsg.INVALID_THIRD_LEVEL_DIRECTORY_NAME.toString("The shape " + shape +
2405                 " is not in {circle} list."));
2406         return false;
2407     }
2408 
2409     /**
2410      * Check whether picturingName meets specifications.
2411      *
2412      * @param name picturingName
2413      * @param utility common data
2414      * @return false and true
2415      */
isPicturing(String name, Utility utility)2416     private boolean isPicturing(String name, Utility utility) {
2417         boolean isSpecifications = false;
2418         if (name == null || name.isEmpty()) {
2419             return isSpecifications;
2420         }
2421         if (!name.endsWith(PNG_SUFFIX) && !name.endsWith(UPPERCASE_PNG_SUFFIX)) {
2422             LOG.error(PackingToolErrMsg.IS_PICTURING_FAILED.toString("The suffix is not .png or .PNG."));
2423             return false;
2424         }
2425         int delimiterIndex = name.lastIndexOf("-");
2426         if (delimiterIndex < 0) {
2427             LOG.error(PackingToolErrMsg.IS_PICTURING_FAILED.toString(
2428                     "The entry card naming format is invalid and should be separated by '-'."));
2429             return false;
2430         }
2431         String formName = name.substring(0, delimiterIndex);
2432         if (!utility.getFormNameList().contains(formName)) {
2433             LOG.error(PackingToolErrMsg.IS_PICTURING_FAILED.toString(
2434                     "The name is not same as formName, name: " + formName + " is not in "
2435                     + utility.getFormNameList().toString() + "."));
2436             return false;
2437         }
2438         String dimension = name.substring(delimiterIndex + 1, name.lastIndexOf("."));
2439         if (!supportDimensionsList.contains(dimension)) {
2440             LOG.error(PackingToolErrMsg.IS_PICTURING_FAILED.toString("The dimension " + dimension +
2441                     " is invalid, is not in the following list: {1X2, 2X2, 2X4, 4X4, 1X1, 6X4}."));
2442             return false;
2443         }
2444 
2445         return true;
2446     }
2447 
getFileList(final String filePath)2448     private void getFileList(final String filePath) throws BundleException {
2449         File file = new File(filePath);
2450         if (!file.exists()) {
2451             LOG.error(PackingToolErrMsg.FILE_NOT_EXIST.toString("File does not exist. File path is "
2452                     + filePath + "."));
2453             return;
2454         }
2455         File[] files = file.listFiles();
2456         if (files == null) {
2457             LOG.error(PackingToolErrMsg.GET_FILE_LIST_FAILED.toString("No files found in the specified path: "
2458                     + filePath + "."));
2459             return;
2460         }
2461         for (File f : files) {
2462             try {
2463                 if (f.isFile()) {
2464                     if (f.getName().endsWith(".DS_Store")) {
2465                         deleteFile(f.getCanonicalPath());
2466                         continue;
2467                     }
2468                     String snapshotDirectoryName = f.getParentFile().getName();
2469                     if (!ENTRYCARD_SNAPSHOT_NAME.equals(snapshotDirectoryName)) {
2470                         LOG.error(PackingToolErrMsg.GET_FILE_LIST_FAILED.toString("The level-4 directory of EntryCard "
2471                                 + "must be named as snapshot, but current is: " + snapshotDirectoryName + "."));
2472                         throw new BundleException("The level-4 directory of EntryCard must be named as snapshot" +
2473                                 ", but current is: " + snapshotDirectoryName + ".");
2474                     }
2475                     fileNameList.add(f.getCanonicalPath());
2476                 } else if (f.isDirectory()) {
2477                     getFileList(f.getCanonicalPath());
2478                 } else {
2479                     LOG.error(PackingToolErrMsg.GET_FILE_LIST_FAILED.toString("It's not file or directory."));
2480                 }
2481             } catch (IOException msg) {
2482                 LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Get file list exist IOException: "
2483                         + msg.getMessage()));
2484                 return;
2485             }
2486         }
2487     }
2488 
checkContain2x2EntryCard(final File snapshotDirectory)2489     private void checkContain2x2EntryCard(final File snapshotDirectory) throws IOException, BundleException {
2490         if (!snapshotDirectory.exists()) {
2491             LOG.error("checkContain2x2EntryCard: file is not exist: " + snapshotDirectory.getName() + ".");
2492             throw new BundleException("checkContain2x2EntryCard: file is not exist.");
2493         }
2494         File[] files = snapshotDirectory.listFiles();
2495         if (files == null) {
2496             LOG.error("checkContain2x2EntryCard: no file in this file path.");
2497             throw new BundleException("checkContain2x2EntryCard: no file in this file path.");
2498         }
2499 
2500         for (File entryCardFile : files) {
2501             if (entryCardFile.isFile() && entryCardFile.getName().contains(PIC_2X2)) {
2502                 return;
2503             }
2504         }
2505         mIsContain2x2EntryCard = false;
2506         LOG.error("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in "
2507                 + snapshotDirectory.getCanonicalPath() + ".");
2508         throw new BundleException("checkContain2x2EntryCard: must contain 2x2 entryCard, please check it in "
2509                 + snapshotDirectory.getCanonicalPath() + ".");
2510     }
2511 
2512     /**
2513      * compress file or directory.
2514      *
2515      * @param utility       common data
2516      * @param path          create new file by path
2517      * @param baseDir       base path for file
2518      * @param isCompression if need compression
2519      * @throws BundleException FileNotFoundException|IOException.
2520      */
pathToFile(Utility utility, String path, String baseDir, boolean isCompression)2521     private void pathToFile(Utility utility, String path, String baseDir, boolean isCompression)
2522             throws BundleException {
2523         if (path.isEmpty()) {
2524             return;
2525         }
2526         if (isCompression && LIBS_DIR_NAME.equals(baseDir)) {
2527             compressNativeLibsParallel(path, baseDir, utility.getCompressLevel());
2528             return;
2529         }
2530         File fileItem = new File(path);
2531         if (fileItem.isDirectory()) {
2532             File[] files = fileItem.listFiles();
2533             if (files == null) {
2534                 return;
2535             }
2536             for (File file : files) {
2537                 if (file.isDirectory()) {
2538                     compressDirectory(utility, file, baseDir, isCompression);
2539                 } else if (isCompression) {
2540                     compressFile(utility, file, baseDir, isCompression);
2541                 } else {
2542                     compressFile(utility, file, baseDir, isCompression);
2543                 }
2544             }
2545         } else {
2546             compressFile(utility, fileItem, baseDir, isCompression);
2547         }
2548     }
2549 
compressNativeLibsParallel(String path, String baseDir, int compressLevel)2550     private void compressNativeLibsParallel(String path, String baseDir, int compressLevel)
2551             throws BundleException {
2552         try {
2553             int cores = Runtime.getRuntime().availableProcessors();
2554             ThreadPoolExecutor executorService = new ThreadPoolExecutor(cores, cores, 60L,
2555                     TimeUnit.SECONDS, new LinkedBlockingQueue<>());
2556             ParallelScatterZipCreator zipCreator = new ParallelScatterZipCreator(
2557                     executorService, new DefaultBackingStoreSupplier(null), compressLevel);
2558             File file = new File(path);
2559             if (file.isDirectory()) {
2560                 File[] files = file.listFiles();
2561                 if (files == null) {
2562                     return;
2563                 }
2564                 for (File f : files) {
2565                     addArchiveEntry(f, baseDir, zipCreator);
2566                 }
2567             } else {
2568                 addArchiveEntry(file, baseDir, zipCreator);
2569             }
2570             zipCreator.writeTo(zipOut);
2571         } catch (IOException | InterruptedException | ExecutionException e) {
2572             LOG.error(PackingToolErrMsg.COMPRESS_PARALLEL_EXCEPTION.toString(
2573                     "Parallel compress exist Exception (IOException | InterruptedException | ExecutionException): " +
2574                             e.getMessage()));
2575             throw new BundleException("Parallel compress exception. " + e.getMessage());
2576         }
2577     }
2578 
addArchiveEntry(File file, String baseDir, ParallelScatterZipCreator zipCreator)2579     private void addArchiveEntry(File file, String baseDir, ParallelScatterZipCreator zipCreator)
2580             throws IOException {
2581         if (file.isDirectory()) {
2582             File[] files = file.listFiles();
2583             if (files == null) {
2584                 return;
2585             }
2586             for (File f : files) {
2587                 addArchiveEntry(f, baseDir + file.getName() + File.separator, zipCreator);
2588             }
2589         } else {
2590             String entryName = (baseDir + file.getName()).replace(File.separator, LINUX_FILE_SEPARATOR);
2591             ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName);
2592             zipEntry.setMethod(ZipArchiveEntry.DEFLATED);
2593             InputStreamSupplier supplier = () -> {
2594                 try {
2595                     return Files.newInputStream(file.toPath());
2596                 } catch (IOException e) {
2597                     LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
2598                             "Add archive entry exist IOException: " + e.getMessage()));
2599                     return null;
2600                 }
2601             };
2602             zipCreator.addArchiveEntry(zipEntry, supplier);
2603         }
2604     }
2605 
2606     /**
2607      * compress file or directory, res mode
2608      *
2609      * @param utility       common data
2610      * @param path          create new file by path
2611      * @param baseDir       base path for file
2612      * @param isCompression if need compression
2613      * @throws BundleException FileNotFoundException|IOException.
2614      */
pathToFileResMode(Utility utility, String path, String baseDir, boolean isCompression)2615     private void pathToFileResMode(Utility utility, String path, String baseDir, boolean isCompression)
2616             throws BundleException {
2617         if (path.isEmpty()) {
2618             return;
2619         }
2620         File fileItem = new File(path);
2621         if (fileItem.isDirectory()) {
2622             File[] files = fileItem.listFiles();
2623             if (files == null) {
2624                 return;
2625             }
2626             for (File file : files) {
2627                 if (!list.contains(file.getName())) {
2628                     // moduleName not in pack.info
2629                     continue;
2630                 }
2631                 if (file.isDirectory()) {
2632                     compressDirectory(utility, file, baseDir, isCompression);
2633                 } else if (isCompression) {
2634                     compressFile(utility, file, baseDir, isCompression);
2635                 } else {
2636                     compressFile(utility, file, baseDir, isCompression);
2637                 }
2638             }
2639         } else {
2640             compressFile(utility, fileItem, baseDir, isCompression);
2641         }
2642     }
2643 
2644     /**
2645      * compress file directory.
2646      *
2647      * @param utility       common data
2648      * @param dir           file directory
2649      * @param baseDir       current directory name
2650      * @param isCompression if need compression
2651      * @throws BundleException FileNotFoundException|IOException.
2652      */
compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)2653     private void compressDirectory(Utility utility, File dir, String baseDir, boolean isCompression)
2654             throws BundleException {
2655         File[] files = dir.listFiles();
2656         if (files == null) {
2657             return;
2658         }
2659         for (File file : files) {
2660             if (file.isDirectory()) {
2661                 compressDirectory(utility, file, baseDir + dir.getName() + File.separator, isCompression);
2662             } else {
2663                 compressFile(utility, file, baseDir + dir.getName() + File.separator, isCompression);
2664             }
2665         }
2666     }
2667 
2668     /**
2669      * compress pack.info
2670      *
2671      * @param sourceFile source
2672      * @param zipOutputStream ZipOutputStream
2673      * @param name filename
2674      * @param keepDirStructure Empty File
2675      */
compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean keepDirStructure)2676     private void compress(File sourceFile, ZipOutputStream zipOutputStream, String name, boolean keepDirStructure) {
2677         FileInputStream in = null;
2678         try {
2679             byte[] buf = new byte[BUFFER_SIZE];
2680             if (sourceFile.isFile()) {
2681                 ZipEntry zipEntry = getStoredZipEntry(sourceFile, name);
2682                 zipOutputStream.putNextEntry(zipEntry);
2683                 in = new FileInputStream(sourceFile);
2684                 int len;
2685                 while ((len = in.read(buf)) != -1) {
2686                     zipOutputStream.write(buf, 0, len);
2687                 }
2688                 zipOutputStream.closeEntry();
2689             } else {
2690                 File[] listFiles = sourceFile.listFiles();
2691                 if (listFiles == null || listFiles.length == 0) {
2692                     if (keepDirStructure) {
2693                         if (!name.isEmpty()) {
2694                             ZipEntry zipEntry = getStoredZipEntry(sourceFile, name + "/");
2695                             zipOutputStream.putNextEntry(zipEntry);
2696                         } else {
2697                             ZipEntry zipEntry = getStoredZipEntry(sourceFile, name);
2698                             zipOutputStream.putNextEntry(zipEntry);
2699                         }
2700                         zipOutputStream.closeEntry();
2701                     }
2702                 } else {
2703                     for (File file : listFiles) {
2704                         if (keepDirStructure) {
2705                             isNameEmpty(zipOutputStream, name, keepDirStructure, file);
2706                         } else {
2707                             compress(file, zipOutputStream, file.getName(), keepDirStructure);
2708                         }
2709                     }
2710                 }
2711             }
2712         } catch (FileNotFoundException ignored) {
2713             LOG.error("Compressor::compressFile file not found exception.");
2714         } catch (IOException exception) {
2715             LOG.error("Compressor::compressFile io exception: " + exception.getMessage());
2716         } catch (BundleException bundleException) {
2717             LOG.error("Compressor::compressFile bundle exception" + bundleException.getMessage());
2718         } finally {
2719             Utility.closeStream(in);
2720         }
2721     }
2722 
getStoredZipEntry(File sourceFile, String name)2723     private ZipEntry getStoredZipEntry(File sourceFile, String name) throws BundleException {
2724         ZipEntry zipEntry = new ZipEntry(name);
2725         zipEntry.setMethod(ZipEntry.STORED);
2726         zipEntry.setCompressedSize(sourceFile.length());
2727         zipEntry.setSize(sourceFile.length());
2728         CRC32 crc = getCrcFromFile(sourceFile);
2729         zipEntry.setCrc(crc.getValue());
2730         return zipEntry;
2731     }
2732 
getCrcFromFile(File file)2733     private CRC32 getCrcFromFile(File file) throws BundleException {
2734         FileInputStream fileInputStream = null;
2735         CRC32 crc = new CRC32();
2736         try {
2737             fileInputStream = new FileInputStream(file);
2738             byte[] buffer = new byte[BUFFER_SIZE];
2739 
2740             int count = fileInputStream.read(buffer);
2741             while (count > 0) {
2742                 crc.update(buffer, 0, count);
2743                 count = fileInputStream.read(buffer);
2744             }
2745         } catch (FileNotFoundException ignored) {
2746             LOG.error(PackingToolErrMsg.FILE_NOT_FOUND.toString("File not found when getting Crc."));
2747             throw new BundleException("Get Crc from file failed.");
2748         } catch (IOException exception) {
2749             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
2750                     "Get Crc from file exist IOException: " + exception.getMessage()));
2751             throw new BundleException("Get Crc from file failed.");
2752         } finally {
2753             Utility.closeStream(fileInputStream);
2754         }
2755         return crc;
2756     }
2757 
2758     /**
2759      * isNameEmpty
2760      *
2761      * @param zipOutputStream ZipOutputStream
2762      * @param name filename
2763      * @param keepDirStructure KeepDirStructure
2764      * @param file file
2765      */
isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean keepDirStructure, File file)2766     private void isNameEmpty(ZipOutputStream zipOutputStream, String name, boolean keepDirStructure, File file) {
2767         if (!name.isEmpty()) {
2768             compress(file, zipOutputStream, name + "/" + file.getName(), keepDirStructure);
2769         } else {
2770             compress(file, zipOutputStream, file.getName(), keepDirStructure);
2771         }
2772     }
2773 
2774     /**
2775      * compress process.
2776      *
2777      * @param utility       common data
2778      * @param srcFile       source file to zip
2779      * @param baseDir       current directory name of file
2780      * @param isCompression if need compression
2781      * @throws BundleException FileNotFoundException|IOException.
2782      */
compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)2783     private void compressFile(Utility utility, File srcFile, String baseDir, boolean isCompression)
2784             throws BundleException {
2785         BufferedInputStream bufferedInputStream = null;
2786         FileInputStream fileInputStream = null;
2787         try {
2788             String entryName = (baseDir + srcFile.getName()).replace(File.separator, LINUX_FILE_SEPARATOR);
2789             ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName);
2790             isEntryOpen = true;
2791             if (!entryName.contains(RAW_FILE_PATH)
2792                     && !entryName.contains(RES_FILE_PATH)
2793                     && srcFile.getName().toLowerCase(Locale.ENGLISH).endsWith(JSON_SUFFIX)
2794                     && !entryName.equals(Constants.FILE_ENCRYPT_JSON)
2795                     && !entryName.equals(Constants.FILE_PAC_JSON)) {
2796                 zipEntry.setMethod(ZipEntry.STORED);
2797                 if (jsonSpecialProcess(utility, srcFile, zipEntry)) {
2798                     return;
2799                 }
2800             }
2801 
2802             if (isCompression) {
2803                 zipEntry.setMethod(ZipEntry.DEFLATED);
2804             } else {
2805                 zipEntry.setMethod(ZipEntry.STORED);
2806 
2807                 // update size
2808                 zipEntry.setCompressedSize(srcFile.length());
2809                 zipEntry.setSize(srcFile.length());
2810 
2811                 // update crc
2812                 CRC32 crc = getCrcFromFile(utility, srcFile);
2813                 zipEntry.setCrc(crc.getValue());
2814             }
2815 
2816             zipOut.putArchiveEntry(zipEntry);
2817             byte[] data = new byte[BUFFER_SIZE];
2818             fileInputStream = new FileInputStream(srcFile);
2819             bufferedInputStream = new BufferedInputStream(fileInputStream);
2820 
2821             int count = bufferedInputStream.read(data);
2822             while (count > 0) {
2823                 zipOut.write(data, 0, count);
2824                 count = bufferedInputStream.read(data);
2825             }
2826         } catch (FileNotFoundException ignored) {
2827             throw new BundleException("CompressFile failed.");
2828         } catch (IOException exception) {
2829             LOG.error(PackingToolErrMsg.COMPRESS_FILE_EXCEPTION.toString(
2830                 "IOException: " + exception.getMessage()));
2831             throw new BundleException("CompressFile failed.");
2832         } finally {
2833             Utility.closeStream(bufferedInputStream);
2834             Utility.closeStream(fileInputStream);
2835         }
2836     }
2837 
2838     /**
2839      * check hap type for pack app.
2840      *
2841      * @param hapPath source file to zip
2842      * @return true is for is stage type and false is for FA type
2843      * @throws BundleException FileNotFoundException|IOException.
2844      */
isModuleHap(String hapPath)2845     public static boolean isModuleHap(String hapPath) throws BundleException {
2846         if (!hapPath.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)) {
2847             return true;
2848         }
2849 
2850         FileInputStream zipInput = null;
2851         ZipInputStream zin = null;
2852         ZipEntry entry = null;
2853         try {
2854             zipInput = new FileInputStream(hapPath);
2855             zin = new ZipInputStream(zipInput);
2856             while ((entry = zin.getNextEntry()) != null) {
2857                 if (MODULE_JSON.equals(entry.getName().toLowerCase())) {
2858                     return true;
2859                 }
2860             }
2861         } catch (IOException exception) {
2862             LOG.error(PackingToolErrMsg.FILE_IO_EXCEPTION.toString(
2863                     "Check module type exist IOException: " + exception.getMessage()));
2864             throw new BundleException("Check module type for pack app failed.");
2865         } finally {
2866             Utility.closeStream(zipInput);
2867             Utility.closeStream(zin);
2868         }
2869         return false;
2870     }
2871 
2872     /**
2873      * get CRC32 from file.
2874      *
2875      * @param utility common data
2876      * @param file    source file
2877      * @return CRC32
2878      * @throws BundleException FileNotFoundException|IOException.
2879      */
getCrcFromFile(Utility utility, File file)2880     private CRC32 getCrcFromFile(Utility utility, File file) throws BundleException {
2881         FileInputStream fileInputStream = null;
2882         CRC32 crc = new CRC32();
2883         try {
2884             fileInputStream = new FileInputStream(file);
2885             byte[] buffer = new byte[BUFFER_SIZE];
2886 
2887             int count = fileInputStream.read(buffer);
2888             while (count > 0) {
2889                 crc.update(buffer, 0, count);
2890                 count = fileInputStream.read(buffer);
2891             }
2892         } catch (FileNotFoundException ignored) {
2893             LOG.error(PackingToolErrMsg.FILE_NOT_FOUND.toString("File not found when getting Crc."));
2894             throw new BundleException("Get Crc from file failed: " + file.getName());
2895         } catch (IOException exception) {
2896             LOG.error(PackingToolErrMsg.FILE_IO_EXCEPTION.toString(
2897                     "Get Crc32 from file exist IOException: " + exception.getMessage()));
2898             throw new BundleException("Get Crc from file failed.");
2899         } finally {
2900             Utility.closeStream(fileInputStream);
2901         }
2902         return crc;
2903     }
2904 
parsePackInfoJsonFile(Utility utility, String jsonPath)2905     private void parsePackInfoJsonFile(Utility utility, String jsonPath)
2906             throws BundleException {
2907         try {
2908             String jsonString = FileUtils.getFileContent(jsonPath).get();
2909             List<String> nameList = new ArrayList<>();
2910             ModuleJsonUtil.parsePackInfoFormsName(jsonString, nameList, formNamesList);
2911             for (String formName : nameList) {
2912                 if (formName.isEmpty()) {
2913                     LOG.error(PackingToolErrMsg.PARSE_PACK_INFO_JSON_FAILED.toString("Form name parsed from " +
2914                             "pack.info is empty."));
2915                     continue;
2916                 }
2917 
2918                 utility.addFormNameList(formName);
2919             }
2920         } catch (JSONException e) {
2921             LOG.error(PackingToolErrMsg.PARSE_PACK_INFO_JSON_FAILED.toString("Parse pack.info eixst JSONException: " +
2922                     e.getMessage()));
2923         }
2924     }
2925 
infoSpecialProcess(Utility utility, File srcFile)2926     private void infoSpecialProcess(Utility utility, File srcFile)
2927             throws BundleException {
2928         FileInputStream fileInputStream = null;
2929         BufferedReader bufferedReader = null;
2930         InputStreamReader inputStreamReader = null;
2931 
2932         try {
2933             fileInputStream = new FileInputStream(srcFile);
2934             inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
2935             bufferedReader = new BufferedReader(inputStreamReader);
2936             bufferedReader.mark((int) srcFile.length() + 1);
2937             // parse moduleName from pack.info
2938             parsePackModuleName(bufferedReader, utility);
2939             bufferedReader.reset();
2940             parseDeviceType(bufferedReader, utility);
2941             bufferedReader.reset();
2942 
2943             Pattern pattern = Pattern.compile(System.lineSeparator());
2944             String str = bufferedReader.readLine();
2945             StringBuilder builder = new StringBuilder();
2946             while (str != null) {
2947                 Matcher matcher = pattern.matcher(str.trim());
2948                 String dest = matcher.replaceAll("");
2949                 builder.append(dest);
2950                 str = bufferedReader.readLine();
2951             }
2952         } catch (IOException exception) {
2953             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("pack.info special process exist IOException: "
2954                     + exception.getMessage()));
2955             throw new BundleException("Json special process failed.");
2956         } finally {
2957             Utility.closeStream(bufferedReader);
2958             Utility.closeStream(inputStreamReader);
2959             Utility.closeStream(fileInputStream);
2960         }
2961     }
2962 
2963     /**
2964      * trim and remove "\r\n" in *.json file.
2965      *
2966      * @param utility common data
2967      * @param srcFile file input
2968      * @param entry   zip file entry
2969      * @return true if process success
2970      */
jsonSpecialProcess(Utility utility, File srcFile, ZipArchiveEntry entry)2971     private boolean jsonSpecialProcess(Utility utility, File srcFile, ZipArchiveEntry entry) {
2972         FileInputStream fileInputStream = null;
2973         BufferedReader bufferedReader = null;
2974         InputStreamReader inputStreamReader = null;
2975 
2976         try {
2977             fileInputStream = new FileInputStream(srcFile);
2978             inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
2979             bufferedReader = new BufferedReader(inputStreamReader);
2980             bufferedReader.mark((int) srcFile.length() + 1);
2981             bufferedReader.reset();
2982             Optional<String> optional = FileUtils.getFileContent(utility.getJsonPath());
2983             String jsonString = optional.get();
2984             String jsonName = new File(utility.getJsonPath()).getName().toLowerCase(Locale.ENGLISH);
2985             if (CONFIG_JSON.equals(jsonName)) {
2986                 parseCompressNativeLibs(bufferedReader, utility);
2987                 utility.setModuleName(ModuleJsonUtil.parseFaModuleName(jsonString));
2988             } else if (MODULE_JSON.equals(jsonName)) {
2989                 utility.setIsCompressNativeLibs(ModuleJsonUtil.stageIsCompressNativeLibs(jsonString));
2990                 utility.setModuleName(ModuleJsonUtil.parseStageModuleName(jsonString));
2991             } else if (PATCH_JSON.equals(jsonName)) {
2992                 utility.setModuleName(ModuleJsonUtil.parsePatchModuleName(jsonString));
2993             }
2994             bufferedReader.reset();
2995             parseDeviceType(bufferedReader, utility);
2996             bufferedReader.reset();
2997 
2998             Pattern pattern = Pattern.compile(System.lineSeparator());
2999             String str = bufferedReader.readLine();
3000             StringBuilder builder = new StringBuilder();
3001             while (str != null) {
3002                 Matcher matcher = pattern.matcher(str.trim());
3003                 String dest = matcher.replaceAll("");
3004                 builder.append(dest);
3005                 str = bufferedReader.readLine();
3006             }
3007             Object jsonObject = JSON.parse(builder.toString());
3008             byte[] trimJson = JSON.toJSONBytes(jsonObject,
3009                     SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
3010 
3011             // update crc
3012             CRC32 crc = new CRC32();
3013             crc.update(trimJson);
3014             entry.setCrc(crc.getValue());
3015 
3016             // update size
3017             entry.setSize(trimJson.length);
3018             entry.setCompressedSize(trimJson.length);
3019 
3020             // compress data
3021             zipOut.putArchiveEntry(entry);
3022             zipOut.write(trimJson);
3023         } catch (Exception exception) {
3024             LOG.error(PackingToolErrMsg.JSON_SPECIAL_PROCESS_FAILED.toString("Exception: " + exception.getMessage()));
3025             LOG.warning("Json format err: " + srcFile.getAbsolutePath());
3026             return false;
3027         } finally {
3028             Utility.closeStream(bufferedReader);
3029             Utility.closeStream(inputStreamReader);
3030             Utility.closeStream(fileInputStream);
3031         }
3032         return true;
3033     }
3034 
3035     /**
3036      * Parse module name from pack.info
3037      *
3038      * @param bufferedReader pack.info buffered Reader
3039      * @param utility        common data
3040      * @throws BundleException IOException
3041      */
parsePackModuleName(BufferedReader bufferedReader, Utility utility)3042     private void parsePackModuleName(BufferedReader bufferedReader, Utility utility) throws BundleException {
3043         String lineStr = null;
3044         try {
3045             while ((lineStr = bufferedReader.readLine()) != null) {
3046                 if (lineStr.contains(DISTRO)) {
3047                     continue;
3048                 }
3049                 if (lineStr.contains(JSON_END)) {
3050                     continue;
3051                 }
3052                 if (lineStr.contains(MODULE_NAME_NEW) || lineStr.contains(MODULE_NAME)) {
3053                     getModuleNameFromString(lineStr, utility);
3054                 }
3055             }
3056         } catch (IOException exception) {
3057             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Parse module name exist IOException: "
3058                     + exception.getMessage()));
3059             throw new BundleException("Parse module name failed.");
3060         }
3061     }
3062 
3063     /**
3064      * Parse Forms name from pack.info
3065      *
3066      * @param bufferedReader pack.info buffered Reader
3067      * @param utility        common data
3068      * @throws BundleException IOException
3069      */
parsePackFormName(BufferedReader bufferedReader, Utility utility)3070     private void parsePackFormName(BufferedReader bufferedReader, Utility utility) throws BundleException {
3071         String lineStr = null;
3072         try {
3073             while ((lineStr = bufferedReader.readLine()) != null) {
3074                 if (lineStr.contains("abilities")) {
3075                     continue;
3076                 }
3077                 if (lineStr.contains(FORMS)) {
3078                     continue;
3079                 }
3080                 if (lineStr.contains(JSON_END)) {
3081                     continue;
3082                 }
3083                 if (lineStr.contains(NAME)) {
3084                     getNameFromString(lineStr, utility);
3085                 }
3086             }
3087         } catch (IOException exception) {
3088             LOG.error("Compressor::parseModuleName io exception: " + exception.getMessage());
3089             throw new BundleException("Parse module name failed.");
3090         }
3091     }
3092 
3093     /**
3094      * Get name from line string
3095      *
3096      * @param lineStr line string
3097      * @param utility common data
3098      * @throws BundleException StringIndexOutOfBoundsException
3099      */
getNameFromString(String lineStr, Utility utility)3100     private void getNameFromString(String lineStr, Utility utility) throws BundleException {
3101         try {
3102             int endIndex = lineStr.lastIndexOf(SEMICOLON);
3103             if (endIndex <= 0) {
3104                 LOG.error("Compressor::getModuleNameFromString field the json is not standard.");
3105                 throw new BundleException("Parse module name failed, module-name is invalid.");
3106             }
3107             int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1;
3108             String formName = lineStr.substring(startIndex, endIndex);
3109             if (formName == null || formName.isEmpty()) {
3110                 LOG.error("Compressor::getModuleNameFromString field module-name is empty.");
3111                 throw new BundleException("Parse module name failed, module-name is empty.");
3112             }
3113             String[] nameList = formName.split("\\.");
3114             if (nameList.length <= 1) {
3115                 formNamesList.add(formName);
3116                 utility.addFormNameList(formName);
3117             }
3118         } catch (StringIndexOutOfBoundsException exception) {
3119             LOG.error("Compressor::parseModuleName field module-name is fault: " + exception.getMessage());
3120             throw new BundleException("Parse module name failed, module-name is invalid.");
3121         }
3122     }
3123 
3124     /**
3125      * Get module name from line string
3126      *
3127      * @param lineStr line string
3128      * @param utility common data
3129      * @throws BundleException StringIndexOutOfBoundsException
3130      */
getModuleNameFromString(String lineStr, Utility utility)3131     private void getModuleNameFromString(String lineStr, Utility utility) throws BundleException {
3132         try {
3133             int endIndex = lineStr.lastIndexOf(SEMICOLON);
3134             if (endIndex <= 0) {
3135                 LOG.error(PackingToolErrMsg.GET_MODULE_NAME_FROM_STRING_FAILED.toString("Unable to find '\"' "
3136                         + "in the lineStr."));
3137                 throw new BundleException("Parse module name failed, module-name is invalid.");
3138             }
3139             int startIndex = lineStr.lastIndexOf(SEMICOLON, endIndex - 1) + 1;
3140             String moduleName = lineStr.substring(startIndex, endIndex);
3141             list.add(moduleName);
3142             if (moduleName == null || moduleName.isEmpty()) {
3143                 LOG.error(PackingToolErrMsg.GET_MODULE_NAME_FROM_STRING_FAILED.toString("moduleName or module-name "
3144                         + "is empty."));
3145                 throw new BundleException("Parse module name failed, module-name is empty.");
3146             }
3147             utility.setModuleName(moduleName);
3148         } catch (StringIndexOutOfBoundsException exception) {
3149             LOG.error(PackingToolErrMsg.GET_MODULE_NAME_FROM_STRING_FAILED.toString("Get Module name from line string "
3150                     + "exist StringIndexOutOfBoundsException: " + exception.getMessage()));
3151             throw new BundleException("Parse module name failed, module-name is invalid.");
3152         }
3153     }
3154 
parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility)3155     private void parseCompressNativeLibs(BufferedReader bufferedReader, Utility utility) throws BundleException {
3156         String lineStr = null;
3157         try {
3158             while ((lineStr = bufferedReader.readLine()) != null) {
3159                 if (lineStr.contains(COMPRESS_NATIVE_LIBS)) {
3160                     if (lineStr.contains(Utility.TRUE_STRING)) {
3161                         utility.setIsCompressNativeLibs(true);
3162                         break;
3163                     }
3164                 }
3165             }
3166         } catch (IOException exception) {
3167             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
3168                     "Parse compress native libs exist IOException: " + exception.getMessage()));
3169             throw new BundleException("Parse compress native libs failed.");
3170         }
3171     }
3172 
3173     /**
3174      * ZipOutputStream flush, closeEntry and finish.
3175      */
closeZipOutputStream()3176     private void closeZipOutputStream() {
3177         try {
3178             if (zipOut != null) {
3179                 zipOut.flush();
3180             }
3181         } catch (IOException exception) {
3182             LOG.error(PackingToolErrMsg.CLOSE_ZIP_OUTPUT_STREAM_EXCEPTION.toString(
3183                     "Close zip output stream flush IOException: " + exception.getMessage()));
3184         }
3185         try {
3186             if (zipOut != null && isEntryOpen) {
3187                 zipOut.closeArchiveEntry();
3188             }
3189         } catch (IOException exception) {
3190             LOG.error(PackingToolErrMsg.CLOSE_ZIP_OUTPUT_STREAM_EXCEPTION.toString(
3191                     "Close entry IOException: " + exception.getMessage()));
3192         }
3193         try {
3194             if (zipOut != null) {
3195                 zipOut.finish();
3196             }
3197         } catch (IOException exception) {
3198             LOG.error(PackingToolErrMsg.CLOSE_ZIP_OUTPUT_STREAM_EXCEPTION.toString(
3199                     "Close zip output stream flush IOException: " + exception.getMessage()));
3200         }
3201     }
3202 
3203     /**
3204      * Parse device type from config.json
3205      *
3206      * @param bufferedReader config.json buffered Reader
3207      * @param utility        common data
3208      * @throws BundleException IOException
3209      */
parseDeviceType(BufferedReader bufferedReader, Utility utility)3210     private void parseDeviceType(BufferedReader bufferedReader, Utility utility) throws BundleException {
3211         String lineStr = null;
3212         boolean isDeviceTypeStart = false;
3213         try {
3214             while ((lineStr = bufferedReader.readLine()) != null) {
3215                 if (!isDeviceTypeStart) {
3216                     if (lineStr.contains(DEVICE_TYPE)) {
3217                         isDeviceTypeStart = true;
3218                     }
3219                     continue;
3220                 }
3221                 if (lineStr.contains(JSON_END)) {
3222                     break;
3223                 }
3224                 utility.setDeviceType(lineStr);
3225                 break;
3226             }
3227         } catch (IOException exception) {
3228             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString(
3229                     "Parse device type exist IOException: " + exception.getMessage()));
3230             throw new BundleException("Parse device type failed.");
3231         }
3232     }
3233 
3234     /**
3235      * check hap and hsp is valid in haps when pack app, check type has bundleName,
3236      * vendor, version, apiVersion moduleName, packageName.
3237      *
3238      * @param fileLists is the list of hapPath.
3239      * @return true is for successful and false is for failed
3240      * @throws BundleException FileNotFoundException|IOException.
3241      */
checkHapIsValid(List<String> fileLists, boolean isSharedApp)3242     private boolean checkHapIsValid(List<String> fileLists, boolean isSharedApp) throws BundleException {
3243         List<HapVerifyInfo> hapVerifyInfos = new ArrayList<>();
3244         for (String hapPath : fileLists) {
3245             if (hapPath.isEmpty()) {
3246                 LOG.error(PackingToolErrMsg.INVALID_HAP_FILE.toString("Invalid hap or hsp file input."));
3247                 throw new BundleException("The hap or hsp files are invalid, or the wrong file was provided.");
3248             }
3249             File srcFile = new File(hapPath);
3250             String fileStr = srcFile.getName();
3251             if (fileStr.isEmpty()) {
3252                 LOG.error(PackingToolErrMsg.INVALID_HAP_FILE.toString("Get file name failed."));
3253                 throw new BundleException("Get file name from the provided path failed.");
3254             }
3255             if (!fileStr.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)
3256                     && !fileStr.toLowerCase(Locale.ENGLISH).endsWith(HSP_SUFFIX)) {
3257                 LOG.error(PackingToolErrMsg.INVALID_HAP_FILE.toString("Invalid hap or hsp file input."));
3258                 throw new BundleException("The provided file is not a valid hap or hsp file.");
3259             }
3260             if (isModuleHap(hapPath)) {
3261                 hapVerifyInfos.add(parseStageHapVerifyInfo(hapPath));
3262             } else {
3263                 hapVerifyInfos.add(parseFAHapVerifyInfo(hapPath));
3264             }
3265 
3266             if (!hapVerifyInfos.isEmpty()) {
3267                 hapVerifyInfoMap.put(getFileNameByPath(hapPath), hapVerifyInfos.get(hapVerifyInfos.size() - 1));
3268             }
3269         }
3270 
3271         if (isSharedApp) {
3272             boolean res = checkSharedAppIsValid(hapVerifyInfos);
3273             if (!res) {
3274                 return false;
3275             }
3276             if (!isOverlay) {
3277                 return true;
3278             }
3279         } else {
3280             for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
3281                 String bundleType = hapVerifyInfo.getBundleType();
3282                 if (TYPE_SHARED.equals(bundleType)) {
3283                     String cause = "Only one item can be entered in the --hsp-path when bundleType is shared.";
3284                     String solution = "Ensure that only one item entered in the --hsp-path when bundleType is shared.";
3285                     LOG.error(PackingToolErrMsg.CHECK_BUNDLETYPE_INVALID.toString(cause, solution));
3286                     return false;
3287                 }
3288             }
3289         }
3290         setAtomicServiceFileSizeLimit(hapVerifyInfos);
3291         if (!HapVerify.checkHapIsValid(hapVerifyInfos)) {
3292             return false;
3293         }
3294         return true;
3295     }
3296 
3297     /**
3298      * parse stage file to hap verify info from hap path.
3299      *
3300      * @param filePath is the hap path
3301      * @return hapVerifyInfo
3302      */
parseStageHapVerifyInfo(String filePath)3303     public static HapVerifyInfo parseStageHapVerifyInfo(String filePath) throws BundleException {
3304         HapVerifyInfo hapVerifyInfo = readStageHapVerifyInfo(filePath);
3305         hapVerifyInfo.setStageModule(true);
3306         ModuleJsonUtil.parseStageHapVerifyInfo(hapVerifyInfo);
3307         hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath));
3308         File srcFile = new File(filePath);
3309         String fileStr = srcFile.getName();
3310         if (fileStr.toLowerCase(Locale.ENGLISH).endsWith(HAP_SUFFIX)) {
3311             hapVerifyInfo.setFileType(HAP_SUFFIX);
3312         } else if (fileStr.toLowerCase(Locale.ENGLISH).endsWith(HSP_SUFFIX)) {
3313             hapVerifyInfo.setFileType(HSP_SUFFIX);
3314         }
3315         return hapVerifyInfo;
3316     }
3317 
3318     /**
3319      * set file size limit for each HapVerifyInfo.
3320      *
3321      * @param hapVerifyInfos Indicates hapVerifyInfo list.
3322      */
setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos)3323     public static void setAtomicServiceFileSizeLimit(List<HapVerifyInfo>hapVerifyInfos) {
3324         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
3325             if (!hapVerifyInfo.getBundleType().equals(ATOMIC_SERVICE)) {
3326                 continue;
3327             }
3328             hapVerifyInfo.setEntrySizeLimit(getEntryModuleSizeLimit());
3329             hapVerifyInfo.setNotEntrySizeLimit(getNotEntryModuleSizeLimit());
3330             hapVerifyInfo.setSumSizeLimit(getSumModuleSizeLimit());
3331         }
3332     }
3333 
3334     /**
3335      * parse fa file to hap verify info from hap path.
3336      *
3337      * @param filePath is the hap path
3338      * @return hapVerifyInfo
3339      */
parseFAHapVerifyInfo(String filePath)3340     public static HapVerifyInfo parseFAHapVerifyInfo(String filePath) throws BundleException {
3341         HapVerifyInfo hapVerifyInfo = readFAHapVerifyInfo(filePath);
3342         hapVerifyInfo.setStageModule(false);
3343         hapVerifyInfo.setFileLength(FileUtils.getFileSize(filePath));
3344         ModuleJsonUtil.parseFAHapVerifyInfo(hapVerifyInfo);
3345         return hapVerifyInfo;
3346     }
3347 
3348     /**
3349      * read stage hap verify info from hap file.
3350      *
3351      * @param srcPath source file to zip
3352      * @return HapVerifyInfo of parse result
3353      * @throws BundleException FileNotFoundException|IOException.
3354      */
readStageHapVerifyInfo(String srcPath)3355     public static HapVerifyInfo readStageHapVerifyInfo(String srcPath) throws BundleException {
3356         HapVerifyInfo hapVerifyInfo = new HapVerifyInfo();
3357         ZipFile zipFile = null;
3358         try {
3359             File srcFile = new File(srcPath);
3360             zipFile = new ZipFile(srcFile);
3361             hapVerifyInfo.setResourceMap(FileUtils.getProfileJson(zipFile));
3362             hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(MODULE_JSON, zipFile));
3363         } catch (IOException e) {
3364             LOG.error(PackingToolErrMsg.READ_STAGE_HAP_VERIFY_INFO_FAILED.toString(
3365                     "Read Stage hap verify info file exist IOException: " + e.getMessage()));
3366             throw new BundleException("Parse Stage hap verify info file exist IOException.");
3367         } finally {
3368             Utility.closeStream(zipFile);
3369         }
3370         return hapVerifyInfo;
3371     }
3372 
3373     /**
3374      * read fa hap verify info from hap file.
3375      *
3376      * @param srcPath source file to zip
3377      * @return HapVerifyInfo of parse result
3378      * @throws BundleException FileNotFoundException|IOException.
3379      */
readFAHapVerifyInfo(String srcPath)3380     public static HapVerifyInfo readFAHapVerifyInfo(String srcPath) throws BundleException {
3381         HapVerifyInfo hapVerifyInfo = new HapVerifyInfo();
3382         ZipFile zipFile = null;
3383         try {
3384             File srcFile = new File(srcPath);
3385             zipFile = new ZipFile(srcFile);
3386             hapVerifyInfo.setProfileStr(FileUtils.getFileStringFromZip(CONFIG_JSON, zipFile));
3387         } catch (IOException e) {
3388             LOG.error(PackingToolErrMsg.READ_FA_HAP_VERIFY_INFO_FAILED.toString(
3389                     "Read FA hap verify info file exist IOException: " + e.getMessage()));
3390             throw new BundleException("Parse FA hap verify info file exist IOException.");
3391         } finally {
3392             Utility.closeStream(zipFile);
3393         }
3394         return hapVerifyInfo;
3395     }
3396 
3397     /**
3398      * compress in hqf mode.
3399      *
3400      * @param utility common data
3401      * @throws BundleException FileNotFoundException|IOException.
3402      */
compressHQFMode(Utility utility)3403     private void compressHQFMode(Utility utility) throws BundleException {
3404         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
3405 
3406         if (!utility.getEtsPath().isEmpty()) {
3407             pathToFile(utility, utility.getEtsPath(), ETS_PATH, false);
3408         }
3409         if (!utility.getLibPath().isEmpty()) {
3410             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, false);
3411         }
3412         if (!utility.getResourcesPath().isEmpty()) {
3413             pathToFile(utility, utility.getResourcesPath(), RESOURCES_DIR_NAME, false);
3414         }
3415     }
3416 
3417     /**
3418      * compress in appqf mode.
3419      *
3420      * @param utility common data
3421      * @throws BundleException FileNotFoundException|IOException.
3422      */
compressAPPQFMode(Utility utility)3423     private void compressAPPQFMode(Utility utility) throws BundleException {
3424         List<String> fileList = utility.getFormatedHQFList();
3425         if (!checkHQFIsValid(fileList)) {
3426             LOG.error(PackingToolErrMsg.COMPRESS_APPQF_FAILED.toString("Verify failed when compress appqf."));
3427             throw new BundleException("Verify failed when compress appqf.");
3428         }
3429         for (String hapPath : fileList) {
3430             pathToFile(utility, hapPath, NULL_DIR_NAME, false);
3431         }
3432     }
3433 
3434     /**
3435      * check input hqf is valid.
3436      *
3437      * @param fileList is input path of hqf files
3438      * @throws BundleException FileNotFoundException|IOException.
3439      */
checkHQFIsValid(List<String> fileList)3440     private boolean checkHQFIsValid(List<String> fileList) throws BundleException {
3441         List<HQFInfo> hqfVerifyInfos = new ArrayList<>();
3442         for (String file : fileList) {
3443             hqfVerifyInfos.add(ModuleJsonUtil.parseHQFInfo(file));
3444         }
3445         if (!HQFVerify.checkHQFIsValid(hqfVerifyInfos)) {
3446             LOG.error(PackingToolErrMsg.CHECK_HQF_INVALID.toString("Input hqf is invalid."));
3447             return false;
3448         }
3449         return true;
3450     }
3451 
compressHSPMode(Utility utility)3452     private void compressHSPMode(Utility utility) throws BundleException {
3453         pathToFile(utility, utility.getJsonPath(), NULL_DIR_NAME, false);
3454 
3455         pathToFile(utility, utility.getProfilePath(), NULL_DIR_NAME, false);
3456 
3457         if (!utility.getIndexPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
3458             String assetsPath = NULL_DIR_NAME;
3459             pathToFile(utility, utility.getIndexPath(), assetsPath, false);
3460         }
3461 
3462         if (!utility.getLibPath().isEmpty()) {
3463             pathToFile(utility, utility.getLibPath(), LIBS_DIR_NAME, utility.isCompressNativeLibs());
3464         }
3465 
3466         if (!utility.getANPath().isEmpty()) {
3467             pathToFile(utility, utility.getANPath(), AN_DIR_NAME, false);
3468         }
3469 
3470         if (!utility.getAPPath().isEmpty()) {
3471             pathToFile(utility, utility.getAPPath(), AP_PATH_NAME, false);
3472         }
3473 
3474         if (!utility.getFilePath().isEmpty()) {
3475             pathToFile(utility, utility.getFilePath(), NULL_DIR_NAME, false);
3476         }
3477 
3478         if (!utility.getResPath().isEmpty() && !utility.getModuleName().isEmpty()) {
3479             String resPath = ASSETS_DIR_NAME + utility.getModuleName() + LINUX_FILE_SEPARATOR
3480                     + RESOURCES_DIR_NAME;
3481             if (DEVICE_TYPE_FITNESSWATCH.equals(
3482                     utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim()) ||
3483                     DEVICE_TYPE_FITNESSWATCH_NEW.equals(
3484                             utility.getDeviceType().replace(SEMICOLON, EMPTY_STRING).trim())) {
3485                 resPath = RES_DIR_NAME;
3486             }
3487             pathToFile(utility, utility.getResPath(), resPath, false);
3488         }
3489 
3490         if (!utility.getResourcesPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
3491             String resourcesPath = RESOURCES_DIR_NAME;
3492             pathToFile(utility, utility.getResourcesPath(), resourcesPath, false);
3493         }
3494         if (!utility.getJsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
3495             String jsPath = JS_PATH;
3496             pathToFile(utility, utility.getJsPath(), jsPath, false);
3497         }
3498 
3499         if (!utility.getEtsPath().isEmpty() && isModuleJSON(utility.getJsonPath())) {
3500             String etsPath = ETS_PATH;
3501             pathToFile(utility, utility.getEtsPath(), etsPath, false);
3502         }
3503 
3504         if (!utility.getRpcidPath().isEmpty()) {
3505             String rpcidPath = NULL_DIR_NAME;
3506             pathToFile(utility, utility.getRpcidPath(), rpcidPath, false);
3507         }
3508 
3509         if (!utility.getAssetsPath().isEmpty()) {
3510             pathToFile(utility, utility.getAssetsPath(), ASSETS_DIR_NAME, false);
3511         }
3512 
3513         if (!utility.getBinPath().isEmpty()) {
3514             pathToFile(utility, utility.getBinPath(), NULL_DIR_NAME, false);
3515         }
3516 
3517         if (!utility.getPackInfoPath().isEmpty()) {
3518             pathToFile(utility, utility.getPackInfoPath(), NULL_DIR_NAME, false);
3519         }
3520 
3521         // pack --dir-list
3522         if (!utility.getFormatedDirList().isEmpty()) {
3523             for (int i = 0; i < utility.getFormatedDirList().size(); ++i) {
3524                 String baseDir = new File(utility.getFormatedDirList().get(i)).getName() + File.separator;
3525                 pathToFile(utility, utility.getFormatedDirList().get(i), baseDir, false);
3526             }
3527         }
3528         if (!utility.getPkgContextPath().isEmpty()) {
3529             pathToFile(utility, utility.getPkgContextPath(), NULL_DIR_NAME, false);
3530         }
3531 
3532         compressHapModeMultiple(utility);
3533     }
3534 
checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos)3535     private static boolean checkSharedAppIsValid(List<HapVerifyInfo> hapVerifyInfos) throws BundleException {
3536         if (hapVerifyInfos.isEmpty()) {
3537             String cause = "Hap verify infos is empty.";
3538             LOG.error(PackingToolErrMsg.CHECK_HAP_VERIFY_INFO_LIST_EMPTY.toString(cause));
3539             return false;
3540         }
3541         if (hapVerifyInfos.size() > SHARED_APP_HSP_LIMIT) {
3542             String cause = "The shared App only can contain one module.";
3543             String solution = "Please ensure that there is only one module in the shared App.";
3544             LOG.error(PackingToolErrMsg.CHECK_SHARED_APP_INVALID.toString(cause, solution));
3545             return false;
3546         }
3547         for (HapVerifyInfo hapVerifyInfo : hapVerifyInfos) {
3548             if (!hapVerifyInfo.getTargetBundleName().isEmpty()) {
3549                 isOverlay = true;
3550                 return true;
3551             }
3552         }
3553         return HapVerify.checkSharedApppIsValid(hapVerifyInfos);
3554     }
3555 
versionNormalize(Utility utility)3556     private void versionNormalize(Utility utility) {
3557         List<VersionNormalizeUtil> utils = new ArrayList<>();
3558         Path tempDir = null;
3559         for (String hapPath : utility.getFormattedHapList()) {
3560             try {
3561                 tempDir = Files.createTempDirectory(Paths.get(utility.getOutPath()), "temp");
3562 
3563                 unpackHap(hapPath, tempDir.toAbsolutePath().toString());
3564                 VersionNormalizeUtil util = new VersionNormalizeUtil();
3565                 File moduleFile = new File(
3566                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + MODULE_JSON);
3567                 File configFile = new File(
3568                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + CONFIG_JSON);
3569                 File packInfoFile = new File(
3570                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + PACKINFO_NAME);
3571 
3572                 if (moduleFile.exists() && configFile.exists()) {
3573                     LOG.error(PackingToolErrMsg.VERSION_NORMALIZE_FAILED.toString("Invalid hap structure."));
3574                     throw new BundleException("versionNormalize failed, invalid hap structure.");
3575                 }
3576                 if (moduleFile.exists()) {
3577                     String moduleJsonPath = tempDir.resolve(MODULE_JSON).toString();
3578                     util = parseAndModifyModuleJson(moduleJsonPath, utility);
3579                 } else if (configFile.exists()) {
3580                     String configJsonPath = tempDir.resolve(CONFIG_JSON).toString();
3581                     util = parseAndModifyConfigJson(configJsonPath, utility);
3582                 } else {
3583                     LOG.error(PackingToolErrMsg.VERSION_NORMALIZE_FAILED.toString("Invalid hap structure."));
3584                     throw new BundleException("versionNormalize failed, invalid hap structure.");
3585                 }
3586                 if (packInfoFile.exists()) {
3587                     String packInfoPath = tempDir.resolve(PACKINFO_NAME).toString();
3588                     parseAndModifyPackInfo(packInfoPath, utility);
3589                 }
3590 
3591                 verifyModuleVersion(util, utility);
3592                 utils.add(util);
3593 
3594                 String modifiedHapPath = Paths.get(utility.getOutPath()) +
3595                         LINUX_FILE_SEPARATOR + Paths.get(hapPath).getFileName().toString();
3596                 compressDirToHap(tempDir, modifiedHapPath);
3597             } catch (IOException | BundleException e) {
3598                 LOG.error(PackingToolErrMsg.VERSION_NORMALIZE_FAILED.toString(
3599                         "Version normalize exist Exception (IOException | BundleException): " + e.getMessage()));
3600             } finally {
3601                 if (tempDir != null) {
3602                     deleteDirectory(tempDir.toFile());
3603                 }
3604             }
3605         }
3606         writeVersionRecord(utils, utility.getOutPath());
3607     }
3608 
parseAndModifyModuleJson(String jsonFilePath, Utility utility)3609     private VersionNormalizeUtil parseAndModifyModuleJson(String jsonFilePath, Utility utility)
3610             throws BundleException {
3611         VersionNormalizeUtil util = new VersionNormalizeUtil();
3612         try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) {
3613             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
3614             if (!jsonObject.containsKey(APP)) {
3615                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The module.json file " +
3616                         "does not contain 'app'."));
3617                 throw new BundleException("The module.json file does not contain 'app'. ");
3618             }
3619             JSONObject appObject = jsonObject.getJSONObject(APP);
3620             if (!appObject.containsKey(VERSION_CODE)) {
3621                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The app object of " +
3622                         "module.json file does not contain 'versionCode'."));
3623                 throw new BundleException("The app object of module.json file does not contain 'versionCode'.");
3624             }
3625             if (!appObject.containsKey(VERSION_NAME)) {
3626                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The app object of " +
3627                         "module.json file does not contain 'versionName'."));
3628                 throw new BundleException("The app object of module.json file does not contain 'versionName'.");
3629             }
3630             util.setOriginVersionCode(appObject.getIntValue(VERSION_CODE));
3631             util.setOriginVersionName(appObject.getString(VERSION_NAME));
3632 
3633             JSONObject moduleObject = jsonObject.getJSONObject(MODULE);
3634             if (!moduleObject.containsKey(NAME)) {
3635                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The module object of " +
3636                         "module.json file does not contain 'name'."));
3637                 throw new BundleException("The module object of module.json file does not contain 'name'. ");
3638             }
3639             util.setModuleName(moduleObject.getString(NAME));
3640             appObject.put(VERSION_CODE, utility.getVersionCode());
3641             appObject.put(VERSION_NAME, utility.getVersionName());
3642             writeJson(jsonFilePath, jsonObject);
3643         } catch (IOException e) {
3644             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Parse and modify module.json exist IOException: " +
3645                     e.getMessage()));
3646             throw new BundleException("Parse and modify module.json exist IOException: " + e.getMessage());
3647         }
3648         return util;
3649     }
3650 
parseAndModifyPackInfo(String packInfoPath, Utility utility)3651     private void parseAndModifyPackInfo(String packInfoPath, Utility utility)
3652             throws BundleException {
3653         try (FileInputStream jsonStream = new FileInputStream(packInfoPath)) {
3654             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
3655             if (jsonObject == null) {
3656                 LOG.warning("parseAndModifyPackInfo failed, json format invalid.");
3657                 return;
3658             }
3659             JSONObject summaryObject = jsonObject.getJSONObject(SUMMARY);
3660             if (summaryObject == null) {
3661                 LOG.warning("parseAndModifyPackInfo failed, summary invalid.");
3662                 return;
3663             }
3664             JSONObject appObject = summaryObject.getJSONObject(APP);
3665             if (appObject == null) {
3666                 LOG.warning("parseAndModifyPackInfo failed, app invalid.");
3667                 return;
3668             }
3669             JSONObject versionObject = appObject.getJSONObject(VERSION);
3670             if (versionObject == null) {
3671                 LOG.warning("parseAndModifyPackInfo failed, version invalid.");
3672                 return;
3673             }
3674             versionObject.put(CODE, utility.getVersionCode());
3675             versionObject.put(NAME, utility.getVersionName());
3676             writeJson(packInfoPath, jsonObject);
3677         } catch (IOException e) {
3678             LOG.warning("parseAndModifyPackInfo failed, IOException." + e.getMessage());
3679         }
3680     }
3681 
writeJson(String jsonFilePath, JSONObject jsonObject)3682     private void writeJson(String jsonFilePath, JSONObject jsonObject) throws IOException, BundleException {
3683         BufferedWriter bw = null;
3684         try {
3685             String pretty = JSON.toJSONString(jsonObject, SerializerFeature.WriteMapNullValue,
3686                 SerializerFeature.WriteDateUseDateFormat);
3687             bw = new BufferedWriter(new OutputStreamWriter(
3688                     new FileOutputStream(jsonFilePath), StandardCharsets.UTF_8));
3689             bw.write(pretty);
3690         } catch (IOException exception) {
3691             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Write json exist IOException: "
3692                     + exception.getMessage()));
3693             throw new BundleException("Compressor::writeJson failed for IOException");
3694         } finally {
3695             if (bw != null) {
3696                 bw.flush();
3697                 bw.close();
3698             }
3699         }
3700     }
3701 
parseAndModifyConfigJson(String jsonFilePath, Utility utility)3702     private VersionNormalizeUtil parseAndModifyConfigJson(String jsonFilePath, Utility utility)
3703             throws BundleException {
3704         VersionNormalizeUtil util = new VersionNormalizeUtil();
3705         try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) {
3706             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
3707             if (!jsonObject.containsKey(APP)) {
3708                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The config.json file " +
3709                         "does not contain 'app'."));
3710                 throw new BundleException("The config.json file does not contain 'app'. ");
3711             }
3712             JSONObject appObject = jsonObject.getJSONObject(APP);
3713             if (!appObject.containsKey(VERSION)) {
3714                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The app object of config.json " +
3715                         "file does not contain 'version'."));
3716                 throw new BundleException("The app object of config.json file does not contain 'version'. ");
3717             }
3718             JSONObject versionObj = appObject.getJSONObject(VERSION);
3719             if (!versionObj.containsKey(CODE)) {
3720                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The version object of " +
3721                         "config.json file does not contain 'code'."));
3722                 throw new BundleException("The version object of config.json file does not contain 'code'. ");
3723             }
3724             util.setOriginVersionCode(versionObj.getIntValue(CODE));
3725             if (!versionObj.containsKey(NAME)) {
3726                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The version object of " +
3727                         "config.json file does not contain 'name'."));
3728                 throw new BundleException("The version object of config.json file does not contain 'name'. ");
3729             }
3730             util.setOriginVersionName(versionObj.getString(NAME));
3731 
3732             JSONObject moduleObject = jsonObject.getJSONObject(MODULE);
3733             if (!moduleObject.containsKey(NAME)) {
3734                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The module object of " +
3735                         "config.json file does not contain 'name'."));
3736                 throw new BundleException("The module object of config.json file does not contain 'name'. ");
3737             }
3738             util.setModuleName(moduleObject.getString(NAME));
3739 
3740             versionObj.put(CODE, utility.getVersionCode());
3741             versionObj.put(NAME, utility.getVersionName());
3742             writeJson(jsonFilePath, jsonObject);
3743         } catch (IOException e) {
3744             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Parse and modify config.json exist IOException: "
3745                     + e.getMessage()));
3746             throw new BundleException("Parse and modify config.json exist IOException: " + e.getMessage());
3747         }
3748         return util;
3749     }
3750 
compressDirToHap(Path sourceDir, String zipFilePath)3751     private boolean compressDirToHap(Path sourceDir, String zipFilePath)
3752             throws IOException, BundleException {
3753         Utility utility = new Utility();
3754         utility.setOutPath(zipFilePath);
3755         if (zipFilePath.endsWith(HAP_SUFFIX)) {
3756             utility.setMode(Utility.MODE_HAP);
3757         } else if (zipFilePath.endsWith(HSP_SUFFIX)) {
3758             utility.setMode(Utility.MODE_HSP);
3759         }
3760         try (Stream<Path> pathStream = Files.walk(sourceDir, 1)) {
3761             pathStream.forEach(path -> {
3762                 String fileName = path.getFileName().toString();
3763                 String filePath = path.toString();
3764 
3765                 switch (fileName) {
3766                     case ETS_FILE_NAME:
3767                         utility.setEtsPath(filePath);
3768                         break;
3769                     case HNP_FILE_NAME:
3770                         utility.setHnpPath(filePath);
3771                         break;
3772                     case LIBS_DIR:
3773                         utility.setLibPath(filePath);
3774                         break;
3775                     case AN_FILE_NAME:
3776                         utility.setANPath(filePath);
3777                         break;
3778                     case AP_FILE_NAME:
3779                         utility.setAPPath(filePath);
3780                         break;
3781                     case RESOURCE_FILE_NAME:
3782                         utility.setResourcesPath(filePath);
3783                         break;
3784                     case JS_FILE_NAME:
3785                         utility.setJsPath(filePath);
3786                         break;
3787                     case ASSETS_FILE_NAME:
3788                         utility.setAssetsPath(filePath);
3789                         break;
3790                     case MAPLE_FILE_NAME:
3791                         utility.setSoDir(filePath);
3792                         break;
3793                     case SHARED_LIBS_FILE_NAME:
3794                         utility.setSharedLibsPath(filePath);
3795                         break;
3796                     case CONFIG_JSON:
3797                         utility.setJsonPath(filePath);
3798                         break;
3799                     case MODULE_JSON:
3800                         utility.setJsonPath(filePath);
3801                         break;
3802                     case RES_INDEX:
3803                         utility.setIndexPath(filePath);
3804                         break;
3805                     case PACKINFO_NAME:
3806                         utility.setPackInfoPath(filePath);
3807                         break;
3808                     case RPCID:
3809                         utility.setRpcid(filePath);
3810                         break;
3811                     case PKG_CONTEXT_INFO:
3812                         utility.setPkgContextPath(filePath);
3813                         break;
3814                 }
3815             });
3816         }
3817         return compressProcess(utility);
3818     }
3819 
deleteDirectory(File dir)3820     private static void deleteDirectory(File dir) {
3821         if (dir.isDirectory()) {
3822             File[] children = dir.listFiles();
3823             if (children != null) {
3824                 for (File child : children) {
3825                     deleteDirectory(child);
3826                 }
3827             }
3828         }
3829         dir.delete();
3830     }
3831 
writeVersionRecord(List<VersionNormalizeUtil> utils, String outPath)3832     private static void writeVersionRecord(List<VersionNormalizeUtil> utils, String outPath) {
3833         String jsonString = JSON.toJSONString(utils);
3834         try (FileWriter fileWriter = new FileWriter(outPath + LINUX_FILE_SEPARATOR + VERSION_RECORD)) {
3835             fileWriter.write(jsonString);
3836         } catch (IOException e) {
3837             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Write version record exist IOException: "
3838                     + e.getMessage()));
3839         }
3840     }
3841 
verifyModuleVersion(VersionNormalizeUtil versionNormalizeUtil, Utility utility)3842     private static void verifyModuleVersion(VersionNormalizeUtil versionNormalizeUtil, Utility utility)
3843             throws BundleException {
3844         if (versionNormalizeUtil.getOriginVersionCode() > utility.getVersionCode()) {
3845             String errorMsg = "Input version code less than module " + versionNormalizeUtil.getModuleName()
3846                     + " version code.";
3847             LOG.error(PackingToolErrMsg.VERIFY_MODULE_VERSION_FAILED.toString(errorMsg));
3848             throw new BundleException(errorMsg);
3849         } else if (versionNormalizeUtil.getOriginVersionCode() == utility.getVersionCode()) {
3850             LOG.warning("versionNormalize warning: module " +
3851                     versionNormalizeUtil.getModuleName() + " version code not changed");
3852         }
3853         if (versionNormalizeUtil.getOriginVersionName().equals(utility.getVersionName())) {
3854             LOG.warning("versionNormalize warning: module " +
3855                     versionNormalizeUtil.getModuleName() + " version name not changed");
3856         }
3857     }
3858 
unpackHap(String srcPath, String outPath)3859     private static void unpackHap(String srcPath, String outPath) throws BundleException {
3860         try (FileInputStream fis = new FileInputStream(srcPath);
3861              ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(fis))) {
3862             File destDir = new File(outPath);
3863             if (!destDir.exists()) {
3864                 destDir.mkdirs();
3865             }
3866 
3867             ZipEntry entry;
3868             while ((entry = zipInputStream.getNextEntry()) != null) {
3869                 String entryName = entry.getName();
3870                 File entryFile = new File(outPath, entryName);
3871 
3872                 if (entry.isDirectory()) {
3873                     entryFile.mkdirs();
3874                     zipInputStream.closeEntry();
3875                     continue;
3876                 }
3877                 File parent = entryFile.getParentFile();
3878                 if (!parent.exists()) {
3879                     parent.mkdirs();
3880                 }
3881 
3882                 writeToFile(zipInputStream, entryFile);
3883 
3884                 zipInputStream.closeEntry();
3885             }
3886         } catch (IOException e) {
3887             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Unpack hap exist IOException: " + e.getMessage()));
3888             throw new BundleException("unpack hap failed IOException " + e.getMessage());
3889         }
3890     }
3891 
writeToFile(ZipInputStream zipInputStream, File entryFile)3892     private static void writeToFile(ZipInputStream zipInputStream, File entryFile) throws IOException {
3893         try (FileOutputStream fos = new FileOutputStream(entryFile)) {
3894             byte[] buffer = new byte[BUFFER_SIZE];
3895             int bytesRead;
3896             while ((bytesRead = zipInputStream.read(buffer)) != -1) {
3897                 fos.write(buffer, 0, bytesRead);
3898             }
3899         }
3900     }
3901 
getFileNameByPath(String path)3902     private static String getFileNameByPath(String path) {
3903         Path filePath = Paths.get(path);
3904         return filePath.getFileName().toString();
3905     }
3906 
packEncryptJsonFile(Utility utility)3907     private void packEncryptJsonFile(Utility utility) throws BundleException {
3908         if (!utility.getEncryptPath().isEmpty()) {
3909             pathToFile(utility, utility.getEncryptPath(), NULL_DIR_NAME, false);
3910         } else {
3911             LOG.info("Compressor::packEncryptJsonFile has no encrypt.json");
3912         }
3913     }
3914 
packPacJsonFile(Utility utility)3915     private void packPacJsonFile(Utility utility) throws BundleException {
3916         if (!utility.getPacJsonPath().isEmpty()) {
3917             pathToFile(utility, utility.getPacJsonPath(), NULL_DIR_NAME, false);
3918         } else {
3919             LOG.info("Compressor::packPacJsonFile has no pac.json");
3920         }
3921     }
3922 
generalNormalize(Utility utility)3923     private void generalNormalize(Utility utility) {
3924         List<GeneralNormalizeUtil> utils = new ArrayList<>();
3925         Path tempDir = null;
3926         boolean isSuccess = true;
3927         String[] name = new String[2];
3928         for (String hapPath : utility.getFormattedHapList()) {
3929             try {
3930                 GeneralNormalizeUtil util = new GeneralNormalizeUtil();
3931                 tempDir = Files.createTempDirectory(Paths.get(utility.getOutPath()), "temp");
3932                 unpackHap(hapPath, tempDir.toAbsolutePath().toString());
3933                 File moduleFile = new File(
3934                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + MODULE_JSON);
3935                 File configFile = new File(
3936                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + CONFIG_JSON);
3937                 File packInfoFile = new File(
3938                         tempDir.toAbsolutePath() + LINUX_FILE_SEPARATOR + PACKINFO_NAME);
3939 
3940                 if (moduleFile.exists() && configFile.exists()) {
3941                     LOG.error(PackingToolErrMsg.GENERAL_NORMALIZE_MODE_ARGS_INVALID .toString("Invalid hap structure"));
3942                     throw new BundleException("generalNormalize failed, invalid hap structure.");
3943                 }
3944                 if (moduleFile.exists()) {
3945                     String moduleJsonPath = tempDir.resolve(MODULE_JSON).toString();
3946                     util = parseAndModifyGeneralModuleJson(moduleJsonPath, utility, name);
3947                 } else if (configFile.exists()) {
3948                     String configJsonPath = tempDir.resolve(CONFIG_JSON).toString();
3949                     util = parseAndModifyGeneralConfigJson(configJsonPath, utility, name);
3950                 } else {
3951                     LOG.error(PackingToolErrMsg.GENERAL_NORMALIZE_MODE_ARGS_INVALID .toString("Invalid hap structure"));
3952                     throw new BundleException("generalNormalize failed, invalid hap structure.");
3953                 }
3954                 if (packInfoFile.exists()) {
3955                     String packInfoPath = tempDir.resolve(PACKINFO_NAME).toString();
3956                     parseAndModifyGeneralPackInfo(packInfoPath, utility);
3957                 }
3958                 utils.add(util);
3959                 String modifiedHapPath = Paths.get(utility.getOutPath()) +
3960                         LINUX_FILE_SEPARATOR + Paths.get(hapPath).getFileName().toString();
3961                 boolean ret = compressDirToHap(tempDir, modifiedHapPath);
3962                 if (!ret) {
3963                     isSuccess = false;
3964                     String errMsg = "compressDirToHap failed bundleName:" + name[0] + " moduleName:" + name[1];
3965                     LOG.error(PackingToolErrMsg.GENERAL_NORMALIZE_MODE_ARGS_INVALID .toString(errMsg));
3966                     break;
3967                 }
3968             } catch (Exception e) {
3969                 String errMsg = "general normalize exist Exception bundleName:" + name[0] + " moduleName:" + name[1];
3970                 LOG.error(PackingToolErrMsg.GENERAL_NORMALIZE_MODE_ARGS_INVALID .toString(errMsg + e.getMessage()));
3971                 isSuccess = false;
3972                 break;
3973             } finally {
3974                 if (tempDir != null) {
3975                     deleteDirectory(tempDir.toFile());
3976                 }
3977             }
3978         }
3979         if (!isSuccess) {
3980             if (Paths.get(utility.getOutPath()) != null) {
3981                 deleteFile(Paths.get(utility.getOutPath()).toFile());
3982             }
3983             return;
3984         }
3985         writeGeneralRecord(utils, utility.getOutPath());
3986     }
3987 
parseAndModifyGeneralModuleJson(String jsonFilePath, Utility utility, String[] name)3988     private GeneralNormalizeUtil parseAndModifyGeneralModuleJson(String jsonFilePath, Utility utility, String[] name)
3989             throws BundleException {
3990         GeneralNormalizeUtil util = new GeneralNormalizeUtil();
3991         try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) {
3992             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
3993             if (!jsonObject.containsKey(APP)) {
3994                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The module.json file " +
3995                         "does not contain 'app'."));
3996                 throw new BundleException("The module.json file does not contain 'app'. ");
3997             }
3998             JSONObject appObject = jsonObject.getJSONObject(APP);
3999             JSONObject moduleObject = jsonObject.getJSONObject(MODULE);
4000             if (!moduleObject.containsKey(NAME)) {
4001                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The module object of " +
4002                         "module.json file does not contain 'name'."));
4003                 throw new BundleException("The module object of module.json file does not contain 'name'. ");
4004             }
4005             if (!appObject.containsKey(BUNDLE_NAME)) {
4006                 LOG.error(PackingToolErrMsg.PARSE_AND_MODIFY_MODULEJSON_FAILED.toString("The app object of " +
4007                         "app.json file does not contain 'bundleName'."));
4008                 throw new BundleException("The app object of app.json file does not contain 'bundleName'. ");
4009             }
4010             name[0] = appObject.getString(BUNDLE_NAME);
4011             name[1] = moduleObject.getString(NAME);
4012             util.setModuleName(moduleObject.getString(NAME));
4013 
4014             if (utility.getGeneralNormalizeList().contains(DEVICE_TYPES)) {
4015                 util.setOriginDeviceTypes(getJsonString(moduleObject, DEVICE_TYPES));
4016                 moduleObject.put(DEVICE_TYPES, utility.getDeviceTypes().split(","));
4017             }
4018 
4019             if (utility.getGeneralNormalizeList().contains(VERSION_CODE)) {
4020                 util.setOriginVersionCode(appObject.getIntValue(VERSION_CODE));
4021                 appObject.put(VERSION_CODE, utility.getVersionCode());
4022             }
4023 
4024             if (utility.getGeneralNormalizeList().contains(VERSION_NAME)) {
4025                 util.setOriginVersionName(appObject.getString(VERSION_NAME));
4026                 appObject.put(VERSION_NAME, utility.getVersionName());
4027             }
4028 
4029             if (utility.getGeneralNormalizeList().contains(BUNDLE_NAME)) {
4030                 util.setOriginBundleName(appObject.getString(BUNDLE_NAME));
4031                 appObject.put(BUNDLE_NAME, utility.getBundleName());
4032             }
4033 
4034             if (utility.getGeneralNormalizeList().contains(MIN_COMPATIBLE_VERSION_CODE)) {
4035                 if (appObject.containsKey(MIN_COMPATIBLE_VERSION_CODE)) {
4036                     util.setOriginMinCompatibleVersionCode(appObject.getIntValue(MIN_COMPATIBLE_VERSION_CODE));
4037                 } else {
4038                     util.setOriginMinCompatibleVersionCode(appObject.getIntValue(VERSION_CODE));
4039                 }
4040                 appObject.put(MIN_COMPATIBLE_VERSION_CODE, utility.getMinCompatibleVersionCode());
4041             }
4042 
4043             if (utility.getGeneralNormalizeList().contains(MIN_API_VERSION)) {
4044                 util.setOriginMinAPIVersion(appObject.getIntValue(MIN_API_VERSION));
4045                 appObject.put(MIN_API_VERSION, utility.getMinAPIVersion());
4046             }
4047 
4048             if (utility.getGeneralNormalizeList().contains(TARGET_API_VERSION)) {
4049                 util.setOriginTargetAPIVersion(appObject.getIntValue(TARGET_API_VERSION));
4050                 appObject.put(TARGET_API_VERSION, utility.getTargetAPIVersion());
4051             }
4052 
4053             if (utility.getGeneralNormalizeList().contains(API_RELEASE_TYPE)) {
4054                 util.setOriginApiReleaseType(appObject.getString(API_RELEASE_TYPE));
4055                 appObject.put(API_RELEASE_TYPE, utility.getApiReleaseType());
4056             }
4057 
4058             if (utility.getGeneralNormalizeList().contains(BUNDLE_TYPE)) {
4059                 if (!appObject.containsKey(BUNDLE_TYPE)) {
4060                     if (moduleObject.getBoolean(INSTALLATION_FREE)) {
4061                         String errMsg =
4062                                 "app.json5 file configuration does not match the 'installationFree' setting of true.";
4063                         String solution = "Add the bundleType field to the app.json5 file or set it atomicService.";
4064                         LOG.error(PackingToolErrMsg.PARSE_STAGE_BUNDLE_TYPE_FAILED.toString(errMsg, solution));
4065                         throw new BundleException(errMsg);
4066                     }
4067                     util.setOriginBundleType(APP);
4068                 } else {
4069                     util.setOriginBundleType(appObject.getString(BUNDLE_TYPE));
4070                 }
4071                 appObject.put(BUNDLE_TYPE, utility.getBundleType());
4072             }
4073 
4074             if (utility.getGeneralNormalizeList().contains(INSTALLATION_FREE)) {
4075                 util.setOriginInstallationFree(moduleObject.getBoolean(INSTALLATION_FREE));
4076                 util.setIsInstallationFree(true);
4077                 moduleObject.put(INSTALLATION_FREE, utility.getDeliveryWithInstall());
4078             }
4079 
4080             if (utility.getGeneralNormalizeList().contains(DELIVERY_WITH_INSTALL)) {
4081                 util.setOriginDeliveryWithInstall(moduleObject.getBoolean(DELIVERY_WITH_INSTALL));
4082                 util.setIsDeliveryWithInstall(true);
4083                 moduleObject.put(DELIVERY_WITH_INSTALL, utility.getInstallationFree());
4084             }
4085             writeJson(jsonFilePath, jsonObject);
4086         } catch (IOException e) {
4087             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Parse and modify module.json exist IOException: " +
4088                     e.getMessage()));
4089             throw new BundleException("Parse and modify module.json exist IOException: " + e.getMessage());
4090         }
4091         return util;
4092     }
4093 
parseAndModifyGeneralConfigJson(String jsonFilePath, Utility utility, String[] name)4094     private GeneralNormalizeUtil parseAndModifyGeneralConfigJson(String jsonFilePath, Utility utility, String[] name)
4095             throws BundleException {
4096         GeneralNormalizeUtil util = new GeneralNormalizeUtil();
4097         try (FileInputStream jsonStream = new FileInputStream(jsonFilePath)) {
4098             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
4099             if (!jsonObject.containsKey(APP)) {
4100                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The config.json file " +
4101                         "does not contain 'app'."));
4102                 throw new BundleException("The config.json file does not contain 'app'. ");
4103             }
4104             JSONObject appObject = jsonObject.getJSONObject(APP);
4105 
4106             if (!appObject.containsKey(VERSION)) {
4107                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The app object of config.json " +
4108                         "file does not contain 'version'."));
4109                 throw new BundleException("The app object of config.json file does not contain 'version'. ");
4110             }
4111             JSONObject versionObj = appObject.getJSONObject(VERSION);
4112 
4113             if (!appObject.containsKey(API_VERSION)) {
4114                 JSONObject apiVersion = new JSONObject();
4115                 appObject.put(API_VERSION, apiVersion);
4116             }
4117             JSONObject apiVersionObj = appObject.getJSONObject(API_VERSION);
4118 
4119             if (!jsonObject.containsKey(MODULE)) {
4120                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The app object of config.json " +
4121                         "file does not contain 'module'."));
4122                 throw new BundleException("The app object of config.json file does not contain 'module'. ");
4123             }
4124             JSONObject moduleObject = jsonObject.getJSONObject(MODULE);
4125 
4126             if (!moduleObject.containsKey(DISTRO)) {
4127                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The app object of config.json " +
4128                         "file does not contain 'distro'."));
4129                 throw new BundleException("The app object of config.json file does not contain 'distro'. ");
4130             }
4131             JSONObject distroObj = moduleObject.getJSONObject(DISTRO);
4132 
4133             if (!distroObj.containsKey(MODULE_NAME_NEW)) {
4134                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The module object of " +
4135                         "config.json file does not contain 'moduleName'."));
4136                 throw new BundleException("The module object of module.json file does not contain 'moduleName'. ");
4137             }
4138             if (!appObject.containsKey(BUNDLE_NAME)) {
4139                 LOG.error(PackingToolErrMsg.PARSE_MODIFY_CONFIG_JSON_FAILED.toString("The app object of " +
4140                         "config.json file does not contain 'bundleName'."));
4141                 throw new BundleException("The app object of config.json file does not contain 'bundleName'. ");
4142             }
4143             name[0] = appObject.getString(BUNDLE_NAME);
4144             name[1] = distroObj.getString(MODULE_NAME_NEW);
4145             util.setModuleName(distroObj.getString(MODULE_NAME_NEW));
4146 
4147             if (utility.getGeneralNormalizeList().contains(DEVICE_TYPES)) {
4148                 util.setOriginDeviceTypes(getJsonString(moduleObject, DEVICE_TYPES));
4149                 moduleObject.put(DEVICE_TYPE, utility.getDeviceTypes().split(","));
4150             }
4151             if (utility.getGeneralNormalizeList().contains(VERSION_CODE)) {
4152                 util.setOriginVersionCode(versionObj.getIntValue(CODE));
4153                 versionObj.put(CODE, utility.getVersionCode());
4154             }
4155 
4156             if (utility.getGeneralNormalizeList().contains(VERSION_NAME)) {
4157                 util.setOriginVersionName(versionObj.getString(NAME));
4158                 versionObj.put(NAME, utility.getVersionName());
4159             }
4160 
4161             if (utility.getGeneralNormalizeList().contains(MIN_COMPATIBLE_VERSION_CODE)) {
4162                 if (versionObj.containsKey(MIN_COMPATIBLE_VERSION_CODE)) {
4163                     util.setOriginMinCompatibleVersionCode(versionObj.getIntValue(MIN_COMPATIBLE_VERSION_CODE));
4164                 } else {
4165                     util.setOriginMinCompatibleVersionCode(versionObj.getIntValue(CODE));
4166                 }
4167                 versionObj.put(MIN_COMPATIBLE_VERSION_CODE, utility.getMinCompatibleVersionCode());
4168             }
4169 
4170             if (utility.getGeneralNormalizeList().contains(BUNDLE_NAME)) {
4171                 util.setOriginBundleName(appObject.getString(BUNDLE_NAME));
4172                 appObject.put(BUNDLE_NAME, utility.getBundleName());
4173             }
4174 
4175             if (utility.getGeneralNormalizeList().contains(MIN_API_VERSION)) {
4176                 util.setOriginMinAPIVersion(apiVersionObj.getIntValue(COMPATIBLE));
4177                 apiVersionObj.put(COMPATIBLE, utility.getMinAPIVersion());
4178             }
4179 
4180             if (utility.getGeneralNormalizeList().contains(TARGET_API_VERSION)) {
4181                 util.setOriginTargetAPIVersion(apiVersionObj.getIntValue(TARGET));
4182                 apiVersionObj.put(TARGET, utility.getTargetAPIVersion());
4183             }
4184 
4185             if (utility.getGeneralNormalizeList().contains(API_RELEASE_TYPE)) {
4186                 util.setOriginApiReleaseType(apiVersionObj.getString(RELEASE_TYPE));
4187                 apiVersionObj.put(RELEASE_TYPE, utility.getApiReleaseType());
4188             }
4189 
4190             if (utility.getGeneralNormalizeList().contains(BUNDLE_TYPE)) {
4191                 if (!appObject.containsKey(BUNDLE_TYPE)) {
4192                     if (distroObj.getBoolean(INSTALLATION_FREE)) {
4193                         util.setOriginBundleType(ATOMIC_SERVICE);
4194                     } else {
4195                         util.setOriginBundleType(APP);
4196                     }
4197                 } else {
4198                     util.setOriginBundleType(appObject.getString(BUNDLE_TYPE));
4199                 }
4200                 appObject.put(BUNDLE_TYPE, utility.getBundleType());
4201             }
4202 
4203             if (utility.getGeneralNormalizeList().contains(INSTALLATION_FREE)) {
4204                 util.setOriginInstallationFree(distroObj.getBoolean(INSTALLATION_FREE));
4205                 util.setIsInstallationFree(true);
4206                 distroObj.put(INSTALLATION_FREE, utility.getDeliveryWithInstall());
4207             }
4208 
4209             if (utility.getGeneralNormalizeList().contains(DELIVERY_WITH_INSTALL)) {
4210                 util.setOriginDeliveryWithInstall(distroObj.getBoolean(DELIVERY_WITH_INSTALL));
4211                 util.setIsDeliveryWithInstall(true);
4212                 distroObj.put(DELIVERY_WITH_INSTALL, utility.getInstallationFree());
4213             }
4214             writeJson(jsonFilePath, jsonObject);
4215         } catch (IOException e) {
4216             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Parse and modify module.json exist IOException: " +
4217                     e.getMessage()));
4218             throw new BundleException("Parse and modify config.json exist IOException: " + e.getMessage());
4219         }
4220         return util;
4221     }
4222 
parseAndModifyGeneralPackInfo(String packInfoPath, Utility utility)4223     private void parseAndModifyGeneralPackInfo(String packInfoPath, Utility utility)
4224             throws BundleException {
4225         try (FileInputStream jsonStream = new FileInputStream(packInfoPath)) {
4226             JSONObject jsonObject = JSON.parseObject(jsonStream, JSONObject.class);
4227             if (jsonObject == null) {
4228                 LOG.warning("parseAndModifyGeneralPackInfo failed, json format invalid.");
4229                 return;
4230             }
4231             JSONObject summaryObject = jsonObject.getJSONObject(SUMMARY);
4232             if (summaryObject == null) {
4233                 LOG.warning("parseAndModifyGeneralPackInfo failed, summary invalid.");
4234                 return;
4235             }
4236             JSONObject appObject = summaryObject.getJSONObject(APP);
4237             if (appObject == null) {
4238                 LOG.warning("parseAndModifyGeneralPackInfo failed, app invalid.");
4239                 return;
4240             }
4241             JSONArray moduleJsonList = summaryObject.getJSONArray(MODULES);
4242             if (moduleJsonList.isEmpty()) {
4243                 LOG.warning("parseAndModifyGeneralPackInfo failed, modules invalid.");
4244                 return;
4245             }
4246 
4247             if (utility.getGeneralNormalizeList().contains(DEVICE_TYPES)) {
4248                 for (int i = 0; i < moduleJsonList.size(); i++) {
4249                     JSONObject moduleJson = moduleJsonList.getJSONObject(i);
4250                     if (moduleJson == null) {
4251                         LOG.warning("parseAndModifyGeneralPackInfo failed, moduleJson invalid.");
4252                         continue;
4253                     }
4254                     moduleJson.put(DEVICE_TYPE, utility.getDeviceTypes().split(","));
4255                 }
4256             }
4257 
4258             JSONObject versionObject = appObject.getJSONObject(VERSION);
4259             if (versionObject == null) {
4260                 LOG.warning("parseAndModifyGeneralPackInfo failed, version invalid.");
4261                 return;
4262             }
4263 
4264             if (utility.getGeneralNormalizeList().contains(VERSION_CODE)) {
4265                 versionObject.put(CODE, utility.getVersionCode());
4266             }
4267 
4268             if (utility.getGeneralNormalizeList().contains(VERSION_NAME)) {
4269                 versionObject.put(NAME, utility.getVersionName());
4270             }
4271 
4272             if (utility.getGeneralNormalizeList().contains(BUNDLE_NAME)) {
4273                 appObject.put(BUNDLE_NAME, utility.getBundleName());
4274             }
4275 
4276             if (utility.getGeneralNormalizeList().contains(MIN_COMPATIBLE_VERSION_CODE)) {
4277                 versionObject.put(MIN_COMPATIBLE_VERSION_CODE, utility.getMinCompatibleVersionCode());
4278             }
4279 
4280             if (utility.getGeneralNormalizeList().contains(MIN_API_VERSION)) {
4281                 setApiVersion(moduleJsonList, MIN_API_VERSION, utility);
4282             }
4283 
4284             if (utility.getGeneralNormalizeList().contains(TARGET_API_VERSION)) {
4285                 setApiVersion(moduleJsonList, TARGET_API_VERSION, utility);
4286             }
4287 
4288             if (utility.getGeneralNormalizeList().contains(API_RELEASE_TYPE)) {
4289                 setApiVersion(moduleJsonList, API_RELEASE_TYPE, utility);
4290             }
4291 
4292             if (utility.getGeneralNormalizeList().contains(BUNDLE_TYPE)) {
4293                 appObject.put(BUNDLE_TYPE, utility.getBundleType());
4294             }
4295 
4296             if (utility.getGeneralNormalizeList().contains(INSTALLATION_FREE)) {
4297                 setDistroObj(moduleJsonList, INSTALLATION_FREE, utility);
4298             }
4299 
4300             if (utility.getGeneralNormalizeList().contains(DELIVERY_WITH_INSTALL)) {
4301                 setDistroObj(moduleJsonList, DELIVERY_WITH_INSTALL, utility);
4302             }
4303 
4304             JSONArray jsonArray = jsonObject.getJSONArray(PACKAGES);
4305             if (jsonArray != null) {
4306                 for (int i = 0; i < jsonArray.size(); i++) {
4307                     JSONObject object = jsonArray.getJSONObject(i);
4308                     if (utility.getGeneralNormalizeList().contains(DEVICE_TYPES)) {
4309                         object.put(DEVICE_TYPE, utility.getDeviceTypes().split(","));
4310                     }
4311                     if (utility.getGeneralNormalizeList().contains(DELIVERY_WITH_INSTALL)) {
4312                         object.put(DELIVERY_WITH_INSTALL, Boolean.parseBoolean(utility.getDeliveryWithInstall()));
4313                     }
4314                 }
4315             }
4316 
4317             writeJson(packInfoPath, jsonObject);
4318         } catch (IOException e) {
4319             LOG.warning("parseAndModifyGeneralPackInfo failed, IOException." + e.getMessage());
4320         }
4321     }
4322 
setApiVersion(JSONArray moduleJsonList, String key, Utility utility)4323     private static void setApiVersion(JSONArray moduleJsonList, String key, Utility utility) {
4324         for (int i = 0; i < moduleJsonList.size(); i++) {
4325             JSONObject moduleJson = moduleJsonList.getJSONObject(i);
4326             if (moduleJson == null) {
4327                 LOG.warning("setApiVersion failed, moduleJson invalid.");
4328                 break;
4329             }
4330             JSONObject apiVersionObj = moduleJson.getJSONObject(API_VERSION);
4331             if (apiVersionObj == null) {
4332                 JSONObject apiVersion = new JSONObject();
4333                 moduleJson.put(API_VERSION, apiVersion);
4334                 apiVersionObj = moduleJson.getJSONObject(API_VERSION);
4335             }
4336             if(key == MIN_API_VERSION) {
4337                 apiVersionObj.put(COMPATIBLE, utility.getMinAPIVersion());
4338             } else if (key == TARGET_API_VERSION) {
4339                 apiVersionObj.put(TARGET, utility.getTargetAPIVersion());
4340             } else if (key == API_RELEASE_TYPE) {
4341                 apiVersionObj.put(RELEASE_TYPE, utility.getApiReleaseType());
4342             }
4343         }
4344     }
4345 
setDistroObj(JSONArray moduleJsonList, String key, Utility utility)4346     private static void setDistroObj(JSONArray moduleJsonList, String key, Utility utility) {
4347         for (int i = 0; i < moduleJsonList.size(); i++) {
4348             JSONObject moduleJson = moduleJsonList.getJSONObject(i);
4349             if (moduleJson == null) {
4350                 LOG.warning("setDistroObj failed, moduleJson invalid.");
4351                 break;
4352             }
4353             JSONObject distroObj = moduleJson.getJSONObject(DISTRO);
4354             if (distroObj == null) {
4355                 LOG.warning("setDistroObj failed, distro invalid.");
4356                 break;
4357             }
4358             if(key == INSTALLATION_FREE) {
4359                 distroObj.put(INSTALLATION_FREE, Boolean.parseBoolean(utility.getInstallationFree()));
4360             } else if (key == DELIVERY_WITH_INSTALL) {
4361                 distroObj.put(DELIVERY_WITH_INSTALL, Boolean.parseBoolean(utility.getDeliveryWithInstall()));
4362             }
4363         }
4364     }
4365 
writeGeneralRecord(List<GeneralNormalizeUtil> utils, String outPath)4366     private static void writeGeneralRecord(List<GeneralNormalizeUtil> utils, String outPath) {
4367         JSONArray jsonArray = new JSONArray();
4368         try (FileWriter fileWriter = new FileWriter(outPath + LINUX_FILE_SEPARATOR + GENERAL_RECORD)) {
4369             for (GeneralNormalizeUtil util : utils) {
4370                 JSONObject jsonObject = new JSONObject();
4371                 if (util.originDeviceTypes != null && !util.originDeviceTypes.isEmpty()) {
4372                     jsonObject.put("deviceTypes", util.originDeviceTypes);
4373                     if (util.originDeviceTypes instanceof String) {
4374                         String arrayStr = (String) util.originDeviceTypes;
4375                         JSONArray deviceTypes = JSON.parseArray(arrayStr);
4376                         jsonObject.put("deviceTypes", deviceTypes);
4377                     }
4378                 }
4379                 if (util.originVersionCode != INVALID_VERSION) {
4380                     jsonObject.put("versionCode", util.originVersionCode);
4381                 }
4382                 if (util.moduleName != null && !util.moduleName.isEmpty()) {
4383                     jsonObject.put("moduleName", util.moduleName);
4384                 }
4385                 if (util.originVersionName != null && !util.originVersionName.isEmpty()) {
4386                     jsonObject.put("versionName", util.originVersionName);
4387                 }
4388                 if (util.originMinCompatibleVersionCode != INVALID_VERSION) {
4389                     jsonObject.put("minCompatibleVersionCode", util.originMinCompatibleVersionCode);
4390                 }
4391                 if (util.originMinAPIVersion != INVALID_VERSION) {
4392                     jsonObject.put("minAPIVersion", util.originMinAPIVersion);
4393                 }
4394                 if (util.originTargetAPIVersion != INVALID_VERSION) {
4395                     jsonObject.put("targetAPIVersion", util.originTargetAPIVersion);
4396                 }
4397                 if (util.originApiReleaseType!= null && !util.originApiReleaseType.isEmpty()) {
4398                     jsonObject.put("apiReleaseType", util.originApiReleaseType);
4399                 }
4400                 if (util.originBundleType != null && !util.originBundleType.isEmpty()) {
4401                     jsonObject.put("bundleType", util.originBundleType);
4402                 }
4403                 if (util.originBundleName != null && !util.originBundleName.isEmpty()) {
4404                     jsonObject.put("bundleName", util.originBundleName);
4405                 }
4406                 if (util.isInstallationFree == true) {
4407                     jsonObject.put("installationFree", util.originInstallationFree);
4408                 }
4409                 if (util.isDeliveryWithInstall == true) {
4410                     jsonObject.put("deliveryWithInstall", util.originDeliveryWithInstall);
4411                 }
4412                 jsonArray.add(jsonObject);
4413             }
4414             fileWriter.write(jsonArray.toString());
4415         } catch (IOException e) {
4416             LOG.error(PackingToolErrMsg.IO_EXCEPTION.toString("Write general record exist IOException: "
4417                     + e.getMessage()));
4418         }
4419     }
4420 
deleteFile(File dir)4421     private static void deleteFile(File dir) {
4422         File[] children = dir.listFiles();
4423         if (children != null) {
4424             for (File child : children) {
4425                 child.delete();
4426             }
4427         }
4428     }
4429 }
4430