remove useless again
This commit is contained in:
parent
0ba86283ea
commit
d2337bbd96
|
|
@ -15,6 +15,7 @@ dependencies {
|
||||||
implementation project(':share')
|
implementation project(':share')
|
||||||
implementation 'commons-io:commons-io:2.10.0'
|
implementation 'commons-io:commons-io:2.10.0'
|
||||||
implementation 'com.android.tools.build:apkzlib:4.2.1'
|
implementation 'com.android.tools.build:apkzlib:4.2.1'
|
||||||
|
implementation 'com.beust:jcommander:1.81'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/apksigner/java"
|
sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/apksigner/java"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import static org.apache.commons.io.FileUtils.copyFile;
|
||||||
|
|
||||||
import com.android.tools.build.apkzlib.zip.StoredEntry;
|
import com.android.tools.build.apkzlib.zip.StoredEntry;
|
||||||
import com.android.tools.build.apkzlib.zip.ZFile;
|
import com.android.tools.build.apkzlib.zip.ZFile;
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.wind.meditor.base.BaseCommand;
|
||||||
import com.wind.meditor.core.FileProcesser;
|
import com.wind.meditor.core.FileProcesser;
|
||||||
import com.wind.meditor.property.AttributeItem;
|
import com.wind.meditor.property.AttributeItem;
|
||||||
import com.wind.meditor.property.ModificationProperty;
|
import com.wind.meditor.property.ModificationProperty;
|
||||||
|
|
@ -13,7 +16,6 @@ import com.wind.meditor.utils.NodeValue;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.lsposed.lspatch.share.Constants;
|
import org.lsposed.lspatch.share.Constants;
|
||||||
import org.lsposed.patch.base.BaseCommand;
|
|
||||||
import org.lsposed.patch.task.BuildAndSignApkTask;
|
import org.lsposed.patch.task.BuildAndSignApkTask;
|
||||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||||
import org.lsposed.patch.util.ManifestParser;
|
import org.lsposed.patch.util.ManifestParser;
|
||||||
|
|
@ -32,26 +34,29 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class LSPatch extends BaseCommand {
|
public class LSPatch {
|
||||||
|
|
||||||
|
@Parameter(description = "apk path")
|
||||||
private String apkPath;
|
private String apkPath;
|
||||||
|
|
||||||
private String unzipApkFilePath;
|
private String unzipApkFilePath;
|
||||||
|
|
||||||
@Opt(opt = "o", longOpt = "output", description = "Output .apk file, default is " +
|
@Parameter(names = "--help", help = true, order = 0)
|
||||||
"$source_apk_dir/[file-name]-xposed-signed.apk", argName = "file")
|
private boolean help = false;
|
||||||
|
|
||||||
|
@Parameter(names = {"-o", "--output"}, description = "Output apk file")
|
||||||
private String outputPath;
|
private String outputPath;
|
||||||
|
|
||||||
@Opt(opt = "f", longOpt = "force", hasArg = false, description = "Force overwrite exists output file")
|
@Parameter(names = {"-f", "--force"}, description = "Force overwrite exists output file")
|
||||||
private boolean forceOverwrite = false;
|
private boolean forceOverwrite = false;
|
||||||
|
|
||||||
@Opt(opt = "p", longOpt = "proxyname", description = "Special proxy app name with full dot path", argName = "name")
|
@Parameter(names = {"-p", "--proxyname"}, description = "Special proxy app name with full dot path")
|
||||||
private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub";
|
private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub";
|
||||||
|
|
||||||
@Opt(opt = "d", longOpt = "debuggable", description = "Set 1 to make the app debuggable = true, " +
|
@Parameter(names = {"-d", "--debuggable"}, description = "Set 1 to make the app debuggable = true, set 0 to make the app debuggable = false")
|
||||||
"set 0 to make the app debuggable = false", argName = "0 or 1")
|
private boolean debuggableFlag = false;
|
||||||
private int debuggableFlag = -1; // 0: debuggable = false 1: debuggable = true
|
|
||||||
|
|
||||||
@Opt(opt = "l", longOpt = "sigbypasslv", description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0", argName = "0-2")
|
@Parameter(names = {"-l", "--sigbypasslv"}, description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0")
|
||||||
private int sigbypassLevel = 0;
|
private int sigbypassLevel = 0;
|
||||||
|
|
||||||
private int dexFileCount = 0;
|
private int dexFileCount = 0;
|
||||||
|
|
@ -67,59 +72,48 @@ public class LSPatch extends BaseCommand {
|
||||||
"lib/x86_64"
|
"lib/x86_64"
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void main(String... args) {
|
private static JCommander jCommander;
|
||||||
new LSPatch().doMain(args);
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
LSPatch lsPatch = new LSPatch();
|
||||||
|
jCommander = JCommander.newBuilder()
|
||||||
|
.addObject(lsPatch)
|
||||||
|
.build();
|
||||||
|
jCommander.parse(args);
|
||||||
|
lsPatch.doCommandLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void fuckIfFail(boolean b) {
|
public void doCommandLine() throws IOException {
|
||||||
if (!b) {
|
if (apkPath == null || apkPath.isEmpty()) {
|
||||||
throw new IllegalStateException("wtf", new Throwable("DUMPBT"));
|
jCommander.usage();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doCommandLine() throws IOException {
|
|
||||||
if (remainingArgs.length != 1) {
|
|
||||||
System.out.println();
|
|
||||||
if (remainingArgs.length == 0) {
|
|
||||||
System.out.println("Please choose one apk file you want to process. ");
|
|
||||||
}
|
|
||||||
if (remainingArgs.length > 1) {
|
|
||||||
System.out.println("This tool can only used with one apk file.");
|
|
||||||
}
|
|
||||||
usage();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
apkPath = remainingArgs[0];
|
|
||||||
|
|
||||||
File srcApkFile = new File(apkPath);
|
File srcApkFile = new File(apkPath);
|
||||||
|
|
||||||
if (!srcApkFile.exists()) {
|
if (!srcApkFile.exists()) {
|
||||||
System.out.println("The source apk file not exsit, please choose another one. " +
|
System.out.println("The source apk file not exsit, please choose another one.");
|
||||||
"current apk file is = " + apkPath);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File finalApk = new File(String.format("%s-%s-unsigned.apk", getBaseName(srcApkFile.getAbsolutePath()),
|
File finalApk = new File(String.format("%s-%s-unsigned.apk", srcApkFile.getName(),
|
||||||
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel));
|
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel));
|
||||||
FileUtils.copyFile(srcApkFile, finalApk);
|
FileUtils.copyFile(srcApkFile, finalApk);
|
||||||
|
|
||||||
ZFile zFile = new ZFile(finalApk);
|
ZFile zFile = ZFile.openReadWrite(finalApk);
|
||||||
|
|
||||||
String currentDir = new File(".").getAbsolutePath();
|
String currentDir = new File(".").getAbsolutePath();
|
||||||
System.out.println("currentDir: " + currentDir);
|
System.out.println("work dir: " + currentDir);
|
||||||
System.out.println("apkPath: " + apkPath);
|
System.out.println("apk path: " + apkPath);
|
||||||
|
|
||||||
if (outputPath == null || outputPath.length() == 0) {
|
if (outputPath == null || outputPath.length() == 0) {
|
||||||
String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel;
|
String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel;
|
||||||
outputPath = String.format("%s-%s-xposed-signed.apk", getBaseName(apkPath), sig);
|
outputPath = String.format("%s-%s-xposed-signed.apk", new File(apkPath).getName(), sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
File outputFile = new File(outputPath);
|
File outputFile = new File(outputPath);
|
||||||
if (outputFile.exists() && !forceOverwrite) {
|
if (outputFile.exists() && !forceOverwrite) {
|
||||||
System.err.println(outputPath + " exists, use --force to overwrite");
|
System.err.println(outputPath + " exists, use --force to overwrite");
|
||||||
usage();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +124,7 @@ public class LSPatch extends BaseCommand {
|
||||||
outputApkFileParentPath = absPath.substring(0, index);
|
outputApkFileParentPath = absPath.substring(0, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
String apkFileName = getBaseName(srcApkFile);
|
String apkFileName = srcApkFile.getName();
|
||||||
|
|
||||||
String tempFilePath = outputApkFileParentPath + File.separator +
|
String tempFilePath = outputApkFileParentPath + File.separator +
|
||||||
currentTimeStr() + "-tmp" + File.separator;
|
currentTimeStr() + "-tmp" + File.separator;
|
||||||
|
|
@ -252,10 +246,7 @@ public class LSPatch extends BaseCommand {
|
||||||
private void modifyManifestFile(String filePath, String dstFilePath) {
|
private void modifyManifestFile(String filePath, String dstFilePath) {
|
||||||
ModificationProperty property = new ModificationProperty();
|
ModificationProperty property = new ModificationProperty();
|
||||||
|
|
||||||
if (debuggableFlag >= 0) {
|
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag));
|
||||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag != 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
|
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
|
||||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName));
|
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,429 +0,0 @@
|
||||||
package org.lsposed.patch.base;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Wind
|
|
||||||
*/
|
|
||||||
public abstract class BaseCommand {
|
|
||||||
|
|
||||||
private String onlineHelp;
|
|
||||||
|
|
||||||
protected Map<String, Option> optMap = new HashMap<String, Option>();
|
|
||||||
|
|
||||||
@Opt(opt = "h", longOpt = "help", hasArg = false, description = "Print this help message")
|
|
||||||
private boolean printHelp = false;
|
|
||||||
|
|
||||||
protected String[] remainingArgs;
|
|
||||||
protected String[] orginalArgs;
|
|
||||||
|
|
||||||
@Retention(value = RetentionPolicy.RUNTIME)
|
|
||||||
@Target(value = { ElementType.FIELD })
|
|
||||||
static public @interface Opt {
|
|
||||||
String argName() default "";
|
|
||||||
|
|
||||||
String description() default "";
|
|
||||||
|
|
||||||
boolean hasArg() default true;
|
|
||||||
|
|
||||||
String longOpt() default "";
|
|
||||||
|
|
||||||
String opt() default "";
|
|
||||||
|
|
||||||
boolean required() default false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static protected class Option implements Comparable<Option> {
|
|
||||||
public String argName = "arg";
|
|
||||||
public String description;
|
|
||||||
public Field field;
|
|
||||||
public boolean hasArg = true;
|
|
||||||
public String longOpt;
|
|
||||||
public String opt;
|
|
||||||
public boolean required = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(Option o) {
|
|
||||||
int result = s(this.opt, o.opt);
|
|
||||||
if (result == 0) {
|
|
||||||
result = s(this.longOpt, o.longOpt);
|
|
||||||
if (result == 0) {
|
|
||||||
result = s(this.argName, o.argName);
|
|
||||||
if (result == 0) {
|
|
||||||
result = s(this.description, o.description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int s(String a, String b) {
|
|
||||||
if (a != null && b != null) {
|
|
||||||
return a.compareTo(b);
|
|
||||||
} else if (a != null) {
|
|
||||||
return 1;
|
|
||||||
} else if (b != null) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOptAndLongOpt() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
boolean havePrev = false;
|
|
||||||
if (opt != null && opt.length() > 0) {
|
|
||||||
sb.append("-").append(opt);
|
|
||||||
havePrev = true;
|
|
||||||
}
|
|
||||||
if (longOpt != null && longOpt.length() > 0) {
|
|
||||||
if (havePrev) {
|
|
||||||
sb.append(", ");
|
|
||||||
}
|
|
||||||
sb.append("--").append(longOpt);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Retention(value = RetentionPolicy.RUNTIME)
|
|
||||||
@Target(value = { ElementType.TYPE })
|
|
||||||
static public @interface Syntax {
|
|
||||||
|
|
||||||
String cmd();
|
|
||||||
|
|
||||||
String desc() default "";
|
|
||||||
|
|
||||||
String onlineHelp() default "";
|
|
||||||
|
|
||||||
String syntax() default "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doMain(String... args) {
|
|
||||||
try {
|
|
||||||
initOptions();
|
|
||||||
parseSetArgs(args);
|
|
||||||
doCommandLine();
|
|
||||||
} catch (HelpException e) {
|
|
||||||
String msg = e.getMessage();
|
|
||||||
if (msg != null && msg.length() > 0) {
|
|
||||||
System.err.println("ERROR: " + msg);
|
|
||||||
}
|
|
||||||
usage();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void doCommandLine() throws Exception;
|
|
||||||
|
|
||||||
protected String getVersionString() {
|
|
||||||
return getClass().getPackage().getImplementationVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initOptions() {
|
|
||||||
initOptionFromClass(this.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initOptionFromClass(Class<?> clz) {
|
|
||||||
if (clz == null) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
initOptionFromClass(clz.getSuperclass());
|
|
||||||
}
|
|
||||||
|
|
||||||
Syntax syntax = clz.getAnnotation(Syntax.class);
|
|
||||||
if (syntax != null) {
|
|
||||||
this.onlineHelp = syntax.onlineHelp();
|
|
||||||
}
|
|
||||||
|
|
||||||
Field[] fs = clz.getDeclaredFields();
|
|
||||||
for (Field f : fs) {
|
|
||||||
Opt opt = f.getAnnotation(Opt.class);
|
|
||||||
if (opt != null) {
|
|
||||||
f.setAccessible(true);
|
|
||||||
Option option = new Option();
|
|
||||||
option.field = f;
|
|
||||||
option.description = opt.description();
|
|
||||||
option.hasArg = opt.hasArg();
|
|
||||||
option.required = opt.required();
|
|
||||||
if ("".equals(opt.longOpt()) && "".equals(opt.opt())) { // into automode
|
|
||||||
option.longOpt = fromCamel(f.getName());
|
|
||||||
if (f.getType().equals(boolean.class)) {
|
|
||||||
option.hasArg=false;
|
|
||||||
try {
|
|
||||||
if (f.getBoolean(this)) {
|
|
||||||
throw new RuntimeException("the value of " + f +
|
|
||||||
" must be false, as it is declared as no args");
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkConflict(option, "--" + option.longOpt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!opt.hasArg()) {
|
|
||||||
if (!f.getType().equals(boolean.class)) {
|
|
||||||
throw new RuntimeException("the type of " + f
|
|
||||||
+ " must be boolean, as it is declared as no args");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (f.getBoolean(this)) {
|
|
||||||
throw new RuntimeException("the value of " + f +
|
|
||||||
" must be false, as it is declared as no args");
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean haveLongOpt = false;
|
|
||||||
if (!"".equals(opt.longOpt())) {
|
|
||||||
option.longOpt = opt.longOpt();
|
|
||||||
checkConflict(option, "--" + option.longOpt);
|
|
||||||
haveLongOpt = true;
|
|
||||||
}
|
|
||||||
if (!"".equals(opt.argName())) {
|
|
||||||
option.argName = opt.argName();
|
|
||||||
}
|
|
||||||
if (!"".equals(opt.opt())) {
|
|
||||||
option.opt = opt.opt();
|
|
||||||
checkConflict(option, "-" + option.opt);
|
|
||||||
} else {
|
|
||||||
if (!haveLongOpt) {
|
|
||||||
throw new RuntimeException("opt or longOpt is not set in @Opt(...) " + f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkConflict(Option option, String key) {
|
|
||||||
if (optMap.containsKey(key)) {
|
|
||||||
Option preOption = optMap.get(key);
|
|
||||||
throw new RuntimeException(String.format("[@Opt(...) %s] conflict with [@Opt(...) %s]",
|
|
||||||
preOption.field.toString(), option.field
|
|
||||||
));
|
|
||||||
}
|
|
||||||
optMap.put(key, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fromCamel(String name) {
|
|
||||||
if (name.length() == 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
char[] charArray = name.toCharArray();
|
|
||||||
sb.append(Character.toLowerCase(charArray[0]));
|
|
||||||
for (int i = 1; i < charArray.length; i++) {
|
|
||||||
char c = charArray[i];
|
|
||||||
if (Character.isUpperCase(c)) {
|
|
||||||
sb.append("-").append(Character.toLowerCase(c));
|
|
||||||
} else {
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void parseSetArgs(String... args) throws IllegalArgumentException, IllegalAccessException {
|
|
||||||
this.orginalArgs = args;
|
|
||||||
List<String> remainsOptions = new ArrayList<>();
|
|
||||||
Set<Option> requiredOpts = collectRequriedOptions(optMap);
|
|
||||||
Option needArgOpt = null;
|
|
||||||
for (String s : args) {
|
|
||||||
if (needArgOpt != null) {
|
|
||||||
Field field = needArgOpt.field;
|
|
||||||
Class clazz = field.getType();
|
|
||||||
if (clazz.equals(List.class)) {
|
|
||||||
try {
|
|
||||||
List<Object> object = ((List<Object>) field.get(this));
|
|
||||||
|
|
||||||
// 获取List对象的泛型类型
|
|
||||||
ParameterizedType listGenericType = (ParameterizedType) field.getGenericType();
|
|
||||||
Type[] listActualTypeArguments = listGenericType.getActualTypeArguments();
|
|
||||||
Class typeClazz = (Class) listActualTypeArguments[0];
|
|
||||||
object.add(convert(s, typeClazz));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
field.set(this, convert(s, clazz));
|
|
||||||
}
|
|
||||||
needArgOpt = null;
|
|
||||||
} else if (s.startsWith("-")) {// its a short or long option
|
|
||||||
Option opt = optMap.get(s);
|
|
||||||
requiredOpts.remove(opt);
|
|
||||||
if (opt == null) {
|
|
||||||
System.err.println("ERROR: Unrecognized option: " + s);
|
|
||||||
throw new HelpException();
|
|
||||||
} else {
|
|
||||||
if (opt.hasArg) {
|
|
||||||
needArgOpt = opt;
|
|
||||||
} else {
|
|
||||||
opt.field.set(this, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
remainsOptions.add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needArgOpt != null) {
|
|
||||||
System.err.println("ERROR: Option " + needArgOpt.getOptAndLongOpt() + " need an argument value");
|
|
||||||
throw new HelpException();
|
|
||||||
}
|
|
||||||
this.remainingArgs = remainsOptions.toArray(new String[remainsOptions.size()]);
|
|
||||||
if (this.printHelp) {
|
|
||||||
throw new HelpException();
|
|
||||||
}
|
|
||||||
if (!requiredOpts.isEmpty()) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("ERROR: Options: ");
|
|
||||||
boolean first = true;
|
|
||||||
for (Option option : requiredOpts) {
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
sb.append(" and ");
|
|
||||||
}
|
|
||||||
sb.append(option.getOptAndLongOpt());
|
|
||||||
}
|
|
||||||
sb.append(" is required");
|
|
||||||
System.err.println(sb.toString());
|
|
||||||
throw new HelpException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
protected Object convert(String value, Class type) {
|
|
||||||
if (type.equals(String.class)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (type.equals(int.class) || type.equals(Integer.class)) {
|
|
||||||
return Integer.parseInt(value);
|
|
||||||
}
|
|
||||||
if (type.equals(long.class) || type.equals(Long.class)) {
|
|
||||||
return Long.parseLong(value);
|
|
||||||
}
|
|
||||||
if (type.equals(float.class) || type.equals(Float.class)) {
|
|
||||||
return Float.parseFloat(value);
|
|
||||||
}
|
|
||||||
if (type.equals(double.class) || type.equals(Double.class)) {
|
|
||||||
return Double.parseDouble(value);
|
|
||||||
}
|
|
||||||
if (type.equals(boolean.class) || type.equals(Boolean.class)) {
|
|
||||||
return Boolean.parseBoolean(value);
|
|
||||||
}
|
|
||||||
if (type.equals(File.class)) {
|
|
||||||
return new File(value);
|
|
||||||
}
|
|
||||||
if (type.equals(Path.class)) {
|
|
||||||
return new File(value).toPath();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
type.asSubclass(Enum.class);
|
|
||||||
return Enum.valueOf(type, value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("can't convert [" + value + "] to type " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Option> collectRequriedOptions(Map<String, Option> optMap) {
|
|
||||||
Set<Option> options = new HashSet<Option>();
|
|
||||||
for (Map.Entry<String, Option> e : optMap.entrySet()) {
|
|
||||||
Option option = e.getValue();
|
|
||||||
if (option.required) {
|
|
||||||
options.add(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
protected static class HelpException extends RuntimeException {
|
|
||||||
|
|
||||||
public HelpException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HelpException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void usage() {
|
|
||||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.err, StandardCharsets.UTF_8), true);
|
|
||||||
out.println();
|
|
||||||
|
|
||||||
if (this.optMap.size() > 0) {
|
|
||||||
out.println("Options:");
|
|
||||||
out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeSet<Option> options = new TreeSet<>(this.optMap.values());
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (Option option : options) {
|
|
||||||
sb.setLength(0);
|
|
||||||
sb.append(" ").append(option.getOptAndLongOpt());
|
|
||||||
if (option.hasArg) {
|
|
||||||
sb.append(" <").append(option.argName).append(">");
|
|
||||||
}
|
|
||||||
String desc = option.description;
|
|
||||||
if (desc == null || desc.length() == 0) {// no description
|
|
||||||
out.println(sb);
|
|
||||||
} else {
|
|
||||||
int maxWidth = 15;
|
|
||||||
if(sb.length() >= maxWidth) {
|
|
||||||
sb.append('\n');
|
|
||||||
sb.append(" ".repeat(maxWidth));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sb.append(" ".repeat(maxWidth - sb.length()));
|
|
||||||
}
|
|
||||||
sb.append(desc);
|
|
||||||
out.println(sb);
|
|
||||||
}
|
|
||||||
out.println();
|
|
||||||
}
|
|
||||||
String ver = getVersionString();
|
|
||||||
if (ver != null && !"".equals(ver)) {
|
|
||||||
out.println("version: " + ver);
|
|
||||||
}
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getBaseName(String fn) {
|
|
||||||
int x = fn.lastIndexOf('.');
|
|
||||||
return x >= 0 ? fn.substring(0, x) : fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取文件不包含后缀的名称
|
|
||||||
public static String getBaseName(File fn) {
|
|
||||||
return getBaseName(fn.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -50,7 +50,9 @@ public class BuildAndSignApkTask implements Runnable {
|
||||||
|
|
||||||
// delete unsigned apk file
|
// delete unsigned apk file
|
||||||
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
||||||
LSPatch.fuckIfFail(unsignedApkFile.delete());
|
if (!unsignedApkFile.delete()) {
|
||||||
|
throw new IllegalStateException("wtf");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception err) {
|
catch (Exception err) {
|
||||||
|
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
||||||
package org.lsposed.patch.util;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class ReflectUtils {
|
|
||||||
|
|
||||||
//获取类的实例的变量的值
|
|
||||||
public static Object getField(Object receiver, String fieldName) {
|
|
||||||
return getField(null, receiver, fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取类的静态变量的值
|
|
||||||
public static Object getField(String className, String fieldName) {
|
|
||||||
return getField(className, null, fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object getField(Class<?> clazz, String className, String fieldName, Object receiver) {
|
|
||||||
try {
|
|
||||||
if (clazz == null) {
|
|
||||||
clazz = Class.forName(className);
|
|
||||||
}
|
|
||||||
Field field = clazz.getDeclaredField(fieldName);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field.get(receiver);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getField(String className, Object receiver, String fieldName) {
|
|
||||||
Class<?> clazz = null;
|
|
||||||
Field field;
|
|
||||||
if (className != null && className.length() > 0) {
|
|
||||||
try {
|
|
||||||
clazz = Class.forName(className);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (receiver != null) {
|
|
||||||
clazz = receiver.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clazz == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
field = findField(clazz, fieldName);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field.get(receiver);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object setField(Object receiver, String fieldName, Object value) {
|
|
||||||
try {
|
|
||||||
Field field;
|
|
||||||
field = findField(receiver.getClass(), fieldName);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object old = field.get(receiver);
|
|
||||||
field.set(receiver, value);
|
|
||||||
return old;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object setField(Class<?> clazz, Object receiver, String fieldName, Object value) {
|
|
||||||
try {
|
|
||||||
Field field;
|
|
||||||
field = findField(clazz, fieldName);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object old = field.get(receiver);
|
|
||||||
field.set(receiver, value);
|
|
||||||
return old;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object callMethod(Object receiver, String methodName, Object... params) {
|
|
||||||
return callMethod(null, receiver, methodName, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object setField(String clazzName, Object receiver, String fieldName, Object value) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName(clazzName);
|
|
||||||
Field field;
|
|
||||||
field = findField(clazz, fieldName);
|
|
||||||
if (field == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object old = field.get(receiver);
|
|
||||||
field.set(receiver, value);
|
|
||||||
return old;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Object callMethod(String className, String methodName, Object... params) {
|
|
||||||
return callMethod(className, null, methodName, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object callMethod(Class<?> clazz, String className, String methodName, Object receiver,
|
|
||||||
Class[] types, Object... params) {
|
|
||||||
try {
|
|
||||||
if (clazz == null) {
|
|
||||||
clazz = Class.forName(className);
|
|
||||||
}
|
|
||||||
Method method = clazz.getDeclaredMethod(methodName, types);
|
|
||||||
method.setAccessible(true);
|
|
||||||
return method.invoke(receiver, params);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object callMethod(String className, Object receiver, String methodName, Object... params) {
|
|
||||||
Class<?> clazz = null;
|
|
||||||
if (className != null && className.length() > 0) {
|
|
||||||
try {
|
|
||||||
clazz = Class.forName(className);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (receiver != null) {
|
|
||||||
clazz = receiver.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clazz == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Method method = findMethod(clazz, methodName, params);
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
method.setAccessible(true);
|
|
||||||
return method.invoke(receiver, params);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Method findMethod(Class<?> clazz, String name, Object... arg) {
|
|
||||||
Method[] methods = clazz.getMethods();
|
|
||||||
Method method = null;
|
|
||||||
for (Method m : methods) {
|
|
||||||
if (methodFitParam(m, name, arg)) {
|
|
||||||
method = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == null) {
|
|
||||||
method = findDeclaredMethod(clazz, name, arg);
|
|
||||||
}
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Method findDeclaredMethod(Class<?> clazz, String name, Object... arg) {
|
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
|
||||||
Method method = null;
|
|
||||||
for (Method m : methods) {
|
|
||||||
if (methodFitParam(m, name, arg)) {
|
|
||||||
method = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == null) {
|
|
||||||
if (clazz.equals(Object.class)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return findDeclaredMethod(clazz.getSuperclass(), name, arg);
|
|
||||||
}
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean methodFitParam(Method method, String methodName, Object... arg) {
|
|
||||||
if (!methodName.equals(method.getName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?>[] paramTypes = method.getParameterTypes();
|
|
||||||
if (arg == null || arg.length == 0) {
|
|
||||||
return paramTypes == null || paramTypes.length == 0;
|
|
||||||
}
|
|
||||||
if (paramTypes.length != arg.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < arg.length; ++i) {
|
|
||||||
Object ar = arg[i];
|
|
||||||
Class<?> paramT = paramTypes[i];
|
|
||||||
if (ar == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO for primitive type
|
|
||||||
if (paramT.isPrimitive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paramT.isInstance(ar)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Field findField(Class<?> clazz, String name) {
|
|
||||||
try {
|
|
||||||
return clazz.getDeclaredField(name);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
if (clazz.equals(Object.class)) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Class<?> base = clazz.getSuperclass();
|
|
||||||
return findField(base, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue