public class PatchManager {
private Context context;
AndFixManager andFixManager;
File src;
public PatchManager(Context context) {
this.context = context;
init();
}
private void init() {
andFixManager = new AndFixManager(context);
}
public void loadPatch(String patch) {
src = new File(patch);
Patch patch1 = new Patch(src, context);
loadPatch(patch1);
}
public void loadPatch(Patch patch) {
// PathClassLoader
ClassLoader classLoader = context.getClassLoader();
List list;
for (String name : patch.getPatchNames()) {
list = patch.getClasses(name);
andFixManager.fix(src, classLoader, list);
}
}
}
public class Patch {
private static final String PATCH_CLASSES = "Patch-Classes";
private static final String ENTRY_NAME = "META-INF/PATCH.MF";
private File mFile;
private Map> mClassMap;
private Context context;
public Patch(File mFile, Context context) {
this.mFile = mFile;
this.context = context;
init();
}
public Set getPatchNames() {
return mClassMap.keySet();
}
public List getClasses(String name) {
return mClassMap.get(name);
}
public File getmFile() {
return mFile;
}
private void init() {
JarFile jarFile = null;
InputStream inputStream = null;
mClassMap = new HashMap>();
List list = new ArrayList();
try {
jarFile = new JarFile(mFile);
JarEntry jarEntry = jarFile.getJarEntry(ENTRY_NAME);
inputStream = jarFile.getInputStream(jarEntry);
Manifest manifest = new Manifest(inputStream);
Attributes main = manifest.getMainAttributes();
Attributes.Name attrName;
for (Iterator> ite = main.keySet().iterator(); ite.hasNext(); ) {
attrName = (Attributes.Name) ite.next();
if (attrName != null) {
String name = attrName.toString();
if (name.endsWith("Classes")) {
list = Arrays.asList(main.getValue(name).split(","));
if (name.equalsIgnoreCase(PATCH_CLASSES)) {
mClassMap.put(name, list);
} else {
mClassMap.put(name.trim().substring(0, name.length() - 8), list);
}
}
}
}
} catch (Exception ex) {
Log.i("dongnao", ex.toString());
} finally {
try {
jarFile.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class HandlerNative {
static {
System.loadLibrary("dongnaofix");
}
public static native void init(int api);
public static native void replaceMethod(Method src,Method dest);
}
public class AndFixManager {
private Context context;
private File optFile;
public AndFixManager(Context context) {
this.context = context;
HandlerNative.init(Build.VERSION.SDK_INT);
}
public void fix(File file, final ClassLoader classLoader, List list) {
optFile = new File(context.getFilesDir(), file.getName());
if (optFile.exists()) {
optFile.delete();
}
try {
final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
optFile.getAbsolutePath(), Context.MODE_PRIVATE);
ClassLoader classLoader1 = new ClassLoader() {
@Override
protected Class> findClass(String className)
throws ClassNotFoundException {
Class clazz = dexFile.loadClass(className, this);
if (clazz == null) {
clazz = Class.forName(className);
}
return clazz;
}
};
Enumeration entry = dexFile.entries();
while (entry.hasMoreElements()) {
String key = entry.nextElement();
if (!list.contains(key)) {
continue;
}
Class realClazz = dexFile.loadClass(key, classLoader1);
if (realClazz != null) {
fixClass(realClazz, classLoader);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void fixClass(Class realClazz, ClassLoader classLoader) {
Method[] methods = realClazz.getMethods();
for (Method needMethod : methods) {
MethodReplace methodReplace = needMethod
.getAnnotation(MethodReplace.class);
if (methodReplace == null) {
continue;
}
Log.i("dongnao", "找到替换方法 " + methodReplace.toString()
+ " clazz 对象 " + realClazz.toString());
String clazz = methodReplace.clazz();
String methodName = methodReplace.method();
replaceMehod(classLoader, clazz, methodName, realClazz, needMethod);
}
}
private void replaceMehod(ClassLoader classLoader, String clazz,
String methodName, Class realClazz, Method method) {
try {
Class srcClazz = Class.forName(clazz);
if (srcClazz != null) {
Method src = srcClazz.getDeclaredMethod(methodName,
method.getParameterTypes());
HandlerNative.replaceMethod(src, method);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodReplace {
String clazz();
String method();
}