diff --git a/xpatch/src/main/assets/classes.dex b/xpatch/src/main/assets/classes.dex index 1807eaf..211a1dc 100755 Binary files a/xpatch/src/main/assets/classes.dex and b/xpatch/src/main/assets/classes.dex differ diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java index 435c940..dccb26c 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java @@ -39,6 +39,10 @@ public class MainCommand extends BaseCommand { description = "disable craching the apk's signature.") private boolean disableCrackSignature = false; + @Opt(opt = "xm", longOpt = "xposed-modules", description = "the xposed mpdule files to be packaged into the apk, " + + "multi files should be seperated by :(mac) or ;(win) ") + private String xposedModules; + // 原来apk中dex文件的数量 private int dexFileCount = 0; @@ -151,7 +155,7 @@ public class MainCommand extends BaseCommand { dexFileCount)); // 2. copy xposed so and dex files into the unzipped apk - mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath)); + mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath, getXposedModules(xposedModules))); // 3. compress all files into an apk and then sign it. mXpatchTasks.add(new BuildAndSignApkTask(keepBuildFiles, unzipApkFilePath, output)); @@ -197,4 +201,11 @@ public class MainCommand extends BaseCommand { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");//设置日期格式 return df.format(new Date()); } + + private String[] getXposedModules(String modules) { + if (modules == null || modules.isEmpty()) { + return null; + } + return modules.split(File.pathSeparator); + } } diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java b/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java index 3f98f8d..ce04acd 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java @@ -11,6 +11,8 @@ import java.util.HashMap; public class SoAndDexCopyTask implements Runnable { private static final String SO_FILE_NAME = "libxpatch_wl.so"; + private static final String XPOSED_MODULE_FILE_NAME_PREFIX = "libxpatch_xp_module_"; + private static final String SO_FILE_SUFFIX = ".so"; private final String[] APK_LIB_PATH_ARRAY = { "lib/armeabi-v7a/", @@ -20,18 +22,20 @@ public class SoAndDexCopyTask implements Runnable { private final HashMap SO_FILE_PATH_MAP = new HashMap() { { - put(APK_LIB_PATH_ARRAY[0], "assets/" + APK_LIB_PATH_ARRAY[0] + SO_FILE_NAME); - put(APK_LIB_PATH_ARRAY[1], "assets/" + APK_LIB_PATH_ARRAY[0] + SO_FILE_NAME); - put(APK_LIB_PATH_ARRAY[2], "assets/" + APK_LIB_PATH_ARRAY[2] + SO_FILE_NAME); + put(APK_LIB_PATH_ARRAY[0], "assets/lib/armeabi-v7a/" + SO_FILE_NAME); + put(APK_LIB_PATH_ARRAY[1], "assets/lib/armeabi-v7a/" + SO_FILE_NAME); + put(APK_LIB_PATH_ARRAY[2], "assets/lib/arm64-v8a/" + SO_FILE_NAME); } }; private int dexFileCount; private String unzipApkFilePath; + private String[] xposedModuleArray; - public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath) { + public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath, String[] xposedModuleArray) { this.dexFileCount = dexFileCount; this.unzipApkFilePath = unzipApkFilePath; + this.xposedModuleArray = xposedModuleArray; } @Override @@ -45,24 +49,27 @@ public class SoAndDexCopyTask implements Runnable { } private void copySoFile() { - // Try to find so file path in the apk, then copy so into it - boolean copySuccess = false; - for (String libPath : APK_LIB_PATH_ARRAY) { - boolean copied = copyLibFile(unzipApkFilePath + libPath.replace("/", File.separator), - SO_FILE_PATH_MAP.get(libPath), false); - if (copied) { - copySuccess = true; - } - } + String apkSoLibPath = findTargetLibPath(); + String apkSoFullPath = fullLibPath(apkSoLibPath); - // Iif apk do not contain so file path, then create lib/armeabi-v7a, and copy libwhale.so into it - if (!copySuccess) { - String path = APK_LIB_PATH_ARRAY[0]; - copySuccess = copyLibFile(unzipApkFilePath + path.replace("/", File.separator), - SO_FILE_PATH_MAP.get(path), true); - } - if (!copySuccess) { - throw new IllegalArgumentException(" copy so file failed "); + copyLibFile(apkSoFullPath, SO_FILE_PATH_MAP.get(apkSoLibPath)); + + // copy xposed modules into the lib path + if (xposedModuleArray != null && xposedModuleArray.length > 0) { + int index = 0; + for (String modulePath : xposedModuleArray) { + modulePath = modulePath.trim(); + if (modulePath == null || modulePath.length() == 0) { + continue; + } + File moduleFile = new File(modulePath); + if (!moduleFile.exists()) { + continue; + } + String outputModuleFile = XPOSED_MODULE_FILE_NAME_PREFIX + index + SO_FILE_SUFFIX; + FileUtils.copyFile(moduleFile, new File(apkSoFullPath, outputModuleFile)); + index++; + } } } @@ -73,18 +80,49 @@ public class SoAndDexCopyTask implements Runnable { FileUtils.copyFileFromJar("assets/classes.dex", unzipApkFilePath + copiedDexFileName); } - private boolean copyLibFile(String libFilePath, String srcSoPath, boolean forceCopy) { + private String fullLibPath(String libPath) { + return unzipApkFilePath + libPath.replace("/", File.separator); + } + + private void copyLibFile(String libFilePath, String srcSoPath) { File apkSoParentFile = new File(libFilePath); - if (forceCopy && !apkSoParentFile.exists()) { + if (!apkSoParentFile.exists()) { apkSoParentFile.mkdirs(); } - File[] childs = apkSoParentFile.listFiles(); - if (apkSoParentFile.exists() && (forceCopy || (childs != null && childs.length > 0))) { - FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, SO_FILE_NAME).getAbsolutePath()); - return true; + // get the file name first + int lastIndex = srcSoPath.lastIndexOf('/'); + int length = srcSoPath.length(); + String soFileName = srcSoPath.substring(lastIndex, length); + + // do copy + FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, soFileName).getAbsolutePath()); + } + + // Try to find the lib path where the so file should put. + // If there is many lib path, try to find the path which has the most so files + private String findTargetLibPath() { + int maxChildFileCount = 0; + int maxChildFileIndex = 0; + int index = 0; + for (String libPath : APK_LIB_PATH_ARRAY) { + String fullPath = fullLibPath(libPath); + File file = new File(fullPath); + if (file.exists()) { + String[] childList = file.list(); + int childCount = 0; + if (childList != null) { + childCount = childList.length; + } + if (childCount > maxChildFileCount) { + maxChildFileCount = childCount; + maxChildFileIndex = index; + } + } + index++; } - return false; + + return APK_LIB_PATH_ARRAY[maxChildFileIndex]; } private void deleteMetaInfo() { diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java index 50dc9f5..e6e61c6 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java @@ -11,6 +11,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.zip.CRC32; @@ -113,6 +115,39 @@ public class FileUtils { } } + public static void copyFile(String sourcePath, String targetPath) { + copyFile(new File(sourcePath), new File(targetPath)); + } + + public static void copyFile(File source, File target) { + + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + try { + inputStream = new FileInputStream(source); + outputStream = new FileOutputStream(target); + FileChannel iChannel = inputStream.getChannel(); + FileChannel oChannel = outputStream.getChannel(); + + ByteBuffer buffer = ByteBuffer.allocate(1024); + while (true) { + buffer.clear(); + int r = iChannel.read(buffer); + if (r == -1) { + break; + } + buffer.limit(buffer.position()); + buffer.position(0); + oChannel.write(buffer); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + close(inputStream); + close(outputStream); + } + } + public static void deleteDir(File file) { if (file.isDirectory()) { File[] files = file.listFiles();