/* * Copyright (C) 2015 Baidu, Inc. All Rights Reserved. */ package dodola.hotfix; import android.annotation.TargetApi; import android.content.Context; import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /* compiled from: ProGuard */ /** * https://github.com/dodola/HotFix/blob/master/hotfixlib/src/main/java/dodola/hotfixlib/HotFix.java */ public final class HotFix { private HotFix() {} public static void patch(Context context, String patchDexFile, String patchClassName) { if (patchDexFile != null && new File(patchDexFile).exists()) { try { if (hasLexClassLoader()) { injectInAliyunOs(context, patchDexFile, patchClassName); } else if (hasDexClassLoader()) { injectAboveEqualApiLevel14(context, patchDexFile, patchClassName); } else { injectBelowApiLevel14(context, patchDexFile, patchClassName); } } catch (Throwable th) { } } } /** * 判断是否是 阿里云 OS 机型 */ private static boolean hasLexClassLoader() { try { Class.forName("dalvik.system.LexClassLoader"); return true; } catch (ClassNotFoundException e) { return false; } } /** * 判断是否是 API 14 以上 */ private static boolean hasDexClassLoader() { try { Class.forName("dalvik.system.BaseDexClassLoader"); return true; } catch (ClassNotFoundException e) { return false; } } /** * 阿里云 OS 上的 插桩策略 * * @param context context * @param patchDexFile 插件 dex 的地址 * @param patchClassName 插件 dex 中要修复的类 name * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException * @throws InstantiationException * @throws NoSuchFieldException */ private static void injectInAliyunOs(Context context, String patchDexFile, String patchClassName) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { PathClassLoader obj = (PathClassLoader) context.getClassLoader(); String replaceAll = new File(patchDexFile).getName().replaceAll("\\.[a-zA-Z0-9]+", ".lex"); Class cls = Class.forName("dalvik.system.LexClassLoader"); Object newInstance = cls.getConstructor( new Class[] { String.class, String.class, String.class, ClassLoader.class }) .newInstance( new Object[] { context.getDir("dex", 0).getAbsolutePath() + File.separator + replaceAll, context.getDir("dex", 0).getAbsolutePath(), patchDexFile, obj }); cls.getMethod("loadClass", new Class[] { String.class }) .invoke(newInstance, new Object[] { patchClassName }); setField(obj, PathClassLoader.class, "mPaths", appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(newInstance, cls, "mRawDexPath"))); setField(obj, PathClassLoader.class, "mFiles", combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(newInstance, cls, "mFiles"))); setField(obj, PathClassLoader.class, "mZips", combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(newInstance, cls, "mZips"))); setField(obj, PathClassLoader.class, "mLexs", combineArray(getField(obj, PathClassLoader.class, "mLexs"), getField(newInstance, cls, "mDexs"))); } /** * 小于 API 14 的插桩策略 * * @param context context * @param str 插件 dex 的路径 * @param str2 插件 dex 中要修复的类 name * @throws ClassNotFoundException * @throws NoSuchFieldException * @throws IllegalAccessException */ @TargetApi(14) private static void injectBelowApiLevel14(Context context, String str, String str2) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { PathClassLoader obj = (PathClassLoader) context.getClassLoader(); DexClassLoader dexClassLoader = new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader()); dexClassLoader.loadClass(str2); setField(obj, PathClassLoader.class, "mPaths", appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(dexClassLoader, DexClassLoader.class, "mRawDexPath") )); setField(obj, PathClassLoader.class, "mFiles", combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(dexClassLoader, DexClassLoader.class, "mFiles") )); setField(obj, PathClassLoader.class, "mZips", combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(dexClassLoader, DexClassLoader.class, "mZips"))); setField(obj, PathClassLoader.class, "mDexs", combineArray(getField(obj, PathClassLoader.class, "mDexs"), getField(dexClassLoader, DexClassLoader.class, "mDexs"))); obj.loadClass(str2); } /** * 大于 API 14 的插桩策略 * * @param context context * @param str dex 的路径 * @param str2 插件 dex 中要修复的类 name * @throws ClassNotFoundException * @throws NoSuchFieldException * @throws IllegalAccessException */ private static void injectAboveEqualApiLevel14(Context context, String str, String str2) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader(); Object a = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList( new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader())))); Object a2 = getPathList(pathClassLoader); setField(a2, a2.getClass(), "dexElements", a); pathClassLoader.loadClass(str2); } /** * 获取 pathList * * @param obj 对应的类 * @return pathList * @throws ClassNotFoundException * @throws NoSuchFieldException * @throws IllegalAccessException */ private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList"); } /** * 获取 dexElements[] * * @param obj 对应 类 * @return dexElements[] * @throws NoSuchFieldException * @throws IllegalAccessException */ private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException { return getField(obj, obj.getClass(), "dexElements"); } private static Object getField(Object obj, Class cls, String str) throws NoSuchFieldException, IllegalAccessException { Field declaredField = cls.getDeclaredField(str); declaredField.setAccessible(true); return declaredField.get(obj); } private static void setField(Object obj, Class cls, String str, Object obj2) throws NoSuchFieldException, IllegalAccessException { Field declaredField = cls.getDeclaredField(str); declaredField.setAccessible(true); declaredField.set(obj, obj2); } /** * 合并两个数组 * * @param obj 数组 * @param obj2 数组 * @return 合并后数组 */ private static Object combineArray(Object obj, Object obj2) { Class componentType = obj2.getClass().getComponentType(); int length = Array.getLength(obj2); int length2 = Array.getLength(obj) + length; Object newInstance = Array.newInstance(componentType, length2); for (int i = 0; i < length2; i++) { if (i < length) { Array.set(newInstance, i, Array.get(obj2, i)); } else { Array.set(newInstance, i, Array.get(obj, i - length)); } } return newInstance; } /** * 添加子元素到数组 * * @param obj 数组 * @param obj2 子元素 * @return 添加后的数组 */ private static Object appendArray(Object obj, Object obj2) { Class componentType = obj.getClass().getComponentType(); int length = Array.getLength(obj); Object newInstance = Array.newInstance(componentType, length + 1); Array.set(newInstance, 0, obj2); for (int i = 1; i < length + 1; i++) { Array.set(newInstance, i, Array.get(obj, i - 1)); } return newInstance; } }