remove useless again
This commit is contained in:
parent
0ba86283ea
commit
d2337bbd96
|
|
@ -15,6 +15,7 @@ dependencies {
|
|||
implementation project(':share')
|
||||
implementation 'commons-io:commons-io:2.10.0'
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -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.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.property.AttributeItem;
|
||||
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.IOUtils;
|
||||
import org.lsposed.lspatch.share.Constants;
|
||||
import org.lsposed.patch.base.BaseCommand;
|
||||
import org.lsposed.patch.task.BuildAndSignApkTask;
|
||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||
import org.lsposed.patch.util.ManifestParser;
|
||||
|
|
@ -32,26 +34,29 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LSPatch extends BaseCommand {
|
||||
public class LSPatch {
|
||||
|
||||
@Parameter(description = "apk path")
|
||||
private String apkPath;
|
||||
|
||||
private String unzipApkFilePath;
|
||||
|
||||
@Opt(opt = "o", longOpt = "output", description = "Output .apk file, default is " +
|
||||
"$source_apk_dir/[file-name]-xposed-signed.apk", argName = "file")
|
||||
@Parameter(names = "--help", help = true, order = 0)
|
||||
private boolean help = false;
|
||||
|
||||
@Parameter(names = {"-o", "--output"}, description = "Output apk file")
|
||||
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;
|
||||
|
||||
@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";
|
||||
|
||||
@Opt(opt = "d", longOpt = "debuggable", description = "Set 1 to make the app debuggable = true, " +
|
||||
"set 0 to make the app debuggable = false", argName = "0 or 1")
|
||||
private int debuggableFlag = -1; // 0: debuggable = false 1: debuggable = true
|
||||
@Parameter(names = {"-d", "--debuggable"}, description = "Set 1 to make the app debuggable = true, set 0 to make the app debuggable = false")
|
||||
private boolean debuggableFlag = false;
|
||||
|
||||
@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 dexFileCount = 0;
|
||||
|
|
@ -67,59 +72,48 @@ public class LSPatch extends BaseCommand {
|
|||
"lib/x86_64"
|
||||
};
|
||||
|
||||
public static void main(String... args) {
|
||||
new LSPatch().doMain(args);
|
||||
private static JCommander jCommander;
|
||||
|
||||
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) {
|
||||
if (!b) {
|
||||
throw new IllegalStateException("wtf", new Throwable("DUMPBT"));
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
public void doCommandLine() throws IOException {
|
||||
if (apkPath == null || apkPath.isEmpty()) {
|
||||
jCommander.usage();
|
||||
return;
|
||||
}
|
||||
|
||||
apkPath = remainingArgs[0];
|
||||
|
||||
File srcApkFile = new File(apkPath);
|
||||
|
||||
if (!srcApkFile.exists()) {
|
||||
System.out.println("The source apk file not exsit, please choose another one. " +
|
||||
"current apk file is = " + apkPath);
|
||||
System.out.println("The source apk file not exsit, please choose another one.");
|
||||
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));
|
||||
FileUtils.copyFile(srcApkFile, finalApk);
|
||||
|
||||
ZFile zFile = new ZFile(finalApk);
|
||||
ZFile zFile = ZFile.openReadWrite(finalApk);
|
||||
|
||||
String currentDir = new File(".").getAbsolutePath();
|
||||
System.out.println("currentDir: " + currentDir);
|
||||
System.out.println("apkPath: " + apkPath);
|
||||
System.out.println("work dir: " + currentDir);
|
||||
System.out.println("apk path: " + apkPath);
|
||||
|
||||
if (outputPath == null || outputPath.length() == 0) {
|
||||
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);
|
||||
if (outputFile.exists() && !forceOverwrite) {
|
||||
System.err.println(outputPath + " exists, use --force to overwrite");
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +124,7 @@ public class LSPatch extends BaseCommand {
|
|||
outputApkFileParentPath = absPath.substring(0, index);
|
||||
}
|
||||
|
||||
String apkFileName = getBaseName(srcApkFile);
|
||||
String apkFileName = srcApkFile.getName();
|
||||
|
||||
String tempFilePath = outputApkFileParentPath + File.separator +
|
||||
currentTimeStr() + "-tmp" + File.separator;
|
||||
|
|
@ -252,10 +246,7 @@ public class LSPatch extends BaseCommand {
|
|||
private void modifyManifestFile(String filePath, String dstFilePath) {
|
||||
ModificationProperty property = new ModificationProperty();
|
||||
|
||||
if (debuggableFlag >= 0) {
|
||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag != 0));
|
||||
}
|
||||
|
||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag));
|
||||
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
|
||||
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
|
||||
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
||||
LSPatch.fuckIfFail(unsignedApkFile.delete());
|
||||
if (!unsignedApkFile.delete()) {
|
||||
throw new IllegalStateException("wtf");
|
||||
}
|
||||
}
|
||||
}
|
||||
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