Add new function: support xposed modules being packed into the apk
This commit is contained in:
parent
6818dfed05
commit
e0dc1a644c
Binary file not shown.
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, String> SO_FILE_PATH_MAP = new HashMap<String, String>() {
|
||||
{
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue