/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ohos; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import org.apache.commons.compress.archivers.zip.DefaultBackingStoreSupplier; import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.compress.parallel.InputStreamSupplier; import org.apache.commons.compress.utils.IOUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import java.util.zip.CRC32; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * PackageUtil * * @since 2024-06-18 */ public class PackageUtil { private static final Log LOG = new Log(""); /** * get the package name list from pack.info * * @param path the path dir or hsp contains pack.info, or the path of pack.info * @return the list of package name */ public static List getPackageNameFromPath(Path path) { List list = new ArrayList<>(); if (!Files.exists(path)) { LOG.warning("getPackageNameFromPath path not exists: " + path); return list; } if (Files.isRegularFile(path)) { String filename = path.getFileName().toString(); if (filename.endsWith(Constants.HSP_SUFFIX)) { // .hsp: return filename list.add(filename.substring(0, filename.lastIndexOf(Constants.HSP_SUFFIX))); return list; } } String content = getPackInfoContentFromPath(path); if (content == null) { return list; } return getPackageNameFromPackInfo(content); } /** * get the package name list from module.json * * @param path the path dir or hsp contains module.json, or the path of module.json * @return the bundleType */ public static String getBundleTypeFromPath(Path path) { if (!Files.exists(path)) { LOG.warning("getBundleTypeFromPath path not exists: " + path); return ""; } String content = getModuleJsonContentFromPath(path); if (content == null) { return ""; } return getBundleTypeFromModuleJson(content); } /** * get the package name list from pack.info * * @param packInfoContent the content of pack.info * @return the list of package name */ public static List getPackageNameFromPackInfo(String packInfoContent) { List packages = new ArrayList<>(); try { JSONObject jsonObject = JSON.parseObject(packInfoContent, JSONObject.class); if (jsonObject == null) { LOG.warning("getPackagesFromPackInfo failed, json format invalid."); return packages; } JSONArray jsonArray = jsonObject.getJSONArray(Constants.PACKAGES); if (jsonArray == null) { LOG.warning("getPackagesFromPackInfo failed, json format invalid."); return packages; } for (int i = 0; i < jsonArray.size(); i++) { JSONObject object = jsonArray.getJSONObject(i); String packageName = object.getString(Constants.MODULE_NAME); if (packageName != null) { packages.add(packageName); } } return packages; } catch (JSONException ex) { LOG.warning("getPackagesFromPackInfo err: " + ex.getMessage()); return new ArrayList<>(); } } private static String getBundleTypeFromModuleJson(String moduleJsonContent) { try { JSONObject jsonObject = JSON.parseObject(moduleJsonContent, JSONObject.class); if (jsonObject == null) { LOG.warning("getBundleTypeFromModuleJson failed, parse json is null."); return ""; } JSONObject appObject = jsonObject.getJSONObject(Constants.APP); if (appObject == null) { LOG.warning("getBundleTypeFromModuleJson failed, [app] is null."); return ""; } String bundleType = appObject.getString(Constants.BUNDLE_TYPE); return bundleType != null ? bundleType : Constants.APP; } catch (JSONException ex) { LOG.warning("getBundleTypeFromModuleJson failed: " + ex.getMessage()); } return ""; } private static ModuleJsonInfo parseModuleJsonInfo(Path moduleJson) { ModuleJsonInfo moduleJsonInfo = new ModuleJsonInfo(); try (FileInputStream input = new FileInputStream(moduleJson.toFile())) { JSONObject jsonObject = JSON.parseObject(input, JSONObject.class); if (jsonObject == null) { LOG.warning("parseModuleJsonInfo failed, json format invalid."); return moduleJsonInfo; } JSONObject appObject = jsonObject.getJSONObject(Constants.APP); if (appObject == null) { LOG.warning("parseModuleJsonInfo failed, not found [app]"); return moduleJsonInfo; } JSONObject moduleObject = jsonObject.getJSONObject(Constants.MODULE); if (moduleObject == null) { LOG.warning("parseModuleJsonInfo failed, not found [module]"); return moduleJsonInfo; } moduleJsonInfo.setGenerateBuildHash( appObject.getBooleanValue(Constants.GENERATE_BUILD_HASH) || moduleObject.getBooleanValue(Constants.GENERATE_BUILD_HASH)); moduleJsonInfo.setCompressNativeLibs( moduleObject.getBooleanValue(Constants.COMPRESS_NATIVE_LIBS)); String moduleType = moduleObject.getString(Constants.MODULE_TYPE); moduleJsonInfo.setModuleType(moduleType != null ? moduleType : ""); String moduleName = moduleObject.getString(Constants.MODULE_NAME); moduleJsonInfo.setModuleName(moduleName != null ? moduleName : ""); return moduleJsonInfo; } catch (IOException ex) { LOG.warning("parseModuleJsonInfo err: " + ex.getMessage()); } return moduleJsonInfo; } private static String getPackInfoContentFromPath(Path path) { try { if (Files.isRegularFile(path)) { String filename = path.getFileName().toString(); if (filename.equals(Constants.FILE_PACK_INFO)) { return new String(Files.readAllBytes(path)); } else if (filename.endsWith(Constants.HSP_SUFFIX)) { return getZipEntryContent(path, Constants.FILE_PACK_INFO); } } else { return new String(Files.readAllBytes(path.resolve(Constants.FILE_PACK_INFO))); } } catch (IOException ex) { LOG.warning("getPackInfoContentFromPath err: " + ex.getMessage()); } return null; } private static String getModuleJsonContentFromPath(Path path) { try { if (Files.isRegularFile(path)) { String filename = path.getFileName().toString(); if (filename.equals(Constants.FILE_MODULE_JSON)) { return new String(Files.readAllBytes(path)); } else if (filename.endsWith(Constants.HSP_SUFFIX)) { return getZipEntryContent(path, Constants.FILE_MODULE_JSON); } } else { return new String(Files.readAllBytes(path.resolve(Constants.FILE_MODULE_JSON))); } } catch (IOException ex) { LOG.warning("getPackInfoContentFromPath err: " + ex.getMessage()); } return null; } private static String getZipEntryContent(Path zipPath, String entryName) { if (!Files.isRegularFile(zipPath)) { return null; } try (ZipFile zipFile = new ZipFile(zipPath.toFile()); ByteArrayOutputStream output = new ByteArrayOutputStream()) { ZipArchiveEntry zipEntry = zipFile.getEntry(entryName); if (zipEntry != null) { IOUtils.copy(zipFile.getInputStream(zipEntry), output); return output.toString(); } } catch (IOException ex) { LOG.warning("getZipEntryContent err: " + ex.getMessage()); } return null; } /** * pack hap or hsp * * @param inputPath input hap/hsp path * @param appPackInfo app scope pack.info * @param outPath output dir * @param compressLevel compress level * @return the hap/hsp path * @throws BundleException bundle exception * @throws IOException IO exception */ public static Path pack(Path inputPath, Path appPackInfo, Path outPath, int compressLevel) throws BundleException, IOException { if (!Files.exists(inputPath)) { throw new BundleException("pack err, input path not exists."); } if (!Files.exists(appPackInfo)) { throw new BundleException("pack err, app pack.info not exists."); } if (Files.isDirectory(inputPath)) { return packDir(inputPath, appPackInfo, outPath, compressLevel); } else if (Files.isRegularFile(inputPath) && inputPath.getFileName().toString().endsWith(Constants.HSP_SUFFIX)) { return repackHsp(inputPath, appPackInfo, outPath, compressLevel); } throw new BundleException("pack err, not support: " + inputPath); } /** * rm dir * * @param dir input path to rm * @return true if rm dir success */ public static boolean rmdir(Path dir) { try { Files.walkFileTree(dir, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException ex) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); return true; } catch (IOException ex) { LOG.warning("rmdir err: " + ex.getMessage()); } return false; } private static String hash(Path path) { try (Stream pathStream = Files.walk(path)) { MessageDigest md = MessageDigest.getInstance(Constants.SHA_256); pathStream.filter(Files::isRegularFile) .sorted() .forEach(file -> { try { byte[] fileBytes = Files.readAllBytes(file); md.update(fileBytes); } catch (IOException e) { LOG.warning("calc hash err: " + e.getMessage()); } }); byte[] hashBytes = md.digest(); StringBuilder sb = new StringBuilder(); for (byte b : hashBytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException | IOException e) { LOG.warning("calc hash err: " + e.getMessage()); } return ""; } private static Path repackHsp(Path inputPath, Path appPackInfo, Path outPath, int compressLevel) throws BundleException, IOException { Path outHsp = Files.createFile(outPath.resolve(inputPath.getFileName())); try (ZipFile hspFile = new ZipFile(inputPath.toFile()); ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream( new CheckedOutputStream(Files.newOutputStream(outHsp), new CRC32()))) { int cores = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor executorService = new ThreadPoolExecutor(cores, cores, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); ParallelScatterZipCreator zipCreator = new ParallelScatterZipCreator( executorService, new DefaultBackingStoreSupplier(null), compressLevel); // pack.info pathToZipEntry(appPackInfo, Constants.NULL_DIR, zipCreator, false); // others Enumeration entries = hspFile.getEntries(); while (entries.hasMoreElements()) { ZipArchiveEntry zipEntry = entries.nextElement(); if (Constants.FILE_PACK_INFO.equals(zipEntry.getName())) { continue; } InputStreamSupplier supplier = () -> { try { return hspFile.getInputStream(zipEntry); } catch (IOException e) { LOG.error("addArchiveEntry err: " + e.getMessage()); return null; } }; zipCreator.addArchiveEntry(zipEntry, supplier); } zipCreator.writeTo(zipOut); } catch (InterruptedException | ExecutionException e) { String errMsg = "repackHsp err: " + e.getMessage(); LOG.error(errMsg); throw new BundleException(errMsg); } return outHsp; } private static Path packDir(Path inputPath, Path appPackInfo, Path outPath, int compressLevel) throws BundleException, IOException { List packageNames = getPackageNameFromPath(inputPath.resolve(Constants.FILE_PACK_INFO)); if (packageNames.size() != 1) { throw new BundleException("pack err, pack.info format err"); } ModuleJsonInfo moduleJsonInfo = parseModuleJsonInfo(inputPath.resolve(Constants.FILE_MODULE_JSON)); String pkgName = packageNames.get(0); String suffix = moduleJsonInfo.isShared() ? Constants.HSP_SUFFIX : Constants.HAP_SUFFIX; Path outHap = Files.createFile(outPath.resolve(pkgName + suffix)); if (moduleJsonInfo.isCompressNativeLibs()) { return packMultiThread(inputPath, appPackInfo, outHap, compressLevel, moduleJsonInfo); } else { return packSingleThread(inputPath, appPackInfo, outHap, moduleJsonInfo); } } private static Path packSingleThread(Path inputPath, Path appPackInfo, Path outHap, ModuleJsonInfo moduleJsonInfo) throws BundleException, IOException { File[] files = inputPath.toFile().listFiles(); if (files == null || files.length == 0) { throw new BundleException("pack err, dir is empty"); } try (ZipOutputStream zipOut = new ZipOutputStream( new CheckedOutputStream(Files.newOutputStream(outHap), new CRC32()))) { // pack.info pathToZipEntry(appPackInfo, Constants.NULL_DIR, zipOut, false); // module.json generateBuildHash if (moduleJsonInfo.isGenerateBuildHash()) { genBuildHash(inputPath, zipOut); } // others filesToZipEntry(files, zipOut, moduleJsonInfo.isGenerateBuildHash(), moduleJsonInfo.isCompressNativeLibs()); } return outHap; } private static Path packMultiThread(Path inputPath, Path appPackInfo, Path outHap, int compressLevel, ModuleJsonInfo moduleJsonInfo)throws BundleException, IOException { File[] files = inputPath.toFile().listFiles(); if (files == null || files.length == 0) { throw new BundleException("pack err, dir is empty"); } try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream( new CheckedOutputStream(Files.newOutputStream(outHap), new CRC32()))) { int cores = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor executorService = new ThreadPoolExecutor(cores, cores, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); ParallelScatterZipCreator zipCreator = new ParallelScatterZipCreator( executorService, new DefaultBackingStoreSupplier(null), compressLevel); // pack.info pathToZipEntry(appPackInfo, Constants.NULL_DIR, zipCreator, false); // module.json generateBuildHash if (moduleJsonInfo.isGenerateBuildHash()) { genBuildHash(inputPath, zipCreator); } // others filesToZipEntry(files, zipCreator, moduleJsonInfo.isGenerateBuildHash(), moduleJsonInfo.isCompressNativeLibs()); // write to zip zipCreator.writeTo(zipOut); } catch (InterruptedException | ExecutionException e) { String errMsg = "packDir err: " + e.getMessage(); LOG.error(errMsg); throw new BundleException(errMsg); } return outHap; } private static void filesToZipEntry(File[] files, ParallelScatterZipCreator zipCreator, boolean genHash, boolean compress) throws BundleException { for (File file : files) { if (file.isFile() && !file.getName().equals(Constants.FILE_PACK_INFO)) { if (genHash && file.getName().equals(Constants.FILE_MODULE_JSON)) { continue; } pathToZipEntry(file.toPath(), Constants.NULL_DIR, zipCreator, false); } else if (file.isDirectory()) { if (file.getName().equals(Constants.LIBS_DIR)) { pathToZipEntry(file.toPath(), Constants.LIBS_DIR + Constants.SLASH, zipCreator, compress); } else { pathToZipEntry(file.toPath(), file.getName() + Constants.SLASH, zipCreator, false); } } } } private static void genBuildHash(Path path, ParallelScatterZipCreator zipCreator) { String hash = hash(path); if (hash.isEmpty()) { return; } Path moduleJson = path.resolve(Constants.FILE_MODULE_JSON); if (!Files.exists(moduleJson)) { LOG.warning("module.json not found: " + path); return; } try (FileInputStream input = new FileInputStream(moduleJson.toFile())) { JSONObject jsonObject = JSON.parseObject(input, JSONObject.class); if (jsonObject == null) { LOG.warning("generateBuildHash: parse json is null."); return; } JSONObject moduleObject = jsonObject.getJSONObject(Constants.MODULE); if (moduleObject == null) { LOG.warning("generateBuildHash: parse json[module] is null."); return; } moduleObject.put(Constants.BUILD_HASH, hash); byte[] data = JSON.toJSONBytes(jsonObject, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField); ZipArchiveEntry zipEntry = new ZipArchiveEntry(Constants.FILE_MODULE_JSON); zipEntry.setMethod(ZipArchiveEntry.STORED); InputStreamSupplier supplier = () -> new ByteArrayInputStream(data); zipCreator.addArchiveEntry(zipEntry, supplier); } catch (IOException ex) { LOG.warning("genBuildHash err: " + ex.getMessage()); } } private static void pathToZipEntry(Path path, String baseDir, ParallelScatterZipCreator zipCreator, boolean compress) throws BundleException { try { File file = path.toFile(); if (file.isDirectory()) { File[] files = file.listFiles(); if (files == null) { return; } for (File f : files) { addArchiveEntry(f, baseDir, zipCreator, compress); } } else { addArchiveEntry(file, baseDir, zipCreator, compress); } } catch (IOException e) { String errMsg = "pathToZip err: " + e.getMessage(); LOG.error(errMsg); throw new BundleException(errMsg); } } private static void addArchiveEntry(File file, String baseDir, ParallelScatterZipCreator zipCreator, boolean compress) throws IOException { if (file.isDirectory()) { File[] files = file.listFiles(); if (files == null) { LOG.error("listFiles null: " + file.getName()); return; } if (files.length == 0) { String entryName = (baseDir + file.getName() + File.separator) .replace(File.separator, Constants.SLASH); ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName); zipEntry.setMethod(ZipArchiveEntry.STORED); zipEntry.setSize(0); zipEntry.setCrc(0); InputStreamSupplier supplier = () -> new ByteArrayInputStream(new byte[0]); zipCreator.addArchiveEntry(zipEntry, supplier); } for (File f : files) { addArchiveEntry(f, baseDir + file.getName() + File.separator, zipCreator, compress); } } else { String entryName = (baseDir + file.getName()).replace(File.separator, Constants.SLASH); ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName); if (compress) { zipEntry.setMethod(ZipArchiveEntry.DEFLATED); } else { zipEntry.setMethod(ZipArchiveEntry.STORED); } InputStreamSupplier supplier = () -> { try { return getInputStream(entryName, file); } catch (IOException e) { LOG.error("addArchiveEntry err: " + e.getMessage()); return null; } }; zipCreator.addArchiveEntry(zipEntry, supplier); } } private static InputStream getInputStream(String entryName, File file) throws IOException { if (!entryName.contains(Constants.RAW_FILE_PATH) && !entryName.contains(Constants.RES_FILE_PATH) && file.getName().toLowerCase(Locale.ENGLISH).endsWith(Constants.JSON_SUFFIX)) { try { Object jsonObject = JSON.parse(Files.readAllBytes(file.toPath())); byte[] data = JSON.toJSONBytes(jsonObject, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat); return new ByteArrayInputStream(data); } catch (JSONException ex) { LOG.warning("json format err: " + file.getName()); } } return Files.newInputStream(file.toPath()); } private static void filesToZipEntry(File[] files, ZipOutputStream zipOut, boolean genHash, boolean compress) throws BundleException { for (File file : files) { if (file.isFile() && !file.getName().equals(Constants.FILE_PACK_INFO)) { if (genHash && file.getName().equals(Constants.FILE_MODULE_JSON)) { continue; } pathToZipEntry(file.toPath(), Constants.NULL_DIR, zipOut, false); } else if (file.isDirectory()) { if (file.getName().equals(Constants.LIBS_DIR)) { pathToZipEntry(file.toPath(), Constants.LIBS_DIR + Constants.SLASH, zipOut, compress); } else { pathToZipEntry(file.toPath(), file.getName() + Constants.SLASH, zipOut, false); } } } } private static void genBuildHash(Path path, ZipOutputStream zipOut) { String hash = hash(path); if (hash.isEmpty()) { return; } Path moduleJson = path.resolve(Constants.FILE_MODULE_JSON); if (!Files.exists(moduleJson)) { LOG.warning("module.json not found: " + path); return; } try (FileInputStream input = new FileInputStream(moduleJson.toFile())) { JSONObject jsonObject = JSON.parseObject(input, JSONObject.class); if (jsonObject == null) { LOG.warning("generateBuildHash: parse json is null."); return; } JSONObject moduleObject = jsonObject.getJSONObject(Constants.MODULE); if (moduleObject == null) { LOG.warning("generateBuildHash: parse json[module] is null."); return; } moduleObject.put(Constants.BUILD_HASH, hash); byte[] data = JSON.toJSONBytes(jsonObject, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField); CRC32 crc32 = new CRC32(); crc32.update(data, 0, data.length); ZipEntry zipEntry = new ZipEntry(Constants.FILE_MODULE_JSON); zipEntry.setMethod(ZipEntry.STORED); zipEntry.setCompressedSize(data.length); zipEntry.setCrc(crc32.getValue()); zipOut.putNextEntry(zipEntry); try (InputStream inputStream = new ByteArrayInputStream(data)) { IOUtils.copy(inputStream, zipOut, Constants.BUFFER_SIZE); } zipOut.closeEntry(); } catch (IOException ex) { LOG.warning("genBuildHash err: " + ex.getMessage()); } } private static void pathToZipEntry(Path path, String baseDir, ZipOutputStream zipOut, boolean compress) throws BundleException { try { File file = path.toFile(); if (file.isDirectory()) { File[] files = file.listFiles(); if (files == null) { return; } for (File f : files) { addArchiveEntry(f, baseDir, zipOut, compress); } } else { addArchiveEntry(file, baseDir, zipOut, compress); } } catch (IOException e) { String errMsg = "pathToZip err: " + e.getMessage(); LOG.error(errMsg); throw new BundleException(errMsg); } } private static void addArchiveEntry(File file, String baseDir, ZipOutputStream zipOut, boolean compress) throws IOException, BundleException { if (file.isDirectory()) { File[] files = file.listFiles(); if (files == null) { LOG.error("listFiles null: " + file.getName()); return; } if (files.length == 0) { String entryName = (baseDir + file.getName() + File.separator) .replace(File.separator, Constants.SLASH); ZipEntry zipEntry = new ZipEntry(entryName); zipEntry.setMethod(ZipEntry.STORED); zipEntry.setSize(0); zipEntry.setCrc(0); zipOut.putNextEntry(zipEntry); zipOut.closeEntry(); } for (File f : files) { addArchiveEntry(f, baseDir + file.getName() + File.separator, zipOut, compress); } } else { String entryName = (baseDir + file.getName()).replace(File.separator, Constants.SLASH); ZipEntry zipEntry = new ZipEntry(entryName); if (compress) { zipEntry.setMethod(ZipEntry.DEFLATED); } else { zipEntry.setMethod(ZipEntry.STORED); CRC32 crc32 = PackageNormalize.getCrcFromFile(file); zipEntry.setCrc(crc32.getValue()); zipEntry.setCompressedSize(file.length()); } zipOut.putNextEntry(zipEntry); try (InputStream input = Files.newInputStream(file.toPath())) { IOUtils.copy(input, zipOut, Constants.BUFFER_SIZE); } zipOut.closeEntry(); } } private static boolean checkBundleTypeConsistency(List hapPathList, List hspPathList, Utility utility) { String bundleType = ""; if (!hapPathList.isEmpty()) { bundleType = getBundleTypeFromPath(Paths.get(hapPathList.get(0))); } else if (!hspPathList.isEmpty()) { bundleType = getBundleTypeFromPath(Paths.get(hspPathList.get(0))); } if (bundleType == null || bundleType.isEmpty()) { return false; } for (String hapPath : hapPathList) { if (!bundleType.equals(getBundleTypeFromPath(Paths.get(hapPath)))) { LOG.error("bundleType is not same"); return false; } } for (String hspPath : hspPathList) { if (!bundleType.equals(getBundleTypeFromPath(Paths.get(hspPath)))) { LOG.error("bundleType is not same"); return false; } } if (bundleType.equals(Constants.BUNDLE_TYPE_SHARED)) { utility.setIsSharedApp(true); } return true; } private static boolean moduleJsonAndPackInfoExists(List hapPathList, List hspPathList) { for (String hapPath : hapPathList) { Path path = Paths.get(hapPath); if (!Files.exists(path.resolve(Constants.FILE_MODULE_JSON))) { LOG.error("not found module.json in path: " + path); return false; } if (!Files.exists(path.resolve(Constants.FILE_PACK_INFO))) { LOG.error("not found pack.info in path: " + path); return false; } } for (String hspPath : hspPathList) { Path path = Paths.get(hspPath); if (Files.isDirectory(path)) { if (!Files.exists(path.resolve(Constants.FILE_MODULE_JSON))) { LOG.error("not found module.json in path: " + path); return false; } if (!Files.exists(path.resolve(Constants.FILE_PACK_INFO))) { LOG.error("not found pack.info in path: " + path); return false; } } } return true; } private static boolean isFileValid(String filePath, String suffix) { Path path = Paths.get(filePath); return Files.isRegularFile(path) && path.getFileName().toString().endsWith(suffix); } private static boolean isDirValid(String filePath) { return Files.isDirectory(Paths.get(filePath)); } /** * verify input param * * @param utility common data * @return true if verify ok */ public static boolean isVerifyValidInFastAppMode(Utility utility) { if (!isVerifyValid(utility)) { return false; } if (!utility.getHapPath().isEmpty() && (!isFormatPathValid(utility.getHapPath(), utility.getFormattedHapPathList()) || !isHapPathValid(utility.getFormattedHapPathList()))) { LOG.error("CompressVerify::isVerifyValidInFastAppMode hap-path is invalid."); return false; } if (!utility.getHspPath().isEmpty() && (!isFormatPathValid(utility.getHspPath(), utility.getFormattedHspPathList()) || !isHspPathValid(utility.getFormattedHspPathList()))) { LOG.error("CompressVerify::isVerifyValidInFastAppMode hsp-path is invalid."); return false; } if (utility.getHapPath().isEmpty() && utility.getHspPath().isEmpty()) { LOG.error("CompressVerify::isVerifyValidInFastAppMode hap-path and hsp-path is empty."); return false; } if (!moduleJsonAndPackInfoExists(utility.getFormattedHapPathList(), utility.getFormattedHspPathList())) { LOG.error("CompressVerify::isVerifyValidInFastAppMode hap-path or hsp-path is invalid."); return false; } if (!checkBundleTypeConsistency( utility.getFormattedHapPathList(), utility.getFormattedHspPathList(), utility)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode bundleType is inconsistent."); return false; } if (!isPackInfoValid(Paths.get(utility.getPackInfoPath()), utility.getFormattedHapPathList(), utility.getFormattedHspPathList())) { LOG.error("CompressVerify::isVerifyValidInFastAppMode pack.info is invalid."); return false; } if (!utility.getEncryptPath().isEmpty() && !isFileValid(utility.getEncryptPath(), Constants.FILE_ENCRYPT_JSON)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode encrypt-path is invalid."); return false; } Path outPath = Paths.get(utility.getOutPath()); if (utility.getForceRewrite().equals(Constants.FALSE) && Files.exists(outPath)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode out file already existed."); return false; } if (!outPath.getFileName().toString().endsWith(Constants.APP_SUFFIX)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode out-path must end with .app."); return false; } return true; } private static boolean isVerifyValid(Utility utility) { if (utility.getPackInfoPath().isEmpty()) { LOG.error("CompressVerify::isArgsValidInAppMode pack-info-path is empty."); return false; } if (!isFileValid(utility.getPackInfoPath(), Constants.FILE_PACK_INFO)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode pack-info-path is invalid."); return false; } if (!utility.getSignaturePath().isEmpty() && !isFileValid(utility.getSignaturePath(), "")) { LOG.error("CompressVerify::isVerifyValidInFastAppMode signature-path is invalid."); return false; } if (!utility.getCertificatePath().isEmpty() && !isFileValid(utility.getCertificatePath(), "")) { LOG.error("CompressVerify::isVerifyValidInFastAppMode certificate-path is invalid."); return false; } if (!utility.getPackResPath().isEmpty() && !isFileValid(utility.getPackResPath(), Constants.FILE_PACK_RES)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode pack-res-path is invalid."); return false; } if (!utility.getEntryCardPath().isEmpty() && !CompressVerify.compatibleProcess(utility, utility.getEntryCardPath(), utility.getformattedEntryCardPathList(), Constants.PNG_SUFFIX)) { LOG.error("CompressVerify::isVerifyValidInFastAppMode entrycard-path is invalid."); return false; } if (utility.getOutPath().isEmpty()) { LOG.error("CompressVerify::isVerifyValidInFastAppMode out-path is empty."); return false; } return true; } private static boolean isHapPathValid(List formatPathList) { for (String path : formatPathList) { if (!isDirValid(path)) { return false; } } return true; } private static boolean isHspPathValid(List formatPathList) { for (String path : formatPathList) { if (!isDirValid(path) && !isFileValid(path, Constants.HSP_SUFFIX)) { return false; } } return true; } private static boolean isFormatPathValid(String inputPath, List formatPathList) { Set formatPathSet = new HashSet<>(); for (String path : inputPath.split(Constants.COMMA)) { try { Path realpath = Paths.get(path).toRealPath(); if (Files.exists(realpath)) { formatPathSet.add(realpath.toString()); } else { LOG.error("PackageUtil::formatPath not exists: " + realpath); return false; } } catch (IOException ex) { LOG.error("PackageUtil::formatPath err: " + ex.getMessage()); return false; } } formatPathList.addAll(formatPathSet); return true; } private static boolean isPackInfoValid(Path packInfo, List hapPathList, List hspPathList) { List allPackages = getPackageNameFromPath(packInfo); Set allPackageSet = new HashSet<>(allPackages); if (allPackages.size() > allPackageSet.size()) { LOG.error("package name is redundant in app pack.info: " + packInfo); return false; } if (allPackages.isEmpty()) { LOG.error("app pack.info format err: " + packInfo); return false; } Set packages = new HashSet<>(); for (String hapPath : hapPathList) { List list = getPackageNameFromPath(Paths.get(hapPath)); if (list.size() != 1) { LOG.error("module pack.info format err: " + hapPath); return false; } String packageName = list.get(0); if (!allPackages.contains(packageName)) { LOG.error("module pack.info name not exist in app pack.info name list: " + hapPath); return false; } if (packages.contains(packageName)) { LOG.error("package name is redundant in " + hapPath); return false; } packages.add(packageName); } for (String hspPath : hspPathList) { List list = getPackageNameFromPath(Paths.get(hspPath)); if (list.size() != 1) { LOG.error("module pack.info format err: " + hspPath); return false; } String packageName = list.get(0); if (!allPackages.contains(packageName)) { LOG.error("module pack.info name not exist in app pack.info name list: " + hspPath); return false; } if (packages.contains(packageName)) { LOG.error("package name is redundant in " + hspPath); return false; } packages.add(packageName); } if (!allPackageSet.equals(packages)) { LOG.error("package name not same between module and app pack.info."); return false; } return true; } }