• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 com.alibaba.fastjson.JSON;
19 import com.alibaba.fastjson.JSONObject;
20 import com.alibaba.fastjson.serializer.SerializerFeature;
21 import org.apache.commons.compress.utils.IOUtils;
22 
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.nio.file.FileVisitResult;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.nio.file.SimpleFileVisitor;
34 import java.nio.file.StandardOpenOption;
35 import java.nio.file.attribute.BasicFileAttributes;
36 import java.util.Enumeration;
37 import java.util.zip.CRC32;
38 import java.util.zip.Deflater;
39 import java.util.zip.ZipEntry;
40 import java.util.zip.ZipFile;
41 import java.util.zip.ZipOutputStream;
42 
43 /**
44  * PackageNormalize, normalize HSP bundleName and versionCode
45  *
46  * @since 2024-04-06
47  */
48 public class PackageNormalize {
49     private static final Log LOG = new Log(PackageNormalize.class.toString());
50     private static final int BUFFER_SIZE = 10 * 1024;
51     private static final String MODULE_JSON = "module.json";
52     private static final String PACK_INFO = "pack.info";
53     private static final String APP = "app";
54     private static final String SUMMARY = "summary";
55     private static final String VERSION = "version";
56     private static final String CODE = "code";
57     private static final String BUNDLE_NAME = "bundleName";
58     private static final String VERSION_CODE = "versionCode";
59     private static final String TMP = "tmp";
60 
61     /**
62      * normalize HSP bundleName and versionCode
63      *
64      * @param utility common data
65      * @return true if normalize succeed
66      */
normalize(Utility utility)67     public static boolean normalize(Utility utility) {
68         Path outPath = Paths.get(utility.getOutPath());
69         for (String hspPath : utility.getFormattedHspPathList()) {
70             try {
71                 normalize(Paths.get(hspPath), outPath, utility.getBundleName(), utility.getVersionCode());
72             } catch (BundleException ex) {
73                 LOG.error("PackageNormalize::normalize BundleException: " + ex.getMessage());
74                 return false;
75             } catch (IOException ex) {
76                 LOG.error("PackageNormalize::normalize IOException: " + ex.getMessage());
77                 return false;
78             }
79         }
80         return true;
81     }
82 
normalize(Path hspPath, Path outPath, String bundleName, int versionCode)83     private static void normalize(Path hspPath, Path outPath, String bundleName, int versionCode)
84             throws BundleException, IOException {
85         Path outHspPath = outPath.resolve(hspPath.getFileName());
86         Path tmpDir = Files.createTempDirectory(outPath, TMP);
87         Path moduleJson = Files.createFile(tmpDir.resolve(MODULE_JSON));
88         Path packInfo = Files.createFile(tmpDir.resolve(PACK_INFO));
89         try (ZipFile hspFile = new ZipFile(hspPath.toFile());
90              OutputStream output = Files.newOutputStream(outHspPath);
91              ZipOutputStream zipOut = new ZipOutputStream(output)) {
92             // 1.unzip module.json and pack.info, copy to tmp
93             ZipEntry moduleEntry = hspFile.getEntry(MODULE_JSON);
94             if (moduleEntry != null) {
95                 try (OutputStream out = Files.newOutputStream(moduleJson)) {
96                     IOUtils.copy(hspFile.getInputStream(moduleEntry), out);
97                 }
98             }
99             ZipEntry packEntry = hspFile.getEntry(PACK_INFO);
100             if (moduleEntry != null) {
101                 try (OutputStream out = Files.newOutputStream(packInfo)) {
102                     IOUtils.copy(hspFile.getInputStream(packEntry), out);
103                 }
104             }
105 
106             // 2.update bundleName, versionCode to module.json, pack.info
107             updateModuleJson(moduleJson, bundleName, versionCode);
108             updatePackInfo(packInfo, bundleName, versionCode);
109 
110             // 3.zip hsp, module.json, pack.info to outPath
111             copyHsp(hspFile, moduleJson, packInfo, zipOut);
112         } finally {
113             rmdir(tmpDir);
114         }
115     }
116 
rmdir(Path dir)117     private static void rmdir(Path dir) throws IOException {
118         Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
119             @Override
120             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
121                 Files.delete(file);
122                 return FileVisitResult.CONTINUE;
123             }
124 
125             @Override
126             public FileVisitResult postVisitDirectory(Path dir, IOException ex) throws IOException {
127                 Files.delete(dir);
128                 return FileVisitResult.CONTINUE;
129             }
130         });
131     }
132 
updateModuleJson(Path moduleJson, String bundleName, int versionCode)133     private static void updateModuleJson(Path moduleJson, String bundleName, int versionCode)
134             throws BundleException, IOException {
135         try (FileInputStream input = new FileInputStream(moduleJson.toFile())) {
136             JSONObject jsonObject = JSON.parseObject(input, JSONObject.class);
137             if (jsonObject == null) {
138                 LOG.error("updateModuleJson failed, parse json is null.");
139                 throw new BundleException("updateModuleJson failed, parse json is null.");
140             }
141             if (!jsonObject.containsKey(APP)) {
142                 LOG.error("updateModuleJson failed, json format not invalid.");
143                 throw new BundleException("updateModuleJson failed, json format invalid.");
144             }
145             JSONObject appObject = jsonObject.getJSONObject(APP);
146             appObject.put(BUNDLE_NAME, bundleName);
147             appObject.put(VERSION_CODE, versionCode);
148             Files.write(moduleJson, JSON.toJSONBytes(jsonObject, SerializerFeature.WriteMapNullValue,
149                             SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField),
150                     StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
151         }
152     }
153 
updatePackInfo(Path packInfo, String bundleName, int versionCode)154     private static void updatePackInfo(Path packInfo, String bundleName, int versionCode)
155             throws BundleException, IOException {
156         try (FileInputStream input = new FileInputStream(packInfo.toFile())) {
157             JSONObject jsonObject = JSON.parseObject(input, JSONObject.class);
158             if (jsonObject == null) {
159                 LOG.error("updatePackInfo failed, json format invalid.");
160                 throw new BundleException("updatePackInfo failed, json format invalid.");
161             }
162             JSONObject summaryObject = jsonObject.getJSONObject(SUMMARY);
163             if (summaryObject == null) {
164                 LOG.error("updatePackInfo failed, json format invalid.");
165                 throw new BundleException("updatePackInfo failed, json format invalid.");
166             }
167             JSONObject appObject = summaryObject.getJSONObject(APP);
168             if (appObject == null) {
169                 LOG.error("updatePackInfo failed, json format invalid.");
170                 throw new BundleException("updatePackInfo failed, json format invalid.");
171             }
172             appObject.put(BUNDLE_NAME, bundleName);
173             JSONObject versionObject = appObject.getJSONObject(VERSION);
174             if (versionObject == null) {
175                 LOG.error("updatePackInfo failed, json format invalid.");
176                 throw new BundleException("updatePackInfo failed, json format invalid.");
177             }
178             versionObject.put(CODE, versionCode);
179             Files.write(packInfo, JSON.toJSONBytes(jsonObject, SerializerFeature.WriteMapNullValue,
180                             SerializerFeature.WriteDateUseDateFormat, SerializerFeature.SortField),
181                     StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
182         }
183     }
184 
copyHsp(ZipFile hspFile, Path moduleJson, Path packInfo, ZipOutputStream zipOut)185     private static void copyHsp(ZipFile hspFile, Path moduleJson, Path packInfo, ZipOutputStream zipOut)
186             throws BundleException, IOException {
187         zipOut.setLevel(Deflater.BEST_SPEED);
188         boolean isStored = true;
189         Enumeration<? extends ZipEntry> entries = hspFile.entries();
190         while (entries.hasMoreElements()) {
191             ZipEntry zipEntry = entries.nextElement();
192             if (MODULE_JSON.equals(zipEntry.getName()) || PACK_INFO.equals(zipEntry.getName())) {
193                 isStored = zipEntry.getMethod() == ZipEntry.STORED;
194                 continue;
195             }
196             ZipEntry newEntry = zipEntry.getMethod() ==
197                     ZipEntry.STORED ? new ZipEntry(zipEntry) : new ZipEntry(zipEntry.getName());
198             zipOut.putNextEntry(newEntry);
199             if (!zipEntry.isDirectory()) {
200                 IOUtils.copy(hspFile.getInputStream(zipEntry), zipOut);
201             }
202             zipOut.closeEntry();
203         }
204         compressFile(moduleJson, isStored, MODULE_JSON, zipOut);
205         compressFile(packInfo, isStored, PACK_INFO, zipOut);
206     }
207 
compressFile(Path file, boolean isStored, String entryName, ZipOutputStream zipOut)208     private static void compressFile(Path file, boolean isStored, String entryName, ZipOutputStream zipOut)
209             throws BundleException, IOException {
210         try (InputStream input = Files.newInputStream(file)) {
211             if (isStored) {
212                 ZipEntry newEntry = new ZipEntry(entryName);
213                 newEntry.setMethod(ZipEntry.STORED);
214                 File jsonFile = file.toFile();
215                 newEntry.setCompressedSize(jsonFile.length());
216                 CRC32 crc = getCrcFromFile(jsonFile);
217                 newEntry.setCrc(crc.getValue());
218                 zipOut.putNextEntry(newEntry);
219             } else {
220                 ZipEntry newEntry = new ZipEntry(entryName);
221                 newEntry.setMethod(ZipEntry.DEFLATED);
222                 zipOut.putNextEntry(newEntry);
223             }
224             IOUtils.copy(input, zipOut);
225             zipOut.closeEntry();
226         }
227     }
228 
getCrcFromFile(File file)229     private static CRC32 getCrcFromFile(File file) throws BundleException {
230         CRC32 crc = new CRC32();
231         try (FileInputStream fileInputStream = new FileInputStream(file)) {
232             byte[] buffer = new byte[BUFFER_SIZE];
233             int count = fileInputStream.read(buffer);
234             while (count > 0) {
235                 crc.update(buffer, 0, count);
236                 count = fileInputStream.read(buffer);
237             }
238         } catch (FileNotFoundException ignored) {
239             LOG.error("PackageNormalize::getCrcFromFile file not found exception: " + ignored.getMessage());
240             throw new BundleException("Get Crc from file failed.");
241         } catch (IOException exception) {
242             LOG.error("PackageNormalize::getCrcFromFile io exception: " + exception.getMessage());
243             throw new BundleException("Get Crc from file failed, io exception.");
244         }
245         return crc;
246     }
247 }
248