diff --git a/BUILD.gn b/BUILD.gn index bc813f8f6735baca68d85643745e417177b0efee..1429673d148cdcdf4d668b04c42d716891b09e48 100755 --- a/BUILD.gn +++ b/BUILD.gn @@ -19,6 +19,7 @@ packing_tool("packing_tool") { "//developtools/packing_tool/haptobin.sh", "//developtools/packing_tool/packingTool.sh", "//developtools/packing_tool/unpackingTool.sh", + "//developtools/packing_tool/adapter/ohos", ] outputs = [ "${target_out_dir}/jar/haptobin_tool.jar", diff --git a/adapter/ohos/APPQFResult.java b/adapter/ohos/APPQFResult.java new file mode 100755 index 0000000000000000000000000000000000000000..7abc2d12141fa8a1b1d33f877de73afc87a3891c --- /dev/null +++ b/adapter/ohos/APPQFResult.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 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 java.util.List; +import java.time.format.DateTimeFormatter; + +/** + * parse result of appqf. + */ +public class APPQFResult { + /** + * indicates whether parse is success. + */ + private boolean success = false; + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + private List hqfInfoList = null; + + public List getHqfInfoList() { + return hqfInfoList; + } + + public void setHqfInfoList(List hqfInfoList) { + this.hqfInfoList = hqfInfoList; + } +} diff --git a/adapter/ohos/Compressor.java b/adapter/ohos/Compressor.java index 47117549276bc88d2e54de8482261548500b47b2..faf035234ddf8019ec5c40a741e873723ebc784b 100644 --- a/adapter/ohos/Compressor.java +++ b/adapter/ohos/Compressor.java @@ -2100,7 +2100,7 @@ public class Compressor { * @throws BundleException FileNotFoundException|IOException. */ private boolean checkHQFIsValid(List fileList) throws BundleException { - List hqfVerifyInfos = new ArrayList<>(); + List hqfVerifyInfos = new ArrayList<>(); for (String file : fileList) { hqfVerifyInfos.add(ModuleJsonUtil.parseHQFInfo(file)); } diff --git a/adapter/ohos/FileUtils.java b/adapter/ohos/FileUtils.java index da9a888553dc72cda9abf4a55c52399f9e5e4f5a..fb6d9a394cf14a359f3989e5721d56d206324611 100644 --- a/adapter/ohos/FileUtils.java +++ b/adapter/ohos/FileUtils.java @@ -560,4 +560,35 @@ class FileUtils { res = realStr.substring(left, right); return res; } + + public static void unzipFile(String zipFilePath, String destDirPath) throws IOException { + File destDir = new File(destDirPath); + if (!destDir.exists()) { + destDir.mkdirs(); + } + ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath)); + ZipEntry entry = zipInputStream.getNextEntry(); + while (entry != null) { + String filePath = destDirPath + File.separator + entry.getName(); + if (!entry.isDirectory()) { + extractFile(zipInputStream, filePath); + } else { + File dir = new File(filePath); + dir.mkdirs(); + } + zipInputStream.closeEntry(); + entry = zipInputStream.getNextEntry(); + } + zipInputStream.close(); + } + + private static void extractFile(ZipInputStream zipInputStream, String filePath) throws IOException { + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath)); + byte[] bytes = new byte[BUFFER_SIZE]; + int readLength = 0; + while ((readLength = zipInputStream.read(bytes)) != -1) { + bufferedOutputStream.write(bytes, 0, readLength); + } + bufferedOutputStream.close(); + } } \ No newline at end of file diff --git a/adapter/ohos/HQFVerifyInfo.java b/adapter/ohos/HQFInfo.java similarity index 95% rename from adapter/ohos/HQFVerifyInfo.java rename to adapter/ohos/HQFInfo.java index 08e5d2fa07c845c1ac286beeae26ba9e89fb4b45..3f369cb274cf0e0e22727df4542f802551d95a83 100644 --- a/adapter/ohos/HQFVerifyInfo.java +++ b/adapter/ohos/HQFInfo.java @@ -20,7 +20,7 @@ import java.util.List; /** * collection of HQF verify info. */ -class HQFVerifyInfo { +public class HQFInfo { private String bundleName = ""; private int versionCode = -1; diff --git a/adapter/ohos/HQFVerify.java b/adapter/ohos/HQFVerify.java index 6933ae57562a6d1d79c46cf35515d92e09c86de2..2354c51d6b1dd35a6e63ceba788bc2cc86304b8e 100644 --- a/adapter/ohos/HQFVerify.java +++ b/adapter/ohos/HQFVerify.java @@ -30,7 +30,7 @@ class HQFVerify { * @param hqfVerifyInfos is the collection of hqf infos * @return the result */ - public static boolean checkHQFIsValid(List hqfVerifyInfos) { + public static boolean checkHQFIsValid(List hqfVerifyInfos) { // check app fields if (hqfVerifyInfos.isEmpty()) { LOG.error("Error: input hqf file is empty!"); @@ -53,13 +53,13 @@ class HQFVerify { * @param hqfVerifyInfos is the collection of hqf infos * @return the result */ - private static boolean checkAppFields(List hqfVerifyInfos) { + private static boolean checkAppFields(List hqfVerifyInfos) { String bundleName = hqfVerifyInfos.get(0).getBundleName(); int versionCode = hqfVerifyInfos.get(0).getVersionCode(); String versionName = hqfVerifyInfos.get(0).getVersionName(); int patchVersionCode = hqfVerifyInfos.get(0).getPatchVersionCode(); String patchVersionName = hqfVerifyInfos.get(0).getPatchVersionName(); - for (HQFVerifyInfo hqfVerifyInfo : hqfVerifyInfos) { + for (HQFInfo hqfVerifyInfo : hqfVerifyInfos) { if (bundleName == null || !bundleName.equals(hqfVerifyInfo.getBundleName())) { LOG.error("Error: input hqf file has different bundleName!"); return false; @@ -90,7 +90,7 @@ class HQFVerify { * @param hqfVerifyInfos is the collection of hqf infos * @return the result */ - private static boolean checkModuleIsValid(List hqfVerifyInfos) { + private static boolean checkModuleIsValid(List hqfVerifyInfos) { for (int i = 0; i < hqfVerifyInfos.size(); ++i) { for (int j = i + 1; j < hqfVerifyInfos.size(); ++j) { if (checkModuleIsDuplicated(hqfVerifyInfos.get(i), hqfVerifyInfos.get(j))) { @@ -110,7 +110,7 @@ class HQFVerify { * @param hqfVerifyInfoRight is another HQFVerifyInfo * @return the result */ - private static boolean checkModuleIsDuplicated(HQFVerifyInfo hqfVerifyInfoLeft, HQFVerifyInfo hqfVerifyInfoRight) { + private static boolean checkModuleIsDuplicated(HQFInfo hqfVerifyInfoLeft, HQFInfo hqfVerifyInfoRight) { if (!hqfVerifyInfoLeft.getModuleName().equals(hqfVerifyInfoRight.getModuleName())) { return false; } diff --git a/adapter/ohos/JsonUtil.java b/adapter/ohos/JsonUtil.java index a9d471512f089492790aa63742f2e51ea4fec8eb..f9488abd8f60c6ac930c5b818fe30d632be7ff89 100644 --- a/adapter/ohos/JsonUtil.java +++ b/adapter/ohos/JsonUtil.java @@ -59,6 +59,15 @@ public class JsonUtil { private static final String WHEN = "when"; private static final String STRING_RESOURCE = "$string:"; private static final String EMPTY = ""; + private static final String BUNDLENAME = "bundleName"; + private static final String VERSIONCODE = "versionCode"; + private static final String VERSIONNAME = "versionName"; + private static final String PATCH_VERSION_CODE = "patchVersionCode"; + private static final String PATCH_VERSION_NAME = "patchVersionName"; + private static final String ORIGINAL_MODULE_HASH = "originalModuleHash"; + private static final String MODULE = "module"; + private static final String DEVICE_TYPES = "deviceTypes"; + private static final String TYPE= "type"; /** @@ -1856,4 +1865,54 @@ public class JsonUtil { } return false; } + + /** + * parse patch.json form json string. + * + * @param jsonString is the file path of hqf file + * @return HQFVerifyInfo + */ + static HQFInfo parsePatch(String jsonString) throws BundleException { + HQFInfo hqfVerifyInfo = new HQFInfo(); + JSONObject jsonObject = JSON.parseObject(jsonString); + JSONObject appObj = jsonObject.getJSONObject(APP); + if (appObj == null) { + LOG.error("Error: parsePatch failed, input patch.json is invalid, patch.json has no app!"); + throw new BundleException("Error: parsePatch failed, input patch.json is invalid!"); + } + + if (appObj.containsKey(BUNDLENAME)) { + hqfVerifyInfo.setBundleName(appObj.getString(BUNDLENAME)); + } + if (appObj.containsKey(VERSIONCODE)) { + hqfVerifyInfo.setVersionCode(appObj.getIntValue(VERSIONCODE)); + } + if (appObj.containsKey(VERSIONNAME)) { + hqfVerifyInfo.setVersionName(appObj.getString(VERSIONNAME)); + } + if (appObj.containsKey(PATCH_VERSION_CODE)) { + hqfVerifyInfo.setPatchVersionCode(appObj.getIntValue(PATCH_VERSION_CODE)); + } + if (appObj.containsKey(PATCH_VERSION_NAME)) { + hqfVerifyInfo.setPatchVersionName(appObj.getString(PATCH_VERSION_NAME)); + } + JSONObject moduleObj = jsonObject.getJSONObject(MODULE); + if (moduleObj == null) { + LOG.error("Error: parse failed, input patch.json is invalid, patch.json has no module!"); + throw new BundleException("Error: parse failed, input patch.json is invalid, patch.json has no module!"); + } + if (moduleObj.containsKey(NAME)) { + hqfVerifyInfo.setModuleName(moduleObj.getString(NAME)); + } + if (moduleObj.containsKey(TYPE)) { + hqfVerifyInfo.setType(moduleObj.getString(TYPE)); + } + if (moduleObj.containsKey(DEVICE_TYPES)) { + hqfVerifyInfo.setDeviceTypes(JSONObject.parseArray(getJsonString(moduleObj, DEVICE_TYPES), String.class)); + } + if (moduleObj.containsKey(ORIGINAL_MODULE_HASH)) { + hqfVerifyInfo.setOriginalModuleHash(moduleObj.getString(ORIGINAL_MODULE_HASH)); + } + return hqfVerifyInfo; + } } diff --git a/adapter/ohos/ModuleJsonUtil.java b/adapter/ohos/ModuleJsonUtil.java index b2e9028b506dcadc5037c9326edf00e4f9a8c5ed..bc49da924ac006b3455cfa007f7db940207a863c 100644 --- a/adapter/ohos/ModuleJsonUtil.java +++ b/adapter/ohos/ModuleJsonUtil.java @@ -1082,7 +1082,7 @@ class ModuleJsonUtil { * @param hqfPath is the file path of hqf file * @return HQFVerifyInfo */ - static HQFVerifyInfo parseHQFInfo(String hqfPath) throws BundleException { + static HQFInfo parseHQFInfo(String hqfPath) throws BundleException { File hqfFile = new File(hqfPath); String patchJson = FileUtils.getJsonInZips(hqfFile, PATCH_JSOPN); return parsePatch(patchJson); @@ -1094,8 +1094,8 @@ class ModuleJsonUtil { * @param jsonString is the file path of hqf file * @return HQFVerifyInfo */ - static HQFVerifyInfo parsePatch(String jsonString) throws BundleException { - HQFVerifyInfo hqfVerifyInfo = new HQFVerifyInfo(); + static HQFInfo parsePatch(String jsonString) throws BundleException { + HQFInfo hqfVerifyInfo = new HQFInfo(); JSONObject jsonObject = JSON.parseObject(jsonString); JSONObject appObj = jsonObject.getJSONObject(APP); if (appObj == null) { diff --git a/adapter/ohos/Uncompress.java b/adapter/ohos/Uncompress.java index 9c3eed4ce083ce0f5debcfcabb876f0ec2c56581..d9ce15228ee927b49a7d5fb00d408fa47d578212 100644 --- a/adapter/ohos/Uncompress.java +++ b/adapter/ohos/Uncompress.java @@ -15,17 +15,10 @@ package ohos; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.util.*; import java.util.zip.CRC32; @@ -50,6 +43,7 @@ public class Uncompress { private static final String RPCID_SC = "rpcid.sc"; private static final String LINUX_FILE_SEPARATOR = "/"; private static final String TEMP_PATH = "temp"; + private static final String TEMP_PATH_APPQF = "temp_appqf_random"; private static final String HAP_SUFFIXI = ".hap"; private static final String ENTRY_TYPE = "entry"; private static final String SYSTEM_ACTION = "action.system.home"; @@ -62,6 +56,8 @@ public class Uncompress { private static final String SO_SUFFIX = ".so"; private static final String RESOURCE_PATH = "resources/base/profile/"; private static final String TRUE = "true"; + private static final String HQF_SUFFIX = ".hqf"; + private static final String PATCH_JSON = "patch.json"; private static final Log LOG = new Log(Uncompress.class.toString()); /** @@ -726,8 +722,6 @@ public class Uncompress { uncomperssResult.addProfileInfo(profileInfo); } - - private static HapZipInfo unZipHapFileFromInputStream(InputStream input) throws BundleException, IOException { BufferedInputStream bufIn = null; ZipInputStream zipIn = null; @@ -1724,4 +1718,55 @@ public class Uncompress { Utility.closeStream(zipFile); } } + + /** + * parrse appqf file. + * + * @param appqfPath is the path of appqf file. + * @throws BundleException if uncompress failed. + * @throws IOException if IOException happened. + */ + public static List parseAPPQFFile(String appqfPath) throws BundleException, IOException { + File appqfFile = new File(appqfPath); + String tmpPath = appqfFile.getParent() + File.separator + TEMP_PATH_APPQF; + File tempDir = new File(tmpPath); + boolean deletePath = false; + if (!tempDir.exists()) { + tempDir.mkdir(); + deletePath = true; + } + // unzip appqf file + FileUtils.unzipFile(appqfPath, tmpPath); + ZipFile zipFile = null; + ZipInputStream zipInputStream = null; + ZipEntry zipEntry = null; + List HQFlist = new ArrayList<>(); + try { + zipFile = new ZipFile(appqfFile); + zipInputStream = new ZipInputStream(new FileInputStream(appqfFile), Charset.forName("utf-8")); + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + if (zipEntry.getName().endsWith(HQF_SUFFIX)) { + HQFlist.add(zipEntry.getName()); + } + } + } catch (IOException e) { + LOG.error("uncompress::uncompressAPPQFFile failed, " + e.getMessage()); + throw new BundleException("uncompress::uncompressAPPQFFile failed!"); + } + // read patch.json in haf file + List jsonStringList = new ArrayList<>(); + for (String name : HQFlist) { + ZipFile hqfFile = new ZipFile(new File(tmpPath + File.separator + name)); + jsonStringList.add(FileUtils.getFileStringFromZip(PATCH_JSON, hqfFile)); + } + if (deletePath) { + FileUtils.deleteDirectory(tmpPath); + } + // parse patch.json + List hqfVerifyInfoList = new ArrayList<>(); + for (String patchString : jsonStringList) { + hqfVerifyInfoList.add(JsonUtil.parsePatch(patchString)); + } + return hqfVerifyInfoList; + } } diff --git a/adapter/ohos/UncompressEntrance.java b/adapter/ohos/UncompressEntrance.java index fa9e987812256c88f5564d96f2a4d4eaa5b74919..4a2dc10fc549723ddd29e02b420953c30bfc8e32 100644 --- a/adapter/ohos/UncompressEntrance.java +++ b/adapter/ohos/UncompressEntrance.java @@ -17,10 +17,12 @@ package ohos; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import static ohos.Uncompress.getResourceFromHap; + /** * bundle uncompress. * step1: parse arguments @@ -84,6 +86,8 @@ public class UncompressEntrance { */ public static final String DEVICE_TYPE_FITNESSBAND = "fitnessBand"; + private static final String APPQF_SUFFIX = ".appqf"; + private static final int EXIT_STATUS_NORMAL = 0; private static final int EXIT_STATUS_EXCEPTION = 1; private static final Log LOG = new Log(UncompressEntrance.class.toString()); @@ -348,7 +352,7 @@ public class UncompressEntrance { } /** - * Parse the hap. + * Parse the hap resource. * * @param hapPath Indicates the hap path. * @return Return the List result of parseHap @@ -357,6 +361,31 @@ public class UncompressEntrance { return getResourceFromHap(hapPath); } + /** + * Parse the appqf file. + * + * @param appqfPath Indicates the hap path. + * @return Return the List result of parseHap + */ + public static APPQFResult parseAPPQF(String appqfPath) { + APPQFResult result = new APPQFResult(); + if (!appqfPath.endsWith(APPQF_SUFFIX)) { + LOG.error("UncompressEntrance::parseAPPQF Error, input wrong type APPQF file!"); + result.setSuccess(false); + } + try { + result.setHqfInfoList(Uncompress.parseAPPQFFile(appqfPath)); + result.setSuccess(true); + } catch (BundleException e) { + LOG.error("UncompressEntrance::parseAPPQF failed, read patch.json in APPQF file failed!"); + result.setSuccess(false); + } catch (IOException e) { + LOG.error("UncompressEntrance::parseAPPQF failed, input APPQF file is invalid!"); + result.setSuccess(false); + } + return result; + } + /** * uncompress tool main function. * diff --git a/packingTool.sh b/packingTool.sh index 78d5b6ab8df5f761f5655fa8ce21c527dab4002d..5d68da7c78780763ddd4cef9bd85dd3a26a4e7ee 100755 --- a/packingTool.sh +++ b/packingTool.sh @@ -64,7 +64,7 @@ declare -a compile_class=( "HapVerify" "HapVerifyInfo" "HQFVerify" - "HQFVerifyInfo" + "HQFInfo" ) compile_class_length=${#compile_class[@]} for ((i=0; i<${compile_class_length};++i)) @@ -100,7 +100,7 @@ declare -a pack_class=( "HapVerify.class" "HapVerifyInfo.class" "HQFVerify.class" - "HQFVerifyInfo.class" + "HQFInfo.class" ) pack_class_length=${#pack_class[@]} for ((i=0; i<${pack_class_length};++i)) diff --git a/unpackingTool.sh b/unpackingTool.sh index 07ed6b87605f178c5fafd21c63438c844c90686f..f91e24dc5b66dcdd787e8189d7462f33c0d0498b 100755 --- a/unpackingTool.sh +++ b/unpackingTool.sh @@ -96,6 +96,8 @@ declare -a unpack_class=( "DefinePermission.java" "ResourceIndexResult.java" "FileUtils.java" + "HQFInfo.java" + "APPQFResult.java" ) unpack_class_length=${#unpack_class[@]} for ((i=0; i<${unpack_class_length};++i))