/* * Tencent is pleased to support the open source community by making Tinker available. * * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause * * 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 com.tencent.tinker.build.util; import com.tencent.tinker.build.decoder.ResDiffDecoder; import com.tencent.tinker.build.patch.Configuration; import com.tencent.tinker.commons.resutil.ResUtil; import com.tencent.tinker.commons.ziputil.TinkerZipEntry; import com.tencent.tinker.commons.ziputil.TinkerZipFile; import com.tencent.tinker.commons.ziputil.TinkerZipOutputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.regex.Pattern; /** * Created by sun on 1/9/16. */ public class Utils { public static boolean isPresent(String str) { return str != null && str.length() > 0; } public static boolean isBlank(String str) { return !isPresent(str); } public static boolean isPresent(Iterator iterator) { return iterator != null && iterator.hasNext(); } public static boolean isBlank(Iterator iterator) { return !isPresent(iterator); } public static String convertToPatternString(String input) { //convert \\. if (input.contains(".")) { input = input.replaceAll("\\.", "\\\\."); } //convert ?to . if (input.contains("?")) { input = input.replaceAll("\\?", "\\."); } //convert * to.* if (input.contains("*")) { input = input.replace("*", ".*"); } return input; } public static boolean isStringMatchesPatterns(String str, Collection<Pattern> patterns) { for (Pattern pattern : patterns) { if (pattern.matcher(str).matches()) { return true; } } return false; } public static <T> String collectionToString(Collection<T> collection) { StringBuilder sb = new StringBuilder(); sb.append('{'); boolean isFirstElement = true; for (T element : collection) { if (isFirstElement) { isFirstElement = false; } else { sb.append(','); } sb.append(element); } sb.append('}'); return sb.toString(); } public static boolean checkFileInPattern(HashSet<Pattern> patterns, String key) { if (!patterns.isEmpty()) { for (Iterator<Pattern> it = patterns.iterator(); it.hasNext();) { Pattern p = it.next(); if (p.matcher(key).matches()) { return true; } } } return false; } public static String genResOutputFile(File output, File newZipFile, Configuration config, ArrayList<String> addedSet, ArrayList<String> modifiedSet, ArrayList<String> deletedSet, ArrayList<String> largeModifiedSet, HashMap<String, ResDiffDecoder.LargeModeInfo> largeModifiedMap) throws IOException { TinkerZipFile oldApk = new TinkerZipFile(config.mOldApkFile); TinkerZipFile newApk = new TinkerZipFile(newZipFile); TinkerZipOutputStream out = new TinkerZipOutputStream(new BufferedOutputStream(new FileOutputStream(output))); try { final Enumeration<? extends TinkerZipEntry> entries = oldApk.entries(); while (entries.hasMoreElements()) { TinkerZipEntry zipEntry = entries.nextElement(); if (zipEntry == null) { throw new TinkerPatchException( String.format("zipEntry is null when get from oldApk") ); } String name = zipEntry.getName(); if (name.contains("../")) { continue; } if (Utils.checkFileInPattern(config.mResFilePattern, name)) { //won't contain in add set. if (!deletedSet.contains(name) && !modifiedSet.contains(name) && !largeModifiedSet.contains(name) && !name.equals(TypedValue.RES_MANIFEST)) { ResUtil.extractTinkerEntry(oldApk, zipEntry, out); } } } //process manifest TinkerZipEntry manifestZipEntry = oldApk.getEntry(TypedValue.RES_MANIFEST); if (manifestZipEntry == null) { throw new TinkerPatchException( String.format("can't found resource file %s from old apk file %s", TypedValue.RES_MANIFEST, config.mOldApkFile.getAbsolutePath()) ); } ResUtil.extractTinkerEntry(oldApk, manifestZipEntry, out); for (String name : largeModifiedSet) { TinkerZipEntry largeZipEntry = oldApk.getEntry(name); if (largeZipEntry == null) { throw new TinkerPatchException( String.format("can't found resource file %s from old apk file %s", name, config.mOldApkFile.getAbsolutePath()) ); } ResDiffDecoder.LargeModeInfo largeModeInfo = largeModifiedMap.get(name); ResUtil.extractLargeModifyFile(largeZipEntry, largeModeInfo.path, largeModeInfo.crc, out); } for (String name : addedSet) { TinkerZipEntry addZipEntry = newApk.getEntry(name); if (addZipEntry == null) { throw new TinkerPatchException( String.format("can't found add resource file %s from new apk file %s", name, config.mNewApkFile.getAbsolutePath()) ); } ResUtil.extractTinkerEntry(newApk, addZipEntry, out); } for (String name : modifiedSet) { TinkerZipEntry modZipEntry = newApk.getEntry(name); if (modZipEntry == null) { throw new TinkerPatchException( String.format("can't found add resource file %s from new apk file %s", name, config.mNewApkFile.getAbsolutePath()) ); } ResUtil.extractTinkerEntry(newApk, modZipEntry, out); } } finally { out.close(); oldApk.close(); newApk.close(); } return MD5.getMD5(output); } public static String getResourceMeta(String baseCrc, String md5) { return TypedValue.RES_OUT + "," + baseCrc + "," + md5; } /** * if bsDiff result is too larger, just treat it as newly file * @param bsDiffFile * @param newFile * @return */ public static boolean checkBsDiffFileSize(File bsDiffFile, File newFile) { if (!bsDiffFile.exists()) { throw new TinkerPatchException("can not find the bsDiff file:" + bsDiffFile.getAbsolutePath()); } //check bsDiffFile file size double ratio = bsDiffFile.length() / (double) newFile.length(); if (ratio > TypedValue.BSDIFF_PATCH_MAX_RATIO) { Logger.e("bsDiff patch file:%s, size:%dk, new file:%s, size:%dk. patch file is too large, treat it as newly file to save patch time!", bsDiffFile.getName(), bsDiffFile.length() / 1024, newFile.getName(), newFile.length() / 1024 ); return false; } return true; } public static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { e.printStackTrace(); } } public static void exec(ArrayList<String> args, File path) throws RuntimeException, IOException, InterruptedException { ProcessBuilder ps = new ProcessBuilder(args); ps.redirectErrorStream(true); if (path != null) { ps.directory(path); } Process pr = ps.start(); BufferedReader ins = new BufferedReader(new InputStreamReader(pr.getInputStream())); String line; while ((line = ins.readLine()) != null) { System.out.println(line); } if (pr.waitFor() != 0) { throw new RuntimeException("exec cmd failed! args: " + args); } ins.close(); } }