content: /*
   *
   *                                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.
   *
   *
   *
   */
  
  package android.taobao.atlas.startup.patch;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.Closeable;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.lang.reflect.Array;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.util.Arrays;
  import java.util.List;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipFile;
  
  import android.app.Application;
  import android.content.Context;
  import android.content.SharedPreferences;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageManager;
  import android.os.Build;
  import android.taobao.atlas.startup.KernalVersionManager;
  import android.taobao.atlas.startup.NClassLoader;
  import android.taobao.atlas.startup.patch.releaser.PatchDexProfile;
  import android.text.TextUtils;
  import android.util.Log;
  import android.widget.Toast;
  import dalvik.system.DexFile;
  import dalvik.system.PathClassLoader;
  
  /**
   * Created by guanjie on 15/6/4.
   */
  public class KernalBundle {
      /**
       * the storage location.
       */
      File bundleDir;
  
      /**
       * the bundle archive file.
       */
      KernalBundleArchive archive;
      private Class FrameworkPropertiesClazz;
      private NClassLoader replaceClassLoader;
  
      public static boolean nativeLibPatched;
  
      static List <String>blackList = Arrays.asList(
              "/Plugin/",
              "/virtual/");
  
      public static String KERNAL_BUNDLE_NAME = "com.taobao.maindex";
  
      public static KernalBundle kernalBundle = null;
      public static boolean patchWithApk = false;
  
      public static boolean checkloadKernalBundle(Application application, String currentProcessName) {
          File updateDir = null;
          File dexPatchDir = null;
          dexPatchDir = updateDir = new File(KernalConstants.baseContext.getFilesDir(), "storage");
  
          if (!TextUtils.isEmpty(KernalVersionManager.instance().DEXPATCH_STORAGE_LOCATION)) {
              dexPatchDir = new File(KernalVersionManager.instance().DEXPATCH_STORAGE_LOCATION);
          }
  
          if (!TextUtils.isEmpty(KernalVersionManager.instance().CURRENT_STORAGE_LOCATION)) {
              updateDir = new File(KernalVersionManager.instance().CURRENT_STORAGE_LOCATION);
          }
  
          final File kernalUpdateDir = new File(updateDir, KERNAL_BUNDLE_NAME);
          final File kernalDexPatchDir = new File(dexPatchDir, KERNAL_BUNDLE_NAME);
  
          if (kernalUpdateDir.exists() || kernalDexPatchDir.exists()) {
              try {
                  kernalBundle = new KernalBundle(kernalUpdateDir, kernalDexPatchDir, KernalVersionManager.instance().getBaseBundleVersion(KERNAL_BUNDLE_NAME), currentProcessName);
                  if (isInBlackList(kernalBundle)){
                      throw new IOException(kernalBundle.getArchive().getArchiveFile().getAbsolutePath());
                  }
                  kernalBundle.patchKernalDex(application);
  //                kernalBundle.patchKernalResource(application);
  
                  return true;
              } catch (Throwable e) {
                  e.printStackTrace();
                  kernalBundle = null;
                  deleteDirectory(kernalUpdateDir);
                  if (kernalDexPatchDir.exists()) {
                      deleteDirectory(kernalDexPatchDir);
                  }
                  return false;
              }
          }
          return false;
      }
  
      private static boolean isInBlackList(KernalBundle kernalBundle) {
          if (kernalBundle!= null && kernalBundle.getArchive()!= null && kernalBundle.getArchive().getArchiveFile()!= null){
              for (String s:blackList) {
                  if (kernalBundle.getArchive().getArchiveFile().getAbsolutePath().contains(s)){
                      return true;
                  }
              }
          }
          return false;
  
      }
  
      public static boolean checkLoadKernalDebugPatch(Application application) {
          if (Build.VERSION.SDK_INT < 21) {
              //暂时只支持art设备的debug调试
              return false;
          }
  
          boolean loadKernalPatch = false;
          try {
              ApplicationInfo app_info = application.getApplicationInfo();
              boolean debug = (app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
              if (debug) {
                  File debugBundleDir = new File(KernalConstants.baseContext.getExternalFilesDir("debug_storage"), KERNAL_BUNDLE_NAME);
                  File patchFile = new File(debugBundleDir, "patch.zip");
                  if (patchFile.exists()) {
                      loadKernalPatch = true;
                      KernalBundle bundle = new KernalBundle();
                      //DexFile dexFile = (DexFile) KernalConstants.dexBooster.loadDex(KernalConstants.baseContext, KernalConstants.baseContext.getApplicationInfo().sourceDir,
                      //        new File(patchFile.getParent(), "base.dex").getAbsolutePath(), 0, true);
  
                      File internalDebugBundleDir = new File(new File(application.getFilesDir(), "debug_storage"), KERNAL_BUNDLE_NAME);
                      internalDebugBundleDir.mkdirs();
                      DexFile patchDexFile = (DexFile) DexFile.loadDex(patchFile.getAbsolutePath(),new File(internalDebugBundleDir, "patch.dex").getAbsolutePath(), 0);
                      if (bundle.needReplaceClassLoader(application)) {
                          NClassLoader loader = new NClassLoader(".", KernalBundle.class.getClassLoader().getParent());
                          try {
                              NClassLoader.replacePathClassLoader(KernalConstants.baseContext, KernalBundle.class.getClassLoader(), loader);
                          } catch (Exception e) {
                              throw new RuntimeException(e);
                          }
                      }
                      bundle.installKernalBundle(KernalConstants.baseContext.getClassLoader(), patchFile, new DexFile[]{patchDexFile/*, dexFile*/}, null,
                          true /*(app_info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0*/);
                      bundle.prepareRuntimeVariables(application);
                      Class DelegateResourcesClazz = application.getClassLoader().loadClass("android.taobao.atlas.runtime.DelegateResources");
                      DelegateResourcesClazz.getDeclaredMethod("addApkpatchResources", String.class)
                              .invoke(DelegateResourcesClazz, patchFile.getAbsolutePath());
                      Toast.makeText(KernalConstants.baseContext, "当前处于DEBUG调试状态，不支持动态更新，清除数据可恢复", Toast.LENGTH_LONG).show();
                  }
              }
          } finally {
              return loadKernalPatch;
          }
      }
  
      public static boolean hasKernalPatch() {
          return KernalVersionManager.instance().isUpdated(KERNAL_BUNDLE_NAME) || KernalVersionManager.instance().isDexPatched(KERNAL_BUNDLE_NAME);
      }
  
  
      public static boolean hasNativeLibPatch(Context base) {
          try {
              return new File(base.getFilesDir(), String.format("nativeLib-%s", base.getPackageManager().getPackageInfo(base.getPackageName(), 0).versionName)).exists()
                      && new File(new File(base.getFilesDir(), String.format("nativeLib-%s", base.getPackageManager().getPackageInfo(base.getPackageName(), 0).versionName)), "mark").exists();
          } catch (PackageManager.NameNotFoundException e) {
              e.printStackTrace();
          }
          return false;
      }
  
      public static void clear() {
          File storageDir = new File(KernalConstants.baseContext.getFilesDir(), "storage");
          final File kernalDir = new File(storageDir, KERNAL_BUNDLE_NAME);
          if (kernalDir.exists()) {
              deleteDirectory(kernalDir);
          }
      }
  
      //DEBUG
      private KernalBundle() {
      }
  
      //create
      public KernalBundle(final File bundleDir,
                          final File file, String version, long dexPatchVersion) throws Exception {
          this.bundleDir = bundleDir;
          try {
              archive = new KernalBundleArchive(bundleDir, file, version, dexPatchVersion);
          } catch (IOException e) {
              e.printStackTrace();
              if (bundleDir.exists()) {
                  deleteDirectory(bundleDir);
              }
              throw new IOException("install kernal Bundlele fail ", e);
          }
      }
  
      //reload
      public KernalBundle(final File updateDir, final File dexPatchDir, String version, String process) throws Exception {
          long dexPatchVersion = KernalVersionManager.instance().getDexPatchBundleVersion(KERNAL_BUNDLE_NAME);
          if (dexPatchVersion > 0) {
              try {
                  bundleDir = dexPatchDir;
                  archive = new KernalBundleArchive(KernalConstants.baseContext, dexPatchDir, "", dexPatchVersion, process);
              } catch (Throwable e) {
                  bundleDir = updateDir;
                  archive = new KernalBundleArchive(KernalConstants.baseContext, bundleDir, makeMainDexUniqueTag(KernalVersionManager.instance().currentVersionName(), version), dexPatchVersion, process);
              }
          } else {
              bundleDir = updateDir;
              archive = new KernalBundleArchive(KernalConstants.baseContext, bundleDir, makeMainDexUniqueTag(KernalVersionManager.instance().currentVersionName(), version), dexPatchVersion, process);
          }
      }
  
      public static void patchNativeLib(Context base) {
          try {
              File dir = new File(base.getFilesDir(), String.format("nativeLib-%s", base.getPackageManager().getPackageInfo(base.getPackageName(), 0).versionName));
              ClassLoader loader = KernalBundle.class.getClassLoader();
              Field pathListField = findField(loader, "pathList");
              Object dexPathList = pathListField.get(loader);
              patchLibrary(dexPathList, dir);
              nativeLibPatched = true;
          } catch (Throwable e) {
              e.printStackTrace();
          }
  
  
      }
  
      private String makeMainDexUniqueTag(String appVersion, String maindexTag) {
          if (maindexTag.startsWith(appVersion)) {
              return maindexTag;
          }
          return appVersion + "_" + maindexTag;
      }
  
      public void  patchKernalDex(Application application) throws Exception {
          DexFile[] dexFile = archive.getOdexFile();
          if ((dexFile != null && dexFile.length > 0) || archive.getLibraryDirectory().exists()) {
              boolean needReplaceClassLoader = needReplaceClassLoader(application);
              boolean dexPatch = dexFile[dexFile.length - 1].getName().contains(KernalBundleArchive.DEXPATCH_DIR);
              int newFrameworkPropertiesDexIndex;
              if (dexPatch) {
                      newFrameworkPropertiesDexIndex = 0;
              } else {
                  newFrameworkPropertiesDexIndex = dexFile.length - 1;
              }
              //临时处理方案，需要替换为dexopt时传入classsLoader以兼容8.0
              patchWithApk = dexPatch &&
                      (TextUtils.isEmpty(KernalVersionManager.instance().currentVersionName()) || KernalVersionManager.instance().currentVersionName().equals(KernalConstants.INSTALLED_VERSIONNAME));
              if (!needReplaceClassLoader) {
                  Log.e("KernalBundle","no need replace Classloader!");
                  FrameworkPropertiesClazz = archive.getOdexFile()[newFrameworkPropertiesDexIndex].loadClass("android.taobao.atlas.framework.FrameworkProperties", application.getClassLoader());
              } else if (patchWithApk) {
                  Log.e("KernalBundle","need replace Classloader && patchWithApk!");
                  replaceClassLoader = new NClassLoader(".", KernalBundle.class.getClassLoader().getParent());
                  if (needReplaceClassLoader) {
                      try {
                          NClassLoader.replacePathClassLoader(KernalConstants.baseContext, KernalBundle.class.getClassLoader(), replaceClassLoader);
                      } catch (Exception e) {
                          throw new RuntimeException(e);
                      }
                  }
                  FrameworkPropertiesClazz = archive.getOdexFile()[newFrameworkPropertiesDexIndex].loadClass("android.taobao.atlas.framework.FrameworkProperties", replaceClassLoader);
  
              } else {
                  Log.e("KernalBundle","need replace Classloader && load FrameworkPropertiesClazz to NclassLoader!");
                  replaceClassLoader = new NClassLoader(".", KernalBundle.class.getClassLoader().getParent());
                  if (needReplaceClassLoader) {
                      try {
                          NClassLoader.replacePathClassLoader(KernalConstants.baseContext, KernalBundle.class.getClassLoader(), replaceClassLoader);
                      } catch (Exception e) {
                          throw new RuntimeException(e);
                      }
                  }
                  FrameworkPropertiesClazz = archive.getOdexFile()[newFrameworkPropertiesDexIndex].loadClass("android.taobao.atlas.framework.FrameworkProperties", replaceClassLoader);
              }
  
  
              //for mini sdk 21
              if (FrameworkPropertiesClazz == null){
                  Log.e("KernalBundle","FrameworkPropertiesClazz is null, load from source apk");
                  FrameworkPropertiesClazz = Class.forName("android.taobao.atlas.framework.FrameworkProperties");
              }
  
              if (FrameworkPropertiesClazz == null && isDeubgMode()) {
                  Log.e("KernalBundle", "main dex is not match, library awo test?");
                  return;
              }
              Field versionField = FrameworkPropertiesClazz.getDeclaredField("version");
              versionField.setAccessible(true);
              String version = (String) versionField.get(FrameworkPropertiesClazz.newInstance());
              if (!KernalVersionManager.instance().currentVersionName().equals(version)) {
                  if (isDeubgMode()) {
                      Log.e("KernalBundle", "main dex is not match, awo test?");
                  } else {
                      throw new RuntimeException(String.format("maindex version is not mismatch %s vs %s!",KernalVersionManager.instance().currentVersionName(),version));
                  }
              }
  
              installKernalBundle(KernalConstants.baseContext.getClassLoader(), archive.getArchiveFile(), archive.getOdexFile(), archive.getLibraryDirectory());
  
              prepareRuntimeVariables(application);
  
              prepareKernalConstant(application);
          }
      }
  
      private void prepareKernalConstant(Application application) {
          try {
              Class kernalConstantClass = application.getClassLoader().loadClass("android.taobao.atlas.startup.patch.KernalConstants");
              kernalConstantClass.getDeclaredField("APK_PATH").set(null,application.getApplicationInfo().sourceDir);
              kernalConstantClass.getDeclaredField("baseContext").set(null,application.getBaseContext());
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
  
      }
  
      public boolean needReplaceClassLoader(Application application) {
          if (Build.VERSION.SDK_INT >= 24) {
              ClassLoader loader = getClass().getClassLoader();
              boolean needReplace = false;
              do {
                  if (loader.getClass().getName().equals(PathClassLoader.class.getName())) {
                      needReplace = true;
                      break;
                  }
              } while ((loader = loader.getParent()) != null);
              return needReplace;
          } else {
              return false;
          }
      }
  
      public void prepareRuntimeVariables(Application application) {
          try {
              Class RuntimeVariablesClass = application.getClassLoader().loadClass("android.taobao.atlas.runtime.RuntimeVariables");
              RuntimeVariablesClass.getDeclaredField("sRawClassLoader").set(RuntimeVariablesClass, KernalBundle.class.getClassLoader());
              if (FrameworkPropertiesClazz != null) {
                  RuntimeVariablesClass.getDeclaredField("FrameworkPropertiesClazz").set(RuntimeVariablesClass, FrameworkPropertiesClazz);
              } else if (!isDeubgMode()) {
                  throw new RuntimeException("FrameworkPropertiesClazz find error,will be rollback!");
              }
              RuntimeVariablesClass.getDeclaredField("sCurrentProcessName").set(RuntimeVariablesClass, KernalConstants.PROCESS);
              RuntimeVariablesClass.getDeclaredField("androidApplication").set(RuntimeVariablesClass, application);
              RuntimeVariablesClass.getDeclaredField("delegateResources").set(RuntimeVariablesClass, KernalConstants.baseContext.getResources());
  
          } catch (Throwable e) {
              throw new RuntimeException(e);
          }
      }
  
      /**
       * 主dex动态资源部署
       * See {@link android.taobao.atlas.runtime.DelegateResources#addApkpatchResources}
       *
       * @param application
       * @throws Exception
       */
      public void patchKernalResource(Application application) throws Exception {
          if (!patchWithApk) {
              Class DelegateResourcesClazz = application.getClassLoader().loadClass("android.taobao.atlas.runtime.DelegateResources");
              DelegateResourcesClazz.getDeclaredMethod("addApkpatchResources", String.class)
                      .invoke(DelegateResourcesClazz, archive.getArchiveFile().getAbsolutePath());
          }
      }
  
      public int getState() {
          return 0;
      }
  
      /**
       * 安装so库
       */
      private static void extract(String zipPath, String entryPath, File targetPath) {
          ZipFile zip = null;
          try {
              zip = new ZipFile(new File(zipPath));
              ZipEntry entry = zip.getEntry(entryPath);
              BufferedOutputStream bos = new BufferedOutputStream(
                      new FileOutputStream(targetPath));
              BufferedInputStream bi = new BufferedInputStream(zip.getInputStream(entry));
              byte[] readContent = new byte[600];
              int readCount = bi.read(readContent);
              while (readCount != -1) {
                  bos.write(readContent, 0, readCount);
                  readCount = bi.read(readContent);
              }
              bos.close();
          } catch (final Exception e) {
              e.printStackTrace();
          } finally {
              try {
                  if (zip != null) {
                      zip.close();
                  }
              } catch (Throwable e) {
              }
          }
      }
  
      /**
       * Locates a given field anywhere in the class inheritance hierarchy.
       *
       * @param instance an object to search the field into.
       * @param name     field name
       * @return a field object
       * @throws NoSuchFieldException if the field cannot be located
       */
      private static Field findField(Object instance, String name) throws NoSuchFieldException {
          for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
              try {
                  Field field = clazz.getDeclaredField(name);
  
  
                  if (!field.isAccessible()) {
                      field.setAccessible(true);
                  }
  
                  return field;
              } catch (NoSuchFieldException e) {
                  // ignore and search next
              }
          }
  
          throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
      }
  
  
      /**
       * Replace the value of a field containing a non null array, by a new array containing the
       * elements of the original array plus the elements of extraElements.
       *
       * @param instance     the instance whose field is to be modified.
       * @param fieldName    the field to modify.
       * @param extraElement element to append at the end of the array.
       */
      private static void expandFieldArray(Object instance, String fieldName,
                                           Object[] extraElement) throws NoSuchFieldException, IllegalArgumentException,
              IllegalAccessException {
          Field jlrField = findField(instance, fieldName);
          Object[] original = (Object[]) jlrField.get(instance);
          Object[] combined = (Object[]) Array.newInstance(
                  original.getClass().getComponentType(), original.length + extraElement.length);
          for (int i = 0; i < extraElement.length; i++) {
              combined[i] = extraElement[i];
          }
          System.arraycopy(original, 0, combined, extraElement.length, original.length);
          jlrField.set(instance, combined);
      }
  
      private static void expandFieldList(Object instance, String fieldName,
                                          Object extraElement) throws NoSuchFieldException, IllegalArgumentException,
              IllegalAccessException {
          Field jlrField = findField(instance, fieldName);
          List original = (List) jlrField.get(instance);
          original.add(0, extraElement);
      }
  
      private static boolean replaceElement(Object instance, String fieldName, Object[] replaceElement) throws NoSuchFieldException, IllegalArgumentException,
              IllegalAccessException {
          boolean replaceSuccess = false;
          Field jlrField = findField(instance, fieldName);
          Object[] original = (Object[]) jlrField.get(instance);
          for (int x = 0; x < original.length; x++) {
              Object element = original[x];
              File apkFile = findDexRawFile(element);
              if (apkFile != null) {
                  if (apkFile.getAbsolutePath() != null && apkFile.getAbsolutePath().contains(KernalConstants.baseContext.getPackageName())) {
                      original[x] = replaceElement;
                      replaceSuccess = true;
                      break;
                  }
              }
          }
          return replaceSuccess;
  
      }
  
      private static File findDexRawFile(Object element) {
          Field field = null;
          File dexRawFile = null;
          if (Build.VERSION.SDK_INT >= 25) {
              try {
                  field = element.getClass().getDeclaredField("path");
                  field.setAccessible(true);
                  dexRawFile = (File) field.get(element);
                  return dexRawFile;
              } catch (Throwable e) {
              }
          }
          try {
              field = element.getClass().getDeclaredField("file");
              field.setAccessible(true);
              dexRawFile = (File) field.get(element);
          } catch (Throwable e) {
          }
  
          if (field == null) {
              try {
                  field = element.getClass().getDeclaredField("zip");
                  field.setAccessible(true);
                  dexRawFile = (File) field.get(element);
              } catch (Throwable e) {
              }
          }
          return dexRawFile;
      }
  
      public boolean installKernalBundle(ClassLoader updateClassLoader, File archiveFile, DexFile[] odexFiles, File libraryDirectory) throws IOException, NoSuchFieldException, IllegalAccessException {
          return installKernalBundle(updateClassLoader, archiveFile, odexFiles, libraryDirectory, false);
      }
  
  
      public boolean installKernalBundle(ClassLoader updateClassLoader, File archiveFile, DexFile[] odexFiles, File libraryDirectory, boolean vmSafeMode) throws IOException, NoSuchFieldException, IllegalAccessException {
          Object[] element = null;
          boolean success = false;
          try {
              ClassLoader loader = updateClassLoader;
              Field pathListField = findField(loader, "pathList");
              Object dexPathList = pathListField.get(loader);
              if (odexFiles != null) {
                  element = makeDexElement(archiveFile, odexFiles);
                  if (element == null || element.length == 0) {
                      throw new IOException("makeDexElement failed");
                  }
  
                  //增加kernal bundle
                 expandFieldArray(dexPathList, "dexElements", element);
  //                if (Build.VERSION.SDK_INT > 20 && !vmSafeMode) {
  //                     success = removeOrignalElement(dexPathList, "dexElements");
  //                    if(!success){
  //                        throw new IOException("replaceElement failed");
  //                    }
  //                } else {
  ////                    expandFieldArray(dexPathList, "dexElements", element);
  //                }
              }
              //增加kernal bundle library
  
              patchLibrary(dexPathList, libraryDirectory);
  
              return true;
          } catch (Exception e) {
              throw new IOException("install kernal fail", e);
          }
  
      }
  
      private boolean removeOrignalElement(Object instance, String fieldName) throws NoSuchFieldException, IllegalAccessException {
          Field jlrField = findField(instance, fieldName);
          Object[] original = (Object[]) jlrField.get(instance);
          for (int x = 0; x < original.length; x++) {
              Object element = original[x];
              Field dexFileField = element.getClass().getDeclaredField("dexFile");
              dexFileField.setAccessible(true);
              File apkFile = findDexRawFile(element);
              if (apkFile != null) {
                  if (apkFile.getAbsolutePath() != null && apkFile.getAbsolutePath().contains(KernalConstants.baseContext.getPackageName())) {
                      dexFileField.set(element,null);
                      break;
                  }
              }
          }
          return true;
      }
  
      private static void patchLibrary(Object dexPathList, File libraryDirectory) throws NoSuchFieldException, IllegalAccessException, IOException {
          if (libraryDirectory != null && !TextUtils.isEmpty(libraryDirectory.getAbsolutePath()) && libraryDirectory.exists()) {
              if (Build.VERSION.SDK_INT < 23) {
                  expandFieldArray(dexPathList, "nativeLibraryDirectories", new Object[]{libraryDirectory});
              } else {
                  expandFieldList(dexPathList, "nativeLibraryDirectories", libraryDirectory);
              }
  
              if (Build.VERSION.SDK_INT >= 23) {
                  Object nativeLibraryElement = makeNativeLibraryElement(libraryDirectory);
                  expandFieldArray(dexPathList, "nativeLibraryPathElements", new Object[]{nativeLibraryElement});
              }
          }
      }
  
      private void quietClose(Closeable closeable) {
          try {
              if (closeable != null) {
                  closeable.close();
              }
          } catch (Throwable e) {
  
          }
      }
  
  
      static Class[] constructorArgs3 = {File.class, ZipFile.class, DexFile.class};
      static Class[] constructorArgs2 = {File.class, File.class, DexFile.class};
      static Class[] constructorArgs1 = {File.class, boolean.class, File.class, DexFile.class};
  
      static Class[] constructorArgsO = {DexFile.class, File.class};
  
  
      public static Object[] makeDexElement(File file, DexFile[] dex) throws Exception {
          Object[] objects = new Object[dex.length];
          for (int i = 0; i < dex.length; i++) {
              try {
                  Class Element = Class.forName("dalvik.system.DexPathList$Element");
                  if (Build.VERSION.SDK_INT > 25 || Build.VERSION.SDK_INT == 25 && Build.VERSION.PREVIEW_SDK_INT > 0) {
                      Constructor cons = getElementConstructor(Element, constructorArgsO);
                      objects[i] = cons.newInstance(dex[i], file);
                  } else {
                      File apkFile = new File(KernalConstants.baseContext.getApplicationInfo().sourceDir);
                      Constructor cons = getElementConstructor(Element, constructorArgs1);
                      if (cons != null) {
                          objects[i] = cons.newInstance(apkFile, false, apkFile, dex[i]);
                      } else {
                          cons = getElementConstructor(Element, constructorArgs2);
                          if (cons != null) {
                              objects[i] = cons.newInstance(apkFile, apkFile, dex[i]);
                          } else {
                              cons = getElementConstructor(Element, constructorArgs3);
                              if (cons != null) {
                                  objects[i] = cons.newInstance(apkFile, null, dex[i]);
                              }
                          }
                      }
                  }
              } catch (Exception e) {
                  throw new RuntimeException("make DexElement fail", e);
              }
          }
          return objects;
      }
  
  
      // for api level >=23
      public static Object makeNativeLibraryElement(File dir) throws IOException {
          if (Build.VERSION.SDK_INT > 25 || Build.VERSION.SDK_INT == 25 && Build.VERSION.PREVIEW_SDK_INT > 0) {
              try {
                  Class NativeLibraryElement = Class.forName("dalvik.system.DexPathList$NativeLibraryElement");
                  Class[] oconstructorArgs = {File.class};
                  Constructor constructor = NativeLibraryElement.getDeclaredConstructor(oconstructorArgs);
                  constructor.setAccessible(true);
                  return constructor.newInstance(dir);
              } catch (Exception e) {
                  throw new IOException("make nativeElement fail", e);
              }
          } else {
              try {
                  Class Element = Class.forName("dalvik.system.DexPathList$Element");
                  Constructor cons = getElementConstructor(Element, constructorArgs1);
                  if (cons != null) {
                      return cons.newInstance(dir, true, null, null);
                  } else {
                      throw new IOException("make nativeElement fail | error constructor");
                  }
              } catch (Exception e) {
                  throw new IOException("make nativeElement fail", e);
              }
          }
      }
  
      private static Constructor getElementConstructor(Class element, Class... args) {
          try {
              return element.getDeclaredConstructor(args);
          } catch (Throwable e) {
              Log.w("KernalBundleImpl", "can not create element by args" + args);
          }
          return null;
      }
  
      public KernalBundleArchive getArchive() {
          return archive;
      }
  
      public File getRevisionDir() {
          return getArchive().getRevisionDir();
      }
  
      public File getRevisionZip() {
          return getArchive().getArchiveFile();
      }
  
      public static boolean shouldSyncUpdateInThisProcess(String process) {
          String processName = process;
          if (processName != null &&
                  (processName.equals(KernalConstants.baseContext.getPackageName()) ||
                          processName.toLowerCase().contains(":safemode")
                  )) {
              return true;
          } else {
              return false;
          }
      }
  
      public static void deleteDirectory(final File path) {
          final File[] files = path.listFiles();
          if (files == null) {
              return;
          }
          for (int i = 0; i < files.length; i++) {
              if (files[i].isDirectory()) {
                  deleteDirectory(files[i]);
              } else {
                  files[i].delete();
              }
          }
          path.delete();
      }
  
      public boolean isDeubgMode() {
          try {
              /**
               * enable patch debug if in debug mode
               */
              final ApplicationInfo app_info = KernalConstants.baseContext.getApplicationInfo();
              boolean DEBUG = (app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
              if (DEBUG) {
                  return true;
              }
              SharedPreferences sharedPreferences = KernalConstants.baseContext.getSharedPreferences("dynamic_test", Context.MODE_PRIVATE);
              boolean dynamic_test_flag = sharedPreferences.getBoolean("dynamic_test_key", false);
              if (dynamic_test_flag) {
                  return true;
              }
          } catch (final Exception e) {
              return false;
          }
          return false;
      }
  
  }
  