content: package com.taobao.android.tools;
  
  /*
   *
   *
   *                                  Apache License
   *                            Version 2.0, January 2004
   *                         http://www.apache.org/licenses/
   *
   *    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
   *
   *    1. Definitions.
   *
   *       "License" shall mean the terms and conditions for use, reproduction,
   *       and distribution as defined by Sections 1 through 9 of this document.
   *
   *       "Licensor" shall mean the copyright owner or entity authorized by
   *       the copyright owner that is granting the License.
   *
   *       "Legal Entity" shall mean the union of the acting entity and all
   *       other entities that control, are controlled by, or are under common
   *       control with that entity. For the purposes of this definition,
   *       "control" means (i) the power, direct or indirect, to cause the
   *       direction or management of such entity, whether by contract or
   *       otherwise, or (ii) ownership of fifty percent (50%) or more of the
   *       outstanding shares, or (iii) beneficial ownership of such entity.
   *
   *       "You" (or "Your") shall mean an individual or Legal Entity
   *       exercising permissions granted by this License.
   *
   *       "Source" form shall mean the preferred form for making modifications,
   *       including but not limited to software source code, documentation
   *       source, and configuration files.
   *
   *       "Object" form shall mean any form resulting from mechanical
   *       transformation or translation of a Source form, including but
   *       not limited to compiled object code, generated documentation,
   *       and conversions to other media types.
   *
   *       "Work" shall mean the work of authorship, whether in Source or
   *       Object form, made available under the License, as indicated by a
   *       copyright notice that is included in or attached to the work
   *       (an example is provided in the Appendix below).
   *
   *       "Derivative Works" shall mean any work, whether in Source or Object
   *       form, that is based on (or derived from) the Work and for which the
   *       editorial revisions, annotations, elaborations, or other modifications
   *       represent, as a whole, an original work of authorship. For the purposes
   *       of this License, Derivative Works shall not include works that remain
   *       separable from, or merely link (or bind by name) to the interfaces of,
   *       the Work and Derivative Works thereof.
   *
   *       "Contribution" shall mean any work of authorship, including
   *       the original version of the Work and any modifications or additions
   *       to that Work or Derivative Works thereof, that is intentionally
   *       submitted to Licensor for inclusion in the Work by the copyright owner
   *       or by an individual or Legal Entity authorized to submit on behalf of
   *       the copyright owner. For the purposes of this definition, "submitted"
   *       means any form of electronic, verbal, or written communication sent
   *       to the Licensor or its representatives, including but not limited to
   *       communication on electronic mailing lists, source code control systems,
   *       and issue tracking systems that are managed by, or on behalf of, the
   *       Licensor for the purpose of discussing and improving the Work, but
   *       excluding communication that is conspicuously marked or otherwise
   *       designated in writing by the copyright owner as "Not a Contribution."
   *
   *       "Contributor" shall mean Licensor and any individual or Legal Entity
   *       on behalf of whom a Contribution has been received by Licensor and
   *       subsequently incorporated within the Work.
   *
   *    2. Grant of Copyright License. Subject to the terms and conditions of
   *       this License, each Contributor hereby grants to You a perpetual,
   *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   *       copyright license to reproduce, prepare Derivative Works of,
   *       publicly display, publicly perform, sublicense, and distribute the
   *       Work and such Derivative Works in Source or Object form.
   *
   *    3. Grant of Patent License. Subject to the terms and conditions of
   *       this License, each Contributor hereby grants to You a perpetual,
   *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   *       (except as stated in this section) patent license to make, have made,
   *       use, offer to sell, sell, import, and otherwise transfer the Work,
   *       where such license applies only to those patent claims licensable
   *       by such Contributor that are necessarily infringed by their
   *       Contribution(s) alone or by combination of their Contribution(s)
   *       with the Work to which such Contribution(s) was submitted. If You
   *       institute patent litigation against any entity (including a
   *       cross-claim or counterclaim in a lawsuit) alleging that the Work
   *       or a Contribution incorporated within the Work constitutes direct
   *       or contributory patent infringement, then any patent licenses
   *       granted to You under this License for that Work shall terminate
   *       as of the date such litigation is filed.
   *
   *    4. Redistribution. You may reproduce and distribute copies of the
   *       Work or Derivative Works thereof in any medium, with or without
   *       modifications, and in Source or Object form, provided that You
   *       meet the following conditions:
   *
   *       (a) You must give any other recipients of the Work or
   *           Derivative Works a copy of this License; and
   *
   *       (b) You must cause any modified files to carry prominent notices
   *           stating that You changed the files; and
   *
   *       (c) You must retain, in the Source form of any Derivative Works
   *           that You distribute, all copyright, patent, trademark, and
   *           attribution notices from the Source form of the Work,
   *           excluding those notices that do not pertain to any part of
   *           the Derivative Works; and
   *
   *       (d) If the Work includes a "NOTICE" text file as part of its
   *           distribution, then any Derivative Works that You distribute must
   *           include a readable copy of the attribution notices contained
   *           within such NOTICE file, excluding those notices that do not
   *           pertain to any part of the Derivative Works, in at least one
   *           of the following places: within a NOTICE text file distributed
   *           as part of the Derivative Works; within the Source form or
   *           documentation, if provided along with the Derivative Works; or,
   *           within a display generated by the Derivative Works, if and
   *           wherever such third-party notices normally appear. The contents
   *           of the NOTICE file are for informational purposes only and
   *           do not modify the License. You may add Your own attribution
   *           notices within Derivative Works that You distribute, alongside
   *           or as an addendum to the NOTICE text from the Work, provided
   *           that such additional attribution notices cannot be construed
   *           as modifying the License.
   *
   *       You may add Your own copyright statement to Your modifications and
   *       may provide additional or different license terms and conditions
   *       for use, reproduction, or distribution of Your modifications, or
   *       for any such Derivative Works as a whole, provided Your use,
   *       reproduction, and distribution of the Work otherwise complies with
   *       the conditions stated in this License.
   *
   *    5. Submission of Contributions. Unless You explicitly state otherwise,
   *       any Contribution intentionally submitted for inclusion in the Work
   *       by You to the Licensor shall be under the terms and conditions of
   *       this License, without any additional terms or conditions.
   *       Notwithstanding the above, nothing herein shall supersede or modify
   *       the terms of any separate license agreement you may have executed
   *       with Licensor regarding such Contributions.
   *
   *    6. Trademarks. This License does not grant permission to use the trade
   *       names, trademarks, service marks, or product names of the Licensor,
   *       except as required for reasonable and customary use in describing the
   *       origin of the Work and reproducing the content of the NOTICE file.
   *
   *    7. Disclaimer of Warranty. Unless required by applicable law or
   *       agreed to in writing, Licensor provides the Work (and each
   *       Contributor provides its Contributions) on an "AS IS" BASIS,
   *       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   *       implied, including, without limitation, any warranties or conditions
   *       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   *       PARTICULAR PURPOSE. You are solely responsible for determining the
   *       appropriateness of using or redistributing the Work and assume any
   *       risks associated with Your exercise of permissions under this License.
   *
   *    8. Limitation of Liability. In no event and under no legal theory,
   *       whether in tort (including negligence), contract, or otherwise,
   *       unless required by applicable law (such as deliberate and grossly
   *       negligent acts) or agreed to in writing, shall any Contributor be
   *       liable to You for damages, including any direct, indirect, special,
   *       incidental, or consequential damages of any character arising as a
   *       result of this License or out of the use or inability to use the
   *       Work (including but not limited to damages for loss of goodwill,
   *       work stoppage, computer failure or malfunction, or any and all
   *       other commercial damages or losses), even if such Contributor
   *       has been advised of the possibility of such damages.
   *
   *    9. Accepting Warranty or Additional Liability. While redistributing
   *       the Work or Derivative Works thereof, You may choose to offer,
   *       and charge a fee for, acceptance of support, warranty, indemnity,
   *       or other liability obligations and/or rights consistent with this
   *       License. However, in accepting such obligations, You may act only
   *       on Your own behalf and on Your sole responsibility, not on behalf
   *       of any other Contributor, and only if You agree to indemnify,
   *       defend, and hold each Contributor harmless for any liability
   *       incurred by, or claims asserted against, such Contributor by reason
   *       of your accepting any such warranty or additional liability.
   *
   *    END OF TERMS AND CONDITIONS
   *
   *    APPENDIX: How to apply the Apache License to your work.
   *
   *       To apply the Apache License to your work, attach the following
   *       boilerplate notice, with the fields enclosed by brackets "[]"
   *       replaced with your own identifying information. (Don't include
   *       the brackets!)  The text should be enclosed in the appropriate
   *       comment syntax for the file format. We also recommend that a
   *       file or class name and description of purpose be included on the
   *       same "printed page" as the copyright notice for easier
   *       identification within third-party archives.
   *
   *    Copyright 2016 Alibaba Group
   *
   *    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.
   *
   *
   */
  
  import java.io.*;
  import java.sql.Date;
  import java.util.*;
  import java.util.concurrent.Callable;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.atomic.AtomicReference;
  import java.util.function.Consumer;
  import java.util.function.UnaryOperator;
  import java.util.jar.Attributes;
  import java.util.jar.JarOutputStream;
  import java.util.jar.Manifest;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipFile;
  
  import com.alibaba.fastjson.JSON;
  
  import com.android.utils.Pair;
  import com.google.common.collect.Lists;
  import com.taobao.android.differ.dex.ApkDiff;
  import com.taobao.android.differ.dex.BundleDiffResult;
  import com.taobao.android.differ.dex.PatchException;
  import com.taobao.android.inputs.TpatchInput;
  import com.taobao.android.object.*;
  import com.taobao.android.outputs.PatchFile;
  import com.taobao.android.outputs.TpatchFile;
  import com.taobao.android.task.ExecutorServicesHelper;
  import com.taobao.android.tpatch.builder.PatchFileBuilder;
  import com.taobao.android.tpatch.manifest.AndroidManifestDiffFactory;
  import com.taobao.android.tpatch.model.BundleBO;
  import com.taobao.android.tpatch.utils.HttpClientUtils;
  import com.taobao.android.tpatch.utils.MD5Util;
  import com.taobao.android.tpatch.utils.PatchUtils;
  import com.taobao.android.tpatch.utils.PathUtils;
  import com.taobao.android.utils.CommandUtils;
  import com.taobao.android.utils.PathMatcher;
  import com.taobao.android.utils.Profiler;
  import com.taobao.android.utils.SoDiffUtils;
  import com.taobao.checker.Checker;
  import com.taobao.update.UpdateInfo;
  import org.antlr.runtime.RecognitionException;
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.io.FilenameUtils;
  import org.apache.commons.io.IOUtils;
  import org.apache.commons.io.filefilter.IOFileFilter;
  import org.apache.commons.io.filefilter.TrueFileFilter;
  import org.apache.commons.lang3.StringEscapeUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.jf.dexlib2.iface.ClassDef;
  
  /**
   * generate atlas dynamic tpatch
   * <p/>
   *
   * Created by lilong on 16/6/17.
   */
  
  public class TPatchTool extends AbstractTool {
  
      public ApkDiff apkDiff = new ApkDiff();
  
  
      public Map<String,Integer> bundleTypes = new HashMap<>();
  
      public ApkDiff apkPatchInfos = new ApkDiff();
  
      public static List<SoFileDef>soFileDefs = new ArrayList<>();
  
      public List<BundleDiffResult> bundleDiffResults = Collections.synchronizedList(new ArrayList<BundleDiffResult>());
  
      public List<BundleDiffResult> diffPatchInfos = Collections.synchronizedList(new ArrayList<BundleDiffResult>());
  
      public final PathMatcher pathMatcher = new PathMatcher();
  
      private final String ANDROID_MANIFEST = "AndroidManifest.xml";
  
      private static final String[] DEFAULT_NOT_INCLUDE_RESOURCES = new String[] {"*.dex",
          "lib/**",
          "META-INF/**"};
  
      public static File hisTpatchFolder;
  
      public static final String SO_PATCH_META = "SO-PATCH-INF";
  
  
      public static final String DEX_PATCH_META = "DEX-PATCH-INF";
  
  
      private boolean hasMainBundle;
  
      public static Map<String,LinkedHashMap>bundleInfos = new HashMap<String, LinkedHashMap>();
  
      private final Map<String, Map<String, ClassDef>> bundleClassMap
          = new ConcurrentHashMap<String, Map<String, ClassDef>>();
  
      private final List<String> whiteList = new ArrayList<>();
      
  
      private List<String> msgToString(List<Checker.ReasonMsg> msgs) {
          List<String>ss = new ArrayList<>();
          for (Checker.ReasonMsg reasonMsg:msgs){
              ss.add(reasonMsg.toString());
          }
          return ss;
      }
      public boolean isBundleFile(File file) {
          if (whiteList.size() > 1) {
              for (String bundleName : whiteList) {
                  if (file.getAbsolutePath().replace("\\", "/").endsWith(bundleName)) {
                      return true;
                  }
              }
          } else {
              return PatchUtils.isBundleFile(file);
          }
  
          return false;
  
      }
  
      private void readWhiteList(File whiteListFile) throws Exception {
  //        File whiteListFile = new File(parentFile, "bundleList.cfg");
          if (whiteListFile.exists()) {
              BufferedReader br = null;
              br = new BufferedReader(new InputStreamReader(new FileInputStream(whiteListFile),
                                                            "UTF-8"));
              String lineTxt = null;
              while ((lineTxt = br.readLine()) != null) {
                  whiteList.add(lineTxt);
              }
              br.close();
          }
      }
  
      private File createTPatchFile(File outPatchDir, File patchTmpDir) throws IOException {
          // 首先压缩主bundle,先判断主bundle里有没有文件
          File mainBundleFoder = new File(patchTmpDir, ((TpatchInput)input).mainBundleName);
          File metaFile = new File(mainBundleFoder,SO_PATCH_META);
          if (soFileDefs.size() > 0){
              com.taobao.android.tpatch.model.PatchFile patchFile = new com.taobao.android.tpatch.model.PatchFile(metaFile);
              soFileDefs.stream().forEach(new Consumer<SoFileDef>() {
                  @Override
                  public void accept(SoFileDef soFileDef) {
                      patchFile.append(soFileDef);
                  }
              });
              patchFile.close();
          }
          File mainBundleFile = new File(patchTmpDir, ((TpatchInput)input).mainBundleName + ".so");
          if (FileUtils.listFiles(mainBundleFoder, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
              .size() > 0) {
              hasMainBundle = true;
              CommandUtils.exec(mainBundleFoder, "zip -r " + mainBundleFile.getAbsolutePath() + " . -x */ -x .*");
          }
          FileUtils.deleteDirectory(mainBundleFoder);
  
          // 再压缩各自的bundle
          File patchFile = null;
  
              patchFile = new File(outPatchDir,
                      "patch-" + input.newApkBo.getVersionName() + "@" + input.baseApkBo.getVersionName() + ".tpatch");
          if (patchFile.exists()) {
              FileUtils.deleteQuietly(patchFile);
          }
  
          File infoFile = new File(patchTmpDir,"patchInfo");
          FileUtils.writeStringToFile(infoFile, "patch-" + input.newApkBo.getVersionName() + "@" + input.baseApkBo.getVersionName() + ".tpatch");
  
          //        zipBundle(patchTmpDir, patchFile);
          CommandUtils.exec(patchTmpDir, "zip -r " + patchFile.getAbsolutePath() + " . -x */ -x .*");
          FileUtils.deleteDirectory(patchTmpDir);
          return patchFile;
      }
  
  
      /**
       * process bundle files
       *
       * @param newBundleFile
       * @param baseBundleFile
       * @param patchTmpDir
       * @param diffTxtFile
       */
      public void processBundleFiles(File newBundleFile,
                                      File baseBundleFile,
                                      File patchTmpDir) throws Exception {
          String bundleName = FilenameUtils.getBaseName(newBundleFile.getName());
          File destPatchBundleDir = new File(patchTmpDir, bundleName);
          final File newBundleUnzipFolder = new File(newBundleFile.getParentFile(), bundleName);
          final File baseBundleUnzipFolder = new File(baseBundleFile.getParentFile(), bundleName);
  
          DiffType modifyType = getModifyType(newBundleFile.getName());
  
          long startTime = System.currentTimeMillis();
  
          logger.warning(">>> start to process bundle for patch " + bundleName + " >> difftype " + modifyType.toString() + " createALl:" + ((TpatchInput)input).createAll);
  
          if (modifyType == DiffType.ADD) {
  
              FileUtils.copyFileToDirectory(newBundleFile, patchTmpDir);
  
          } else if (((TpatchInput)input).createAll || (modifyType == DiffType.MODIFY) )  {
  
              if (null != baseBundleFile &&
                  baseBundleFile.isFile() &&
                  baseBundleFile.exists() &&
                  !((TpatchInput)input).noPatchBundles.contains(baseBundleFile.getName()
                                               .replace("_", ".")
                                               .substring(3,
                                                          baseBundleFile.getName().length() -
                                                              3)) &&
                  input.diffBundleDex) {
                  doBundlePatch(newBundleFile, baseBundleFile, patchTmpDir, bundleName, destPatchBundleDir,
                                newBundleUnzipFolder,
                                baseBundleUnzipFolder);
              }
          }
  
          logger.warning(">>> fininsh to process bundle for patch " + bundleName + " >> difftype " + modifyType.toString() + " consume:" + (System.currentTimeMillis() - startTime));
      }
  
      public void doBundlePatch(File newBundleFile, File baseBundleFile, File patchTmpDir, String bundleName,
                                 File destPatchBundleDir, final File newBundleUnzipFolder, File baseBundleUnzipFolder)
          throws Exception {
  
  
          doBundleDexPatch(newBundleFile,baseBundleFile,patchTmpDir,bundleName,destPatchBundleDir,newBundleUnzipFolder,baseBundleUnzipFolder);
  
          doBundleResPatch(bundleName,destPatchBundleDir,newBundleUnzipFolder,baseBundleUnzipFolder);
          // unzip
          // compare dex changes
  
  
  
      }
  
      public void doBundleResPatch(String bundleName, File destPatchBundleDir, File newBundleUnzipFolder, File baseBundleUnzipFolder) throws IOException {
          // compare resource changes
          Collection<File> newBundleResFiles = FileUtils.listFiles(newBundleUnzipFolder,
                  new IOFileFilter() {
  
                      @Override
                      public boolean accept(File file) {
                          // 不包括dex文件
                          if (file.getName()
                                  .endsWith(
                                          ".dex")) {
                              return false;
                          }
                          String relativePath = PathUtils
                                  .toRelative(
                                          newBundleUnzipFolder,
                                          file.getAbsolutePath());
                          if (null !=
                                  ((TpatchInput)(input)).notIncludeFiles &&
                                  pathMatcher.match(
                                          ((TpatchInput)(input)).notIncludeFiles,
                                          relativePath)) {
                              return false;
                          }
                          return true;
                      }
  
                      @Override
                      public boolean accept(File file,
                                            String s) {
                          return accept(new File(
                                  file,
                                  s));
                      }
                  },
                  TrueFileFilter.INSTANCE);
  
          for (File newBundleResFile : newBundleResFiles) {
              String resPath = PathUtils.toRelative(newBundleUnzipFolder,
                      newBundleResFile.getAbsolutePath());
              File baseBundleResFile = new File(baseBundleUnzipFolder, resPath);
              File destResFile = new File(destPatchBundleDir, resPath);
              if (baseBundleResFile.exists()) {
                  if (isFileModify(newBundleResFile,
                          baseBundleResFile,
                          bundleName,
                          resPath)) { // modify resource
                      if (baseBundleResFile.getName().endsWith(".so")){
                          if (((TpatchInput)input).diffNativeSo && ((TpatchInput) input).diffBundleSo){
                              destResFile = new File(destPatchBundleDir,resPath.concat(".patch"));
                              SoDiffUtils.diffSo(baseBundleResFile.getParentFile(),baseBundleResFile,newBundleResFile,destResFile);
  
                          }else {
                              FileUtils.copyFile(newBundleResFile, destResFile);
                          }
  
                      }else {
                          FileUtils.copyFile(newBundleResFile, destResFile);
                      }
                  }
              } else {// new resource
                  FileUtils.copyFile(newBundleResFile, destResFile);
              }
          }
      }
  
      public void doBundleDexPatch(File newBundleFile, File baseBundleFile, File patchTmpDir, String bundleName, File destPatchBundleDir, File newBundleUnzipFolder, File baseBundleUnzipFolder) throws Exception {
          CommandUtils.exec(patchTmpDir,
                  "unzip " + newBundleFile.getAbsolutePath() + " -d " + newBundleUnzipFolder.getAbsolutePath());
          CommandUtils.exec(patchTmpDir, "unzip " + baseBundleFile.getAbsolutePath() + " -d " + baseBundleUnzipFolder
                  .getAbsolutePath());
  //        File destDex = new File(destPatchBundleDir, DEX_NAME);
  //        File tmpDexFolder = new File(patchTmpDir, bundleName + "-dex");
          createBundleDexPatch(newBundleUnzipFolder,
                  baseBundleUnzipFolder,
                  destPatchBundleDir,
                  false);
  
      }
  
      /**
       * resource changes in main bundle
       *
       * @param newApkUnzipFolder
       * @param baseApkUnzipFolder
       * @param patchTmpDir
       * @param retainFiles
       * @throws IOException
       */
      public void copyMainBundleResources(final File newApkUnzipFolder, final File baseApkUnzipFolder, File patchTmpDir,
                                          Collection<File> retainFiles) throws IOException {
          boolean resoureModified = false;
  
          for (File retainFile : retainFiles) {
              String relativePath = PathUtils.toRelative(newApkUnzipFolder,
                                                         retainFile.getAbsolutePath());
              File baseFile = new File(baseApkUnzipFolder, relativePath);
              if (isBundleFile(retainFile)) {
              } else if (isFileModify(retainFile, baseFile)) {
                  resoureModified = true;
                  File destFile = new File(patchTmpDir, relativePath);
                  FileUtils.copyFile(retainFile, destFile);
              }
          }
          if (resoureModified) {
              File AndroidMenifestFile = new File(newApkUnzipFolder, ANDROID_MANIFEST);
              FileUtils.copyFileToDirectory(AndroidMenifestFile, patchTmpDir);
          }
      }
  
      /**
       * get bundle diff dex file
       *
       * @param newApkUnzipFolder
       * @param baseApkUnzipFolder
       * @param destDex
       * @param tmpDexFile
       * @param diffTxtFile
       * @return
       * @throws IOException
       * @throws RecognitionException
       */
      public List<File> createBundleDexPatch(File newApkUnzipFolder,
                                        File baseApkUnzipFolder,
                                        File diffDexFolder,
                                        boolean mainDex) throws Exception {
          List<File> dexs = Lists.newArrayList();
          // 比较主bundle的dex
  //        if (!tmpDexFile.exists()) {
  //            tmpDexFile.mkdirs();
  //        }
          List<File> baseDexFiles = getFolderDexFiles(baseApkUnzipFolder);
          List<File> newDexFiles = getFolderDexFiles(newApkUnzipFolder);
          PatchDexTool dexTool = new TpatchDexTool(baseDexFiles,
                                                    newDexFiles,
                                                    DEFAULT_API_LEVEL,
                                                    bundleClassMap.get(diffDexFolder.getName()),
                                                    mainDex);
          dexTool.setNewPatch(((TpatchInput)input).newPatch);
          DexDiffInfo dexDiffInfo = dexTool.createPatchDex(diffDexFolder);
          if (diffDexFolder.listFiles()!= null && diffDexFolder.listFiles().length > 0) {
              dexs.addAll(FileUtils.listFiles(diffDexFolder,new String[]{"dex"},true));
              BundleDiffResult bundleDiffResult = new BundleDiffResult();
              if (mainDex) {
                  bundleDiffResult.setBundleName("com.taobao.maindex");
  
              } else {
                  bundleDiffResult.setBundleName(baseApkUnzipFolder.getName().substring(3).replace("_", "."));
              }
              bundleDiffResults.add(bundleDiffResult);
              diffPatchInfos.add(bundleDiffResult);
              dexDiffInfo.save(bundleDiffResult);
          }
  //        if (dexs.size() > 0) {
  //
  //            FileUtils.copyFile(dexs.get(0), destDex);
  //        }
  //
  //        FileUtils.deleteDirectory(tmpDexFile);
  //        if (mainDex){
  //            try {
  //
  //                bundleInfos.put(input.newApkBo.getVersionName(), new AtlasFrameworkPropertiesReader(
  //                        new MethodReader(
  //                                new ClassReader(
  //                                        new DexReader(destDex))), null).read("Landroid/taobao/atlas/framework/FrameworkProperties;", "<clinit>"));
  //                bundleInfos.put(input.baseApkBo.getVersionName(), new AtlasFrameworkPropertiesReader(
  //                        new MethodReader(
  //                                new ClassReader(
  //                                        new DexReader(baseDexFiles))), bundleInfos.get(input.newApkBo.getVersionName())).read("Landroid/taobao/atlas/framework/FrameworkProperties;", "<clinit>"));
  //            }catch (Throwable e){
  //                e.printStackTrace();
  //            }
  //
  //            }
  
          return dexs;
      }
  
      /**
       * 获取基准patch包的patchInfo对象
       *
       * @param fileName
       * @return
       */
      public PatchInfo createBasePatchInfo(File file) {
          PatchInfo patchInfo = new PatchInfo();
          patchInfo.setPatchVersion(input.newApkBo.getVersionName());
          patchInfo.setTargetVersion(input.baseApkBo.getVersionName());
          patchInfo.setFileName(file.getName());
          Set<String> modifyBundles = new HashSet<>();
          ZipFile zipFile = newZipFile(file);
          Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
          while (enumeration.hasMoreElements()) {
              ZipEntry zipEntry = enumeration.nextElement();
              if (zipEntry.getName().startsWith("lib") && zipEntry.getName().indexOf("/") != -1) {
                  modifyBundles.add(zipEntry.getName().substring(3, zipEntry.getName().indexOf("/")).replace("_", "."));
              } else if (zipEntry.getName().endsWith(".so") && zipEntry.getName().indexOf("/") == -1) {
                  modifyBundles.add(
                      zipEntry.getName().substring(3, zipEntry.getName().lastIndexOf(".")).replace("_", "."));
              }
  
          }
  
          for (ArtifactBundleInfo artifactBundleInfo : input.artifactBundleInfos) {
              if (artifactBundleInfo.getMainBundle()) {
                  if (DiffType.MODIFY.equals(artifactBundleInfo.getDiffType()) || hasMainBundle) {
                      PatchBundleInfo patchBundleInfo = new PatchBundleInfo();
                      patchBundleInfo.setNewBundle(DiffType.ADD.equals(artifactBundleInfo.getDiffType()));
                      patchBundleInfo.setMainBundle(true);
                      patchBundleInfo.setVersion(artifactBundleInfo.getVersion());
                      patchBundleInfo.setName(((TpatchInput)input).mainBundleName);
                      patchBundleInfo.setSrcUnitTag(artifactBundleInfo.getSrcUnitTag());
                      patchBundleInfo.setUnitTag(artifactBundleInfo.getUnitTag());
                      patchBundleInfo.setPatchType(bundleTypes.get(((TpatchInput)input).mainBundleName) == null? 0:bundleTypes.get(((TpatchInput)input).mainBundleName));
                      patchBundleInfo.setApplicationName(artifactBundleInfo.getApplicationName());
                      patchBundleInfo.setArtifactId(artifactBundleInfo.getArtifactId());
                      patchBundleInfo.setPkgName(artifactBundleInfo.getPkgName());
                      patchBundleInfo.setDependency(artifactBundleInfo.getDependency());
                      patchBundleInfo.setBaseVersion(artifactBundleInfo.getBaseVersion());
                      patchInfo.getBundles().add(patchBundleInfo);
                      continue;
                  }
              } else if (DiffType.MODIFY.equals(artifactBundleInfo.getDiffType()) ||
                  DiffType.ADD.equals(artifactBundleInfo.getDiffType())) {
                  PatchBundleInfo patchBundleInfo = new PatchBundleInfo();
                  patchBundleInfo.setNewBundle(DiffType.ADD.equals(artifactBundleInfo.getDiffType()));
                  patchBundleInfo.setMainBundle(false);
                  patchBundleInfo.setSrcUnitTag(artifactBundleInfo.getSrcUnitTag());
                  patchBundleInfo.setUnitTag(artifactBundleInfo.getUnitTag());
                  patchBundleInfo.setVersion(artifactBundleInfo.getVersion());
                  patchBundleInfo.setPatchType(bundleTypes.get(artifactBundleInfo.getPkgName()) == null? 0:bundleTypes.get(artifactBundleInfo.getPkgName()));
                  patchBundleInfo.setName(artifactBundleInfo.getPkgName());
                  if (!modifyBundles.contains(artifactBundleInfo.getPkgName().replace("_","."))){
                      patchBundleInfo.setInherit(true);
                  }
                  patchBundleInfo.setApplicationName(artifactBundleInfo.getApplicationName());
                  patchBundleInfo.setArtifactId(artifactBundleInfo.getArtifactId());
                  patchBundleInfo.setPkgName(artifactBundleInfo.getPkgName());
                  patchBundleInfo.setDependency(artifactBundleInfo.getDependency());
                  patchBundleInfo.setBaseVersion(artifactBundleInfo.getBaseVersion());
                  patchInfo.getBundles().add(patchBundleInfo);
              } else if (modifyBundles.contains(artifactBundleInfo.getPkgName().replace("_","."))) {
                  PatchBundleInfo patchBundleInfo = new PatchBundleInfo();
                  patchBundleInfo.setNewBundle(false);
                  patchBundleInfo.setMainBundle(false);
                  patchBundleInfo.setPatchType(bundleTypes.get(artifactBundleInfo.getPkgName()) == null? 0:bundleTypes.get(artifactBundleInfo.getPkgName()));
                  patchBundleInfo.setSrcUnitTag(artifactBundleInfo.getSrcUnitTag());
                  patchBundleInfo.setUnitTag(artifactBundleInfo.getUnitTag());
                  patchBundleInfo.setVersion(artifactBundleInfo.getVersion());
                  patchBundleInfo.setName(artifactBundleInfo.getName());
                  patchBundleInfo.setApplicationName(artifactBundleInfo.getApplicationName());
                  patchBundleInfo.setArtifactId(artifactBundleInfo.getArtifactId());
                  patchBundleInfo.setPkgName(artifactBundleInfo.getPkgName());
                  patchBundleInfo.setDependency(artifactBundleInfo.getDependency());
                  patchBundleInfo.setBaseVersion(artifactBundleInfo.getBaseVersion());
                  patchInfo.getBundles().add(patchBundleInfo);
                  if (artifactBundleInfo.getUnitTag().equals(artifactBundleInfo.getSrcUnitTag())){
                      throw new RuntimeException(artifactBundleInfo.getPkgName()+"has contents change,but unitTag equals srcUnitTag"+artifactBundleInfo.getUnitTag()+",please upgrade bundle version and reintegration");
                  }
              }
          }
  
          try {
              zipFile.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
  
          return patchInfo;
      }
  
      private ZipFile newZipFile(File file) {
          try {
              return new ZipFile(file);
          } catch (IOException e) {
              e.printStackTrace();
          }
          return null;
      }
  
      /**
       * create increment patch
       * file
       */
      private BuildPatchInfos createIncrementPatchFiles(String productionName,
                                                        File curTPatchFile,
                                                        File targetDirectory,
                                                        File newApkUnzipFolder,
                                                        PatchInfo curPatchInfo,
                                                        String patchHistoryUrl) throws IOException, PatchException {
  
          BuildPatchInfos historyBuildPatchInfos = null;
          String response = null;
          if (!StringUtils.isEmpty(patchHistoryUrl)) {
              String patchHisUrl = patchHistoryUrl +
                  "?baseVersion=" +
                  input.baseApkBo.getVersionName() +
                  "&productIdentifier=" +
                  productionName;
              try {
              response = HttpClientUtils.getUrl(patchHisUrl);
              historyBuildPatchInfos = JSON.parseObject(response, BuildPatchInfos.class);
              }catch (Throwable e){
                  historyBuildPatchInfos = null;
              }
          } else {
              File[] files = hisTpatchFolder.listFiles(new FilenameFilter() {
                  @Override
                  public boolean accept(File dir, String filename) {
                      return filename.startsWith("patchs-") && filename.endsWith(".json");
                  }
              });
              if (files != null && files.length > 0) {
                  historyBuildPatchInfos = mergeHisPatchInfo(files);
              }
          }
          if (historyBuildPatchInfos == null) {
              return new BuildPatchInfos();
          }
          Iterator<PatchInfo> patchInfos = historyBuildPatchInfos.getPatches().iterator();
          while (patchInfos.hasNext()) {
              PatchInfo patchInfo = patchInfos.next();
              if (!patchInfo.getTargetVersion().equals(input.baseApkBo.getVersionName())) {
                  patchInfos.remove();
              }
          }
  
          Map<String, File> awbBundleMap = new HashMap<String, File>();
          for (ArtifactBundleInfo artifactBundleInfo : input.artifactBundleInfos) {
              String bundleFileSoName = "lib" +
                      artifactBundleInfo.getPkgName().replace('.', '_') +
                      ".so";
              File bundleFile = new File(newApkUnzipFolder,
                      "lib" +
                              "/" +
                              "armeabi" +
                              "/" +
                              bundleFileSoName);
              File assetsBundleFile = new File(newApkUnzipFolder,
                      "assets" +
                              "/" +
                              bundleFileSoName);
              if (bundleFile.exists()) {
                  awbBundleMap.put(artifactBundleInfo.getArtifactId(), bundleFile);
              }else if (assetsBundleFile.exists()){
                  awbBundleMap.put(artifactBundleInfo.getArtifactId(), assetsBundleFile);
  
              }
          }
          if (((TpatchInput)input).splitDiffBundle != null) {
              for (Pair<BundleBO, BundleBO> bundle : ((TpatchInput)input).splitDiffBundle) {
                  awbBundleMap.put(bundle.getSecond().getBundleName(), bundle.getSecond().getBundleFile());
  
              }
          }
  
  
          PatchFileBuilder patchFileBuilder = new PatchFileBuilder(historyBuildPatchInfos,
                                                                   curTPatchFile,
                                                                   curPatchInfo,
                                                                   awbBundleMap,
                                                                   targetDirectory,
                                                                   input.baseApkBo.getVersionName());
          patchFileBuilder.setNoPatchBundles(((TpatchInput) input).noPatchBundles);
          patchFileBuilder.setHistroyVersionList(((TpatchInput) input).versionList);
  
          return patchFileBuilder.createHistoryTPatches(input.diffBundleDex, logger);
      }
  
      private BuildPatchInfos mergeHisPatchInfo(File[] files) {
          BuildPatchInfos mergeBuildPatchInfo = new BuildPatchInfos();
          List<PatchInfo>patchInfos = new ArrayList<>();
          mergeBuildPatchInfo.setPatches(patchInfos);
          try {
              for (File localPatchInfo:files) {
                  String response = FileUtils.readFileToString(localPatchInfo);
                  BuildPatchInfos historyBuildPatchInfos = JSON.parseObject(response, BuildPatchInfos.class);
                  patchInfos.addAll(historyBuildPatchInfos.getPatches());
                  mergeBuildPatchInfo.setBaseVersion(historyBuildPatchInfos.getBaseVersion());
                  mergeBuildPatchInfo.setDiffBundleDex(true);
               }
          } catch (IOException e) {
                  e.printStackTrace();
              }
          return mergeBuildPatchInfo;
      }
  
      /**
       * create manifest for patch file
       *
       * @return
       */
      private Manifest createManifest() {
          Manifest manifest = new Manifest();
          Attributes main = manifest.getMainAttributes();
          main.putValue("Manifest-Version", "1.0");
          main.putValue("Created-By", "1.0 (DexPatch)");
          main.putValue("Created-Time", new Date(System.currentTimeMillis()).toGMTString());
          return manifest;
      }
  
      /**
       * judge if is file modified
       *
       * @param newFile  new File
       * @param baseFile
       * @return
       */
      public synchronized boolean isFileModify(File newFile, File baseFile) throws IOException {
          if (null == baseFile || !baseFile.exists()) {
              return true;
          }
  
          String newFileMd5 = MD5Util.getFileMD5String(newFile);
          String baseFileMd5 = MD5Util.getFileMD5String(baseFile);
          if (StringUtils.equals(newFileMd5, baseFileMd5)) {
              return false;
          } else {
              return true;
          }
      }
  
      /**
       * if a file is modify
       *
       * @param newFile
       * @param baseFile
       * @param bundleFileName
       * @param filePath
       * @return
       * @throws IOException
       */
      private synchronized boolean isFileModify(File newFile,
                                                File baseFile,
                                                String bundleFileName,
                                                String filePath) throws IOException {
          if (null == baseFile || !baseFile.exists()) {
              return true;
          }
  
          String newFileMd5 = MD5Util.getFileMD5String(newFile);
          String baseFileMd5 = MD5Util.getFileMD5String(baseFile);
          newFileMd5 = getBundleFileMappingMd5(getNewApkFileList(),
                                               bundleFileName,
                                               filePath,
                                               newFileMd5);
          baseFileMd5 = getBundleFileMappingMd5(getBaseApkFileList(),
                                                bundleFileName,
                                                filePath,
                                                baseFileMd5);
          if (StringUtils.equals(newFileMd5, baseFileMd5)) {
              return false;
          } else if (newFile.getName().equals(ANDROID_MANIFEST)) {
              return isManifestModify(baseFile, newFile);
  
          } else {
              return true;
          }
      }
  
      private boolean isManifestModify(File baseFile, File newFile) {
          AndroidManifestDiffFactory androidManifestDiffFactory = new AndroidManifestDiffFactory();
          try {
              androidManifestDiffFactory.diff(baseFile, newFile);
              for (AndroidManifestDiffFactory.DiffItem diffItem : androidManifestDiffFactory.diffResuit) {
                  if (diffItem.Component instanceof com.taobao.android.tpatch.manifest.Manifest.Activity ||
                      diffItem.Component instanceof com.taobao.android.tpatch.manifest.Manifest.Service) {
                      return true;
                  }
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
          return false;
      }
  
      /**
       * get file md5 from apkFileList
       *
       * @param apkFileList
       * @param bundleFileName,如果为null,则表示是主bundle
       * @param filePath
       * @param curMd5
       * @return
       */
      private String getBundleFileMappingMd5(ApkFileList apkFileList,
                                             String bundleFileName,
                                             String filePath,
                                             String curMd5) {
          if (null == apkFileList) {
              return curMd5;
          }
          String bundleName = null;
          if (null != bundleFileName) {
              bundleName = getBundleName(bundleFileName);
              if (null != bundleName) {
                  String mappingMd5 = apkFileList.getAwbFile(bundleName, filePath);
                  if (null != mappingMd5) {
                      return mappingMd5;
                  }
              }
          } else { // 主bundle
              String mappingMd5 = apkFileList.getMainBundle().get(filePath);
              if (null != mappingMd5) {
                  return mappingMd5;
              }
          }
          return curMd5;
      }
  
      /**
       * add files to jar
       *
       * @param jos
       * @param file
       */
      private void addFile(JarOutputStream jos, File file) throws IOException {
          byte[] buf = new byte[8064];
          String path = file.getName();
          InputStream in = null;
          try {
              in = new FileInputStream(file);
              ZipEntry fileEntry = new ZipEntry(path);
              jos.putNextEntry(fileEntry);
              // Transfer bytes from the file to the ZIP file
              int len;
              while ((len = in.read(buf)) > 0) {
                  jos.write(buf, 0, len);
              }
              // Complete the entry
              jos.closeEntry();
          } finally {
              IOUtils.closeQuietly(in);
          }
      }
  
      /**
       * Adds a directory to a {@link} with a directory prefix.
       *
       * @param jos       ZipArchiver to use to archive the file.
       * @param directory The directory to add.
       * @param prefix    An optional prefix for where in the Jar file the directory's contents should go.
       */
      protected void addDirectory(JarOutputStream jos,
                                  File directory,
                                  String prefix) throws IOException {
          if (directory != null && directory.exists()) {
              Collection<File> files = FileUtils.listFiles(directory,
                                                           TrueFileFilter.INSTANCE,
                                                           TrueFileFilter.INSTANCE);
              byte[] buf = new byte[8064];
              for (File file : files) {
                  if (file.isDirectory()) {
                      continue;
                  }
                  String path = prefix +
                      "/" +
                      PathUtils.toRelative(directory, file.getAbsolutePath());
                  InputStream in = null;
                  try {
                      in = new FileInputStream(file);
                      ZipEntry fileEntry = new ZipEntry(path);
                      jos.putNextEntry(fileEntry);
                      // Transfer bytes from the file to the ZIP file
                      int len;
                      while ((len = in.read(buf)) > 0) {
                          jos.write(buf, 0, len);
                      }
                      // Complete the entry
                      jos.closeEntry();
                      in.close();
                  } finally {
                      IOUtils.closeQuietly(in);
                  }
              }
          }
      }
  
  
  
  
  
  
      @Override
      public PatchFile doPatch() throws Exception {
          TpatchInput tpatchInput = (TpatchInput) input;
          TpatchFile tpatchFile = new TpatchFile();
          File hisPatchJsonFile = new File(tpatchInput.outPutJson.getParentFile(),"patchs-"+input.newApkBo.getVersionName()+".json");
          hisTpatchFolder = new File(tpatchInput.outPatchDir.getParentFile().getParentFile().getParentFile().getParentFile(),"hisTpatch");
           tpatchFile.diffJson = new File(((TpatchInput) input).outPatchDir, "diff.json");
           tpatchFile.patchInfo = new File(((TpatchInput) input).outPatchDir, "patchInfo.json");
          final File patchTmpDir = new File(((TpatchInput) input).outPatchDir, "tpatch-tmp");
          final File mainDiffFolder = new File(patchTmpDir, ((TpatchInput)input).mainBundleName);
          patchTmpDir.mkdirs();
          FileUtils.cleanDirectory(patchTmpDir);
          mainDiffFolder.mkdirs();
          File lastPatchFile = null;
          readWhiteList(((TpatchInput) input).bundleWhiteList);
          lastPatchFile = getLastPatchFile(input.baseApkBo.getVersionName(), ((TpatchInput) input).productName, ((TpatchInput) input).outPatchDir);
          PatchUtils.getTpatchClassDef(lastPatchFile, bundleClassMap);
          Profiler.release();
          Profiler.enter("unzip apks");
          // unzip apk
          File unzipFolder = unzipApk(((TpatchInput) input).outPatchDir);
          final File newApkUnzipFolder = new File(unzipFolder, NEW_APK_UNZIP_NAME);
          final File baseApkUnzipFolder = new File(unzipFolder, BASE_APK_UNZIP_NAME);
          Profiler.release();
          ExecutorServicesHelper executorServicesHelper = new ExecutorServicesHelper();
          String taskName = "diffBundleTask";
          //
  
          Collection<File> soFiles = FileUtils.listFiles(newApkUnzipFolder, new String[] {"so"}, true);
  
          //process remote bumdle
          if ((((TpatchInput) input).splitDiffBundle != null)) {
              for (final Pair<BundleBO, BundleBO> bundle : ((TpatchInput) input).splitDiffBundle) {
                  if (bundle.getFirst() == null || bundle.getSecond() == null){
                      logger.warning("remote bundle is not set to splitDiffBundles");
                      continue;
                  }
                  executorServicesHelper.submitTask(taskName, new Callable<Boolean>() {
                      @Override
                      public Boolean call() throws Exception {
                          TPatchTool.this.processBundleFiles(bundle.getSecond().getBundleFile(), bundle.getFirst().getBundleFile(), patchTmpDir);
                          return true;
                      }
                  });
              }
          }
  
  
          Profiler.enter("awbspatch");
  
          Collection<File> retainFiles = FileUtils.listFiles(newApkUnzipFolder, new IOFileFilter() {
  
  
              @Override
              public boolean accept(File file) {
                  String relativePath = PathUtils.toRelative(newApkUnzipFolder, file.getAbsolutePath());
                  if (pathMatcher.match(DEFAULT_NOT_INCLUDE_RESOURCES, relativePath)) {
                      return false;
                  }
                  if (null != ((TpatchInput)(input)).notIncludeFiles && pathMatcher
                      .match(((TpatchInput)(input)).notIncludeFiles, relativePath)) {
                      return false;
                  }
                  return true;
              }
  
              @Override
              public boolean accept(File file, String s) {
                  return accept(new File(file, s));
              }
          }, TrueFileFilter.INSTANCE);
  
  
          executorServicesHelper.submitTask(taskName, new Callable<Boolean>() {
              @Override
              public Boolean call() throws Exception {
                  // 得到主bundle的dex diff文件
  //                File mianDiffDestDex = new File(mainDiffFolder, DEX_NAME);
  //                File tmpDexFolder = new File(patchTmpDir, ((TpatchInput)input).mainBundleName + "-dex");
                  createBundleDexPatch(newApkUnzipFolder,
                          baseApkUnzipFolder,
                          mainDiffFolder,
  //                        tmpDexFolder,
                          true);
  
                  // 是否保留主bundle的资源文件
                  if (isRetainMainBundleRes()) {
                      copyMainBundleResources(newApkUnzipFolder,
                              baseApkUnzipFolder,
                      new File(patchTmpDir, ((TpatchInput)input).mainBundleName),
                      retainFiles);
                  }
                  return true;
              }
  
          }
          );
  
  
          for (final File soFile : soFiles) {
              System.out.println("do patch:" + soFile.getAbsolutePath());
              final String relativePath = PathUtils.toRelative(newApkUnzipFolder,
                      soFile.getAbsolutePath());
              if (null != ((TpatchInput) input).notIncludeFiles && pathMatcher.match(((TpatchInput)input).notIncludeFiles, relativePath)) {
                  continue;
              }
              executorServicesHelper.submitTask(taskName, new Callable<Boolean>() {
  
                  @Override
                  public Boolean call() throws Exception {
                      File destFile = new File(patchTmpDir, ((TpatchInput)input).mainBundleName + "/" +
                              relativePath);
                      File baseSoFile = new File(baseApkUnzipFolder, relativePath);
                      if (isBundleFile(soFile)) {
                          processBundleFiles(soFile, baseSoFile, patchTmpDir);
  
                      } else if (isFileModify(soFile, baseSoFile)) {
                          if (destFile.exists()){
                              FileUtils.deleteQuietly(destFile);
                          }
                          if (!baseSoFile.exists()||!((TpatchInput) input).diffNativeSo) {
                              //新增
                              FileUtils.copyFile(soFile, destFile);
                          }else {
  
                              destFile = new File(destFile.getParentFile(),destFile.getName()+".patch");
                              SoDiffUtils.diffSo(patchTmpDir,baseSoFile,soFile,destFile);
                              soFileDefs.add(new SoFileDef(baseSoFile,soFile,destFile,relativePath));
  
                          }
                      }
  
                      return true;
                  }
              });
          }
  
          executorServicesHelper.waitTaskCompleted(taskName);
          executorServicesHelper.stop();
          Profiler.release();
  
          Profiler.enter("ziptpatchfile");
          // zip file
          File patchFile = createTPatchFile(((TpatchInput) input).outPatchDir, patchTmpDir);
          tpatchFile.patchFile = patchFile;
          PatchInfo curPatchInfo = createBasePatchInfo(patchFile);
  
          Profiler.release();
  
          Profiler.enter("createhistpatch");
          BuildPatchInfos buildPatchInfos = createIncrementPatchFiles(((TpatchInput) input).productName,
                  patchFile,
                  ((TpatchInput) input).outPatchDir,
                  newApkUnzipFolder,
                  curPatchInfo,
                  ((TpatchInput) input).hisPatchUrl);
          Profiler.release();
  
          Profiler.enter("writejson");
          buildPatchInfos.getPatches().add(curPatchInfo);
          buildPatchInfos.setBaseVersion(input.baseApkBo.getVersionName());
          buildPatchInfos.setDiffBundleDex(input.diffBundleDex);
  
              FileUtils.writeStringToFile(((TpatchInput) input).outPutJson, JSON.toJSONString(buildPatchInfos));
              BuildPatchInfos testForBuildPatchInfos = new BuildPatchInfos();
              testForBuildPatchInfos.setBaseVersion(buildPatchInfos.getBaseVersion());
              List<PatchInfo>patchInfos = new ArrayList<>();
              testForBuildPatchInfos.setPatches(patchInfos);
              testForBuildPatchInfos.setDiffBundleDex(buildPatchInfos.isDiffBundleDex());
              for(PatchInfo patchInfo:buildPatchInfos.getPatches()){
                  if (patchInfo.getTargetVersion().equals(buildPatchInfos.getBaseVersion())){
                      patchInfos.add(patchInfo);
                  }
              }
              FileUtils.writeStringToFile(hisPatchJsonFile, JSON.toJSONString(testForBuildPatchInfos));
              tpatchFile.updateJsons = new ArrayList<File>();
              Map<String,List<String>>map = new HashMap<>();
          for (PatchInfo patchInfo : buildPatchInfos.getPatches()) {
              UpdateInfo updateInfo = new UpdateInfo(patchInfo, buildPatchInfos.getBaseVersion());
  //            System.out.println("start to check:"+patchInfo.getTargetVersion()+"......");
  //            List<PatchChecker.ReasonMsg> msgs = new PatchChecker(updateInfo,bundleInfos.get(patchInfo.getTargetVersion()),new File(((TpatchInput) input).outPatchDir,patchInfo.getFileName())).check();
  //            map.put(patchInfo.getFileName(),msgToString(msgs));
              File updateJson = new File(((TpatchInput) input).outPatchDir, "update-" + patchInfo.getTargetVersion() + ".json");
              FileUtils.writeStringToFile(updateJson, JSON.toJSONString(updateInfo, true));
              tpatchFile.updateJsons.add(updateJson);
          }
  //        tpatchFile.patchChecker = new File(((TpatchInput) input).outPatchDir,"patch-check.json");
  //        FileUtils.writeStringToFile(tpatchFile.patchChecker, JSON.toJSONString(map, true));
          // 删除临时的目录
          FileUtils.deleteDirectory(patchTmpDir);
          apkDiff.setBaseApkVersion(input.baseApkBo.getVersionName());
          apkDiff.setNewApkVersion(input.newApkBo.getVersionName());
          apkDiff.setBundleDiffResults(bundleDiffResults);
          boolean newApkFileExist = input.newApkBo.getApkFile().exists() && input.newApkBo.getApkFile().isFile();
          if (newApkFileExist) {
              apkDiff.setNewApkMd5(MD5Util.getFileMD5String(input.newApkBo.getApkFile()));
          }
          apkDiff.setFileName(input.newApkBo.getApkName());
          apkPatchInfos.setBaseApkVersion(input.baseApkBo.getVersionName());
          apkPatchInfos.setNewApkVersion(input.newApkBo.getVersionName());
          apkPatchInfos.setBundleDiffResults(diffPatchInfos);
          apkPatchInfos.setFileName(patchFile.getName());
          apkPatchInfos.setNewApkMd5(MD5Util.getFileMD5String(patchFile));
          FileUtils.writeStringToFile(tpatchFile.diffJson, JSON.toJSONString(apkDiff));
          FileUtils.writeStringToFile(tpatchFile.patchInfo, JSON.toJSONString(apkPatchInfos));
          FileUtils.copyFileToDirectory(tpatchFile.diffJson, ((TpatchInput) input).outPatchDir.getParentFile(), true);
          if (newApkFileExist) {
              FileUtils.copyFileToDirectory(input.newApkBo.getApkFile(), ((TpatchInput) input).outPatchDir.getParentFile(), true);
          }
          Profiler.release();
          logger.warning(Profiler.dump());
          return tpatchFile;
      }
  
      @Override
      public boolean isRetainMainBundleRes() {
          return true;
      }
  
      protected File getLastPatchFile(String baseApkVersion,
                                      String productName,
                                      File outPatchDir) throws IOException {
          try {
              String httpUrl = ((TpatchInput)input).LAST_PATCH_URL +
                      "baseVersion=" +
                      baseApkVersion +
                      "&productIdentifier=" +
                      productName;
              String response = HttpClientUtils.getUrl(httpUrl);
              if (StringUtils.isBlank(response) ||
                      response.equals("\"\"")) {
                  return null;
              }
              File downLoadFolder = new File(outPatchDir, "LastPatch");
              downLoadFolder.mkdirs();
              File downLoadFile = new File(downLoadFolder, "lastpatch.tpatch");
              String downLoadUrl = StringEscapeUtils.unescapeJava(response);
              downloadTPath(downLoadUrl.substring(1, downLoadUrl.length() - 1), downLoadFile);
  
              return downLoadFile;
          } catch (Exception e) {
              return null;
          }
      }
  
      public static void main(String[]args) throws IOException {
  
  
  //        TPatchTool.class.getProtectionDomain().getCodeSource().getLocation();
  //        File workingDir = new File("/Users/lilong/Downloads/patch-7.7.4.67686@7.7.3");
  //        File oldFile = new File("/Users/lilong/Documents/main_builder/aa/lib/armeabi/libalinnkit-v7a.so");
  //        File newFile = new File("/Users/lilong/Downloads/patch-7.7.4.67686@7.7.3/libcom_taobao_maindex/lib/armeabi/libalinnkit-v7a.so");
  //        File patchFile = new File("/Users/lilong/Downloads/libalinnkit-v7a.so.patch");
  //        File new2File = new File("/Users/lilong/Downloads/libalinnkit-v7a.so");
  //        File bsDiffFile = new File(TPatchTool.class.getClassLoader().getResource("mac/bsdiff").getFile());
  //        File bsPatchFile = new File(TPatchTool.class.getClassLoader().getResource("mac/bspatch").getFile());
  //
  //        CommandUtils.exec(workingDir,bsDiffFile.getAbsolutePath()+ " "+oldFile.getAbsolutePath() + " "+newFile.getAbsolutePath()+" "+patchFile.getAbsolutePath());
  //        CommandUtils.exec(workingDir,bsPatchFile.getAbsolutePath()+" "+oldFile.getAbsolutePath() + " "+new2File.getAbsolutePath()+" "+patchFile.getAbsolutePath());
  //
  //        assert MD5Util.getFileMD5String(new2File).equals(MD5Util.getFileMD5String(newFile));
      }
  
  }
  