AndFix is a solution to fix the bugs online instead of redistributing Android App. AndFix is an acronym for “Android hot-fix”. AndFix supports Android version from 2.3 to 6.0, both ARM and X86 architecture, both Dalvik and ART runtime.
The compressed file format of AndFix’s patch is .apatch. It is dispatched from your own server to client to fix your App’s bugs.
优点:实时修复,仅支持java层修改
缺点:不支持添加文件到assets文件夹 不支持layout文件添加组件 不支持add filed(R.xx.xx), new class, 内部类, 匿名内部类
Principle
The implementation principle of AndFix is method body’s replacing,
具体的实现原理就是方法替换
Method replacing
AndFix judges the methods should be replaced by java custom annotation and replaces it by hooking it. AndFix has a native method art_replaceMethod in ART or dalvik_replaceMethod in Dalvik.
Now you get the application savior, the patch file. Then you need to dispatch it to your client in some way, push or pull.
Sometimes, your team members may fix each other’s bugs, and generate not only one .apatch. For this situation, you can merge .apatch files using this tool,
Rename the patch file to out.apatch, and then copy it to sdcard.
Run 1.apk and view log.
Notice
ProGuard
If you enable ProGuard, you must save the mapping.txt, so your new version’s build can use it with “-applymapping”.
And it is necessary to keep classes as follow,
Native method
com.alipay.euler.andfix.AndFix
Annotation
com.alipay.euler.andfix.annotation.MethodReplace
To ensure that these classes can be found after running an obfuscation and static analysis tool like ProGuard, add the configuration below to your ProGuard configuration file.
/** * fix * * @param file patch file * @param classLoader classloader of class that will be fixed * @param classes classes will be fixed */ public synchronized void fix(File file, ClassLoader classLoader, List<String> classes) { // 系统是否支持 if (!mSupport) { return; } if (!mSecurityChecker.verifyApk(file)) {// security check fail 签名校验 return; } try { // loadClass 输出目录 File optfile = newFile(mOptDir, file.getName()); // 保存指纹签名 boolean saveFingerprint = true; if (optfile.exists()) { // need to verify fingerprint when the optimize file exist, // prevent someone attack on jailbreak device with // Vulnerability-Parasyte. // btw:exaggerated android Vulnerability-Parasyte // http://secauo.com/Exaggerated-Android-Vulnerability-Parasyte.html // 校验MD5 if (mSecurityChecker.verifyOpt(optfile)) { saveFingerprint = false; } elseif (!optfile.delete()) { return; } } final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(), optfile.getAbsolutePath(), Context.MODE_PRIVATE); if (saveFingerprint) { // 保存MD5 mSecurityChecker.saveOptSig(optfile); } ClassLoader patchClassLoader = newClassLoader(classLoader) { @Override protected Class<?> findClass(String className) throws ClassNotFoundException { Class<?> clazz = dexFile.loadClass(className, this); if (clazz == null && className.startsWith("com.alipay.euler.andfix")) { return Class.forName(className);// annotation’s class // not found } if (clazz == null) { throw newClassNotFoundException(className); } return clazz; } }; Enumeration<String> entrys = dexFile.entries(); Class<?> clazz = null; while (entrys.hasMoreElements()) { String entry = entrys.nextElement(); if (classes != null && !classes.contains(entry)) { continue;// skip, not need fix } // loadClass clazz = dexFile.loadClass(entry, patchClassLoader); if (clazz != null) { // fixClass fixClass(clazz, classLoader); } } } catch (IOException e) { Log.e(TAG, "pacth", e); } } /** * fix class * * @param clazz class */ private void fixClass(Class<?> clazz, ClassLoader classLoader) { Method[] methods = clazz.getDeclaredMethods(); MethodReplace methodReplace; String clz; String meth; for (Method method : methods) { // 反射提取带有MethodReplace注解的方法 methodReplace = method.getAnnotation(MethodReplace.class); if (methodReplace == null) continue; clz = methodReplace.clazz(); meth = methodReplace.method(); if (!isEmpty(clz) && !isEmpty(meth)) { // jni层替换方法 replaceMethod(classLoader, clz, meth, method); } } }
SecurityChecker.java
用于验证apk和dex的签名
1 2 3 4 5 6 7 8 9 10 11
init()获取apk的签名mPublicKey verifyApk(File path)//loadPatch()时检查apk和补丁的签名 签名校验失败时抛出异常 E/SecurityChecker: /data/data/com.euler.andfix/files/apatch/out.apatch java.security.SignatureException: Signature was not verified at org.apache.harmony.security.provider.cert.X509CertImpl.verify(X509CertImpl.java:384) at com.alipay.euler.andfix.security.SecurityChecker.check(SecurityChecker.java:158) at com.alipay.euler.andfix.security.SecurityChecker.verifyApk(SecurityChecker.java:124) at com.alipay.euler.andfix.AndFixManager.fix(AndFixManager.java:121) at com.alipay.euler.andfix.patch.PatchManager.loadPatch(PatchManager.java:230) at com.alipay.euler.andfix.patch.PatchManager.addPatch(PatchManager.java:161) at com.euler.andfix.MainApplication.onCreate(MainApplication.java:63)
Compat.java
检查当前系统是否支持andfix AndFix supports Android version from 2.3 to 6.0, both ARM and X86 architecture, both Dalvik and ART runtime. not support alibaba’s YunOs
// initialize art or dalvik privatestaticnativebooleansetup(boolean isArt, int apilevel); // privatestaticnativevoidreplaceMethod(Method dest, Method src); /** * modify access flag of class’ fields to public * * @param field field */ privatestaticnativevoidsetFieldFlag(Field field);