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.")
|
description = "disable craching the apk's signature.")
|
||||||
private boolean disableCrackSignature = false;
|
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文件的数量
|
// 原来apk中dex文件的数量
|
||||||
private int dexFileCount = 0;
|
private int dexFileCount = 0;
|
||||||
|
|
||||||
|
|
@ -151,7 +155,7 @@ public class MainCommand extends BaseCommand {
|
||||||
dexFileCount));
|
dexFileCount));
|
||||||
|
|
||||||
// 2. copy xposed so and dex files into the unzipped apk
|
// 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.
|
// 3. compress all files into an apk and then sign it.
|
||||||
mXpatchTasks.add(new BuildAndSignApkTask(keepBuildFiles, unzipApkFilePath, output));
|
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");//设置日期格式
|
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");//设置日期格式
|
||||||
return df.format(new Date());
|
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 {
|
public class SoAndDexCopyTask implements Runnable {
|
||||||
|
|
||||||
private static final String SO_FILE_NAME = "libxpatch_wl.so";
|
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 = {
|
private final String[] APK_LIB_PATH_ARRAY = {
|
||||||
"lib/armeabi-v7a/",
|
"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>() {
|
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[0], "assets/lib/armeabi-v7a/" + SO_FILE_NAME);
|
||||||
put(APK_LIB_PATH_ARRAY[1], "assets/" + APK_LIB_PATH_ARRAY[0] + SO_FILE_NAME);
|
put(APK_LIB_PATH_ARRAY[1], "assets/lib/armeabi-v7a/" + SO_FILE_NAME);
|
||||||
put(APK_LIB_PATH_ARRAY[2], "assets/" + APK_LIB_PATH_ARRAY[2] + SO_FILE_NAME);
|
put(APK_LIB_PATH_ARRAY[2], "assets/lib/arm64-v8a/" + SO_FILE_NAME);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private int dexFileCount;
|
private int dexFileCount;
|
||||||
private String unzipApkFilePath;
|
private String unzipApkFilePath;
|
||||||
|
private String[] xposedModuleArray;
|
||||||
|
|
||||||
public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath) {
|
public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath, String[] xposedModuleArray) {
|
||||||
this.dexFileCount = dexFileCount;
|
this.dexFileCount = dexFileCount;
|
||||||
this.unzipApkFilePath = unzipApkFilePath;
|
this.unzipApkFilePath = unzipApkFilePath;
|
||||||
|
this.xposedModuleArray = xposedModuleArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -45,24 +49,27 @@ public class SoAndDexCopyTask implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copySoFile() {
|
private void copySoFile() {
|
||||||
// Try to find so file path in the apk, then copy so into it
|
String apkSoLibPath = findTargetLibPath();
|
||||||
boolean copySuccess = false;
|
String apkSoFullPath = fullLibPath(apkSoLibPath);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iif apk do not contain so file path, then create lib/armeabi-v7a, and copy libwhale.so into it
|
copyLibFile(apkSoFullPath, SO_FILE_PATH_MAP.get(apkSoLibPath));
|
||||||
if (!copySuccess) {
|
|
||||||
String path = APK_LIB_PATH_ARRAY[0];
|
// copy xposed modules into the lib path
|
||||||
copySuccess = copyLibFile(unzipApkFilePath + path.replace("/", File.separator),
|
if (xposedModuleArray != null && xposedModuleArray.length > 0) {
|
||||||
SO_FILE_PATH_MAP.get(path), true);
|
int index = 0;
|
||||||
}
|
for (String modulePath : xposedModuleArray) {
|
||||||
if (!copySuccess) {
|
modulePath = modulePath.trim();
|
||||||
throw new IllegalArgumentException(" copy so file failed ");
|
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);
|
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);
|
File apkSoParentFile = new File(libFilePath);
|
||||||
if (forceCopy && !apkSoParentFile.exists()) {
|
if (!apkSoParentFile.exists()) {
|
||||||
apkSoParentFile.mkdirs();
|
apkSoParentFile.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
File[] childs = apkSoParentFile.listFiles();
|
// get the file name first
|
||||||
if (apkSoParentFile.exists() && (forceCopy || (childs != null && childs.length > 0))) {
|
int lastIndex = srcSoPath.lastIndexOf('/');
|
||||||
FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, SO_FILE_NAME).getAbsolutePath());
|
int length = srcSoPath.length();
|
||||||
return true;
|
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() {
|
private void deleteMetaInfo() {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.zip.CRC32;
|
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) {
|
public static void deleteDir(File file) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
File[] files = file.listFiles();
|
File[] files = file.listFiles();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue