[core] Remove useless codes (#505)
* [core] Replace tabs with spaces * [core] Remove useless codes
This commit is contained in:
parent
86304f11ce
commit
d7897b67d9
|
|
@ -28,21 +28,29 @@ package de.robv.android.xposed;
|
||||||
* implemented anymore.
|
* implemented anymore.
|
||||||
*/
|
*/
|
||||||
public interface IXposedHookCmdInit extends IXposedMod {
|
public interface IXposedHookCmdInit extends IXposedMod {
|
||||||
/**
|
/**
|
||||||
* Called very early during startup of a command-line tool.
|
* Called very early during startup of a command-line tool.
|
||||||
* @param startupParam Details about the module itself and the started process.
|
*
|
||||||
* @throws Throwable Everything is caught, but it will prevent further initialization of the module.
|
* @param startupParam Details about the module itself and the started process.
|
||||||
*/
|
* @throws Throwable Everything is caught, but it will prevent further initialization of the module.
|
||||||
void initCmdApp(StartupParam startupParam) throws Throwable;
|
*/
|
||||||
|
void initCmdApp(StartupParam startupParam) throws Throwable;
|
||||||
|
|
||||||
/** Data holder for {@link #initCmdApp}. */
|
/**
|
||||||
final class StartupParam {
|
* Data holder for {@link #initCmdApp}.
|
||||||
/*package*/ StartupParam() {}
|
*/
|
||||||
|
final class StartupParam {
|
||||||
|
/*package*/ StartupParam() {
|
||||||
|
}
|
||||||
|
|
||||||
/** The path to the module's APK. */
|
/**
|
||||||
public String modulePath;
|
* The path to the module's APK.
|
||||||
|
*/
|
||||||
|
public String modulePath;
|
||||||
|
|
||||||
/** The class name of the tools that the hook was invoked for. */
|
/**
|
||||||
public String startClassName;
|
* The class name of the tools that the hook was invoked for.
|
||||||
}
|
*/
|
||||||
|
public String startClassName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,33 +33,35 @@ import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResou
|
||||||
* registering it as a callback automatically.
|
* registering it as a callback automatically.
|
||||||
*/
|
*/
|
||||||
public interface IXposedHookInitPackageResources extends IXposedMod {
|
public interface IXposedHookInitPackageResources extends IXposedMod {
|
||||||
/**
|
/**
|
||||||
* This method is called when resources for an app are being initialized.
|
* This method is called when resources for an app are being initialized.
|
||||||
* Modules can call special methods of the {@link XResources} class in order to replace resources.
|
* Modules can call special methods of the {@link XResources} class in order to replace resources.
|
||||||
*
|
*
|
||||||
* @param resparam Information about the resources.
|
* @param resparam Information about the resources.
|
||||||
* @throws Throwable Everything the callback throws is caught and logged.
|
* @throws Throwable Everything the callback throws is caught and logged.
|
||||||
*/
|
*/
|
||||||
void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;
|
void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
final class Wrapper extends XC_InitPackageResources {
|
* @hide
|
||||||
private final IXposedHookInitPackageResources instance;
|
*/
|
||||||
private final String apkPath;
|
final class Wrapper extends XC_InitPackageResources {
|
||||||
|
private final IXposedHookInitPackageResources instance;
|
||||||
|
private final String apkPath;
|
||||||
|
|
||||||
public Wrapper(IXposedHookInitPackageResources instance, String apkPath) {
|
public Wrapper(IXposedHookInitPackageResources instance, String apkPath) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.apkPath = apkPath;
|
this.apkPath = apkPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
|
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
|
||||||
instance.handleInitPackageResources(resparam);
|
instance.handleInitPackageResources(resparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApkPath() {
|
public String getApkPath() {
|
||||||
return apkPath;
|
return apkPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,33 +33,36 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
|
||||||
* registering it as a callback automatically.
|
* registering it as a callback automatically.
|
||||||
*/
|
*/
|
||||||
public interface IXposedHookLoadPackage extends IXposedMod {
|
public interface IXposedHookLoadPackage extends IXposedMod {
|
||||||
/**
|
/**
|
||||||
* This method is called when an app is loaded. It's called very early, even before
|
* This method is called when an app is loaded. It's called very early, even before
|
||||||
* {@link Application#onCreate} is called.
|
* {@link Application#onCreate} is called.
|
||||||
* Modules can set up their app-specific hooks here.
|
* Modules can set up their app-specific hooks here.
|
||||||
*
|
*
|
||||||
* @param lpparam Information about the app.
|
* @param lpparam Information about the app.
|
||||||
* @throws Throwable Everything the callback throws is caught and logged.
|
* @throws Throwable Everything the callback throws is caught and logged.
|
||||||
*/
|
*/
|
||||||
void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;
|
void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
final class Wrapper extends XC_LoadPackage {
|
* @hide
|
||||||
private final IXposedHookLoadPackage instance;
|
*/
|
||||||
private final String apkPath;
|
final class Wrapper extends XC_LoadPackage {
|
||||||
|
private final IXposedHookLoadPackage instance;
|
||||||
|
private final String apkPath;
|
||||||
|
|
||||||
public Wrapper(IXposedHookLoadPackage instance, String apkPath) {
|
public Wrapper(IXposedHookLoadPackage instance, String apkPath) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.apkPath = apkPath;
|
this.apkPath = apkPath;
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
|
|
||||||
instance.handleLoadPackage(lpparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApkPath() {
|
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
|
||||||
return apkPath;
|
instance.handleLoadPackage(lpparam);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public String getApkPath() {
|
||||||
|
return apkPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,16 +35,20 @@ import de.robv.android.xposed.callbacks.XCallback;
|
||||||
* <p>If you want to hook one/multiple specific apps, use {@link IXposedHookLoadPackage} instead.
|
* <p>If you want to hook one/multiple specific apps, use {@link IXposedHookLoadPackage} instead.
|
||||||
*/
|
*/
|
||||||
public interface IXposedHookZygoteInit extends IXposedMod {
|
public interface IXposedHookZygoteInit extends IXposedMod {
|
||||||
/**
|
/**
|
||||||
* Called very early during startup of Zygote.
|
* Called very early during startup of Zygote.
|
||||||
* @param startupParam Details about the module itself and the started process.
|
*
|
||||||
* @throws Throwable everything is caught, but will prevent further initialization of the module.
|
* @param startupParam Details about the module itself and the started process.
|
||||||
*/
|
* @throws Throwable everything is caught, but will prevent further initialization of the module.
|
||||||
void initZygote(StartupParam startupParam) throws Throwable;
|
*/
|
||||||
|
void initZygote(StartupParam startupParam) throws Throwable;
|
||||||
|
|
||||||
/** Data holder for {@link #initZygote}. */
|
/**
|
||||||
final class StartupParam extends XCallback.Param {
|
* Data holder for {@link #initZygote}.
|
||||||
/*package*/ StartupParam() {}
|
*/
|
||||||
|
final class StartupParam extends XCallback.Param {
|
||||||
|
/*package*/ StartupParam() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callbacks
|
* @param callbacks
|
||||||
|
|
@ -54,15 +58,17 @@ public interface IXposedHookZygoteInit extends IXposedMod {
|
||||||
super(callbacks);
|
super(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The path to the module's APK. */
|
/**
|
||||||
public String modulePath;
|
* The path to the module's APK.
|
||||||
|
*/
|
||||||
|
public String modulePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always {@code true} on 32-bit ROMs. On 64-bit, it's only {@code true} for the primary
|
* Always {@code true} on 32-bit ROMs. On 64-bit, it's only {@code true} for the primary
|
||||||
* process that starts the system_server.
|
* process that starts the system_server.
|
||||||
*/
|
*/
|
||||||
public boolean startsSystemServer;
|
public boolean startsSystemServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
|
|
@ -83,9 +89,9 @@ public interface IXposedHookZygoteInit extends IXposedMod {
|
||||||
instance.initZygote(this.startupParam);
|
instance.initZygote(this.startupParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApkPath() {
|
public String getApkPath() {
|
||||||
return startupParam.modulePath;
|
return startupParam.modulePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,8 @@
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
package de.robv.android.xposed;
|
||||||
|
|
||||||
/** Marker interface for Xposed modules. Cannot be implemented directly. */
|
/**
|
||||||
/* package */ interface IXposedMod {}
|
* Marker interface for Xposed modules. Cannot be implemented directly.
|
||||||
|
*/
|
||||||
|
/* package */ interface IXposedMod {
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,49 +27,50 @@ import de.robv.android.xposed.services.DirectAccessService;
|
||||||
* A helper to work with (or without) SELinux, abstracting much of its big complexity.
|
* A helper to work with (or without) SELinux, abstracting much of its big complexity.
|
||||||
*/
|
*/
|
||||||
public final class SELinuxHelper {
|
public final class SELinuxHelper {
|
||||||
private SELinuxHelper() {}
|
private SELinuxHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether SELinux is disabled or enabled.
|
* Determines whether SELinux is disabled or enabled.
|
||||||
*
|
*
|
||||||
* @return A boolean indicating whether SELinux is enabled.
|
* @return A boolean indicating whether SELinux is enabled.
|
||||||
*/
|
*/
|
||||||
public static boolean isSELinuxEnabled() {
|
public static boolean isSELinuxEnabled() {
|
||||||
// lsp: always enabled
|
// lsp: always enabled
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether SELinux is permissive or enforcing.
|
* Determines whether SELinux is permissive or enforcing.
|
||||||
*
|
*
|
||||||
* @return A boolean indicating whether SELinux is enforcing.
|
* @return A boolean indicating whether SELinux is enforcing.
|
||||||
*/
|
*/
|
||||||
public static boolean isSELinuxEnforced() {
|
public static boolean isSELinuxEnforced() {
|
||||||
// lsp: always enforcing
|
// lsp: always enforcing
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the security context of the current process.
|
* Gets the security context of the current process.
|
||||||
*
|
*
|
||||||
* @return A String representing the security context of the current process.
|
* @return A String representing the security context of the current process.
|
||||||
*/
|
*/
|
||||||
public static String getContext() {
|
public static String getContext() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the service to be used when accessing files in {@code /data/data/*}.
|
* Retrieve the service to be used when accessing files in {@code /data/data/*}.
|
||||||
*
|
*
|
||||||
* <p class="caution"><strong>IMPORTANT:</strong> If you call this from the Zygote process,
|
* <p class="caution"><strong>IMPORTANT:</strong> If you call this from the Zygote process,
|
||||||
* don't re-use the result in different process!
|
* don't re-use the result in different process!
|
||||||
*
|
*
|
||||||
* @return An instance of the service.
|
* @return An instance of the service.
|
||||||
*/
|
*/
|
||||||
public static BaseService getAppDataFileService() {
|
public static BaseService getAppDataFileService() {
|
||||||
return sServiceAppDataFile;
|
return sServiceAppDataFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final BaseService sServiceAppDataFile = new DirectAccessService(); // ed: initialized directly
|
private static final BaseService sServiceAppDataFile = new DirectAccessService(); // ed: initialized directly
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,157 +32,175 @@ import de.robv.android.xposed.callbacks.XCallback;
|
||||||
* {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}.
|
* {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}.
|
||||||
*/
|
*/
|
||||||
public abstract class XC_MethodHook extends XCallback {
|
public abstract class XC_MethodHook extends XCallback {
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with default priority.
|
* Creates a new callback with default priority.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public XC_MethodHook() {
|
public XC_MethodHook() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with a specific priority.
|
* Creates a new callback with a specific priority.
|
||||||
*
|
*
|
||||||
* <p class="note">Note that {@link #afterHookedMethod} will be called in reversed order, i.e.
|
* <p class="note">Note that {@link #afterHookedMethod} will be called in reversed order, i.e.
|
||||||
* the callback with the highest priority will be called last. This way, the callback has the
|
* the callback with the highest priority will be called last. This way, the callback has the
|
||||||
* final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e.
|
* final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e.
|
||||||
* highest priority first.
|
* highest priority first.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
*/
|
*/
|
||||||
public XC_MethodHook(int priority) {
|
public XC_MethodHook(int priority) {
|
||||||
super(priority);
|
super(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called before the invocation of the method.
|
* Called before the invocation of the method.
|
||||||
*
|
*
|
||||||
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
|
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
|
||||||
* to prevent the original method from being called.
|
* to prevent the original method from being called.
|
||||||
*
|
*
|
||||||
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
||||||
*
|
*
|
||||||
* @param param Information about the method call.
|
* @param param Information about the method call.
|
||||||
* @throws Throwable Everything the callback throws is caught and logged.
|
* @throws Throwable Everything the callback throws is caught and logged.
|
||||||
*/
|
*/
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {}
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
}
|
||||||
|
|
||||||
public void callBeforeHookedMethod(MethodHookParam param) throws Throwable {
|
public void callBeforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
beforeHookedMethod(param);
|
beforeHookedMethod(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after the invocation of the method.
|
* Called after the invocation of the method.
|
||||||
*
|
*
|
||||||
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
|
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
|
||||||
* to modify the return value of the original method.
|
* to modify the return value of the original method.
|
||||||
*
|
*
|
||||||
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
||||||
*
|
*
|
||||||
* @param param Information about the method call.
|
* @param param Information about the method call.
|
||||||
* @throws Throwable Everything the callback throws is caught and logged.
|
* @throws Throwable Everything the callback throws is caught and logged.
|
||||||
*/
|
*/
|
||||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {}
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
}
|
||||||
|
|
||||||
public void callAfterHookedMethod(MethodHookParam param) throws Throwable {
|
public void callAfterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
afterHookedMethod(param);
|
afterHookedMethod(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps information about the method call and allows to influence it.
|
* Wraps information about the method call and allows to influence it.
|
||||||
*/
|
*/
|
||||||
public static final class MethodHookParam extends XCallback.Param {
|
public static final class MethodHookParam extends XCallback.Param {
|
||||||
/** @hide */
|
/**
|
||||||
@SuppressWarnings("deprecation")
|
* @hide
|
||||||
public MethodHookParam() {
|
*/
|
||||||
super();
|
@SuppressWarnings("deprecation")
|
||||||
}
|
public MethodHookParam() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
/** The hooked method/constructor. */
|
/**
|
||||||
public Member method;
|
* The hooked method/constructor.
|
||||||
|
*/
|
||||||
|
public Member method;
|
||||||
|
|
||||||
/** The {@code this} reference for an instance method, or {@code null} for static methods. */
|
/**
|
||||||
public Object thisObject;
|
* The {@code this} reference for an instance method, or {@code null} for static methods.
|
||||||
|
*/
|
||||||
|
public Object thisObject;
|
||||||
|
|
||||||
/** Arguments to the method call. */
|
/**
|
||||||
public Object[] args;
|
* Arguments to the method call.
|
||||||
|
*/
|
||||||
|
public Object[] args;
|
||||||
|
|
||||||
private Object result = null;
|
private Object result = null;
|
||||||
private Throwable throwable = null;
|
private Throwable throwable = null;
|
||||||
public boolean returnEarly = false;
|
public boolean returnEarly = false;
|
||||||
|
|
||||||
/** Returns the result of the method call. */
|
/**
|
||||||
public Object getResult() {
|
* Returns the result of the method call.
|
||||||
return result;
|
*/
|
||||||
}
|
public Object getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify the result of the method call.
|
* Modify the result of the method call.
|
||||||
*
|
*
|
||||||
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
|
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
|
||||||
*/
|
*/
|
||||||
public void setResult(Object result) {
|
public void setResult(Object result) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.throwable = null;
|
this.throwable = null;
|
||||||
this.returnEarly = true;
|
this.returnEarly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the {@link Throwable} thrown by the method, or {@code null}. */
|
/**
|
||||||
public Throwable getThrowable() {
|
* Returns the {@link Throwable} thrown by the method, or {@code null}.
|
||||||
return throwable;
|
*/
|
||||||
}
|
public Throwable getThrowable() {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns true if an exception was thrown by the method. */
|
/**
|
||||||
public boolean hasThrowable() {
|
* Returns true if an exception was thrown by the method.
|
||||||
return throwable != null;
|
*/
|
||||||
}
|
public boolean hasThrowable() {
|
||||||
|
return throwable != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify the exception thrown of the method call.
|
* Modify the exception thrown of the method call.
|
||||||
*
|
*
|
||||||
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
|
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
|
||||||
*/
|
*/
|
||||||
public void setThrowable(Throwable throwable) {
|
public void setThrowable(Throwable throwable) {
|
||||||
this.throwable = throwable;
|
this.throwable = throwable;
|
||||||
this.result = null;
|
this.result = null;
|
||||||
this.returnEarly = true;
|
this.returnEarly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the result of the method call, or throws the Throwable caused by it. */
|
/**
|
||||||
public Object getResultOrThrowable() throws Throwable {
|
* Returns the result of the method call, or throws the Throwable caused by it.
|
||||||
if (throwable != null)
|
*/
|
||||||
throw throwable;
|
public Object getResultOrThrowable() throws Throwable {
|
||||||
return result;
|
if (throwable != null)
|
||||||
}
|
throw throwable;
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object with which the method/constructor can be unhooked.
|
* An object with which the method/constructor can be unhooked.
|
||||||
*/
|
*/
|
||||||
public class Unhook implements IXUnhook<XC_MethodHook> {
|
public class Unhook implements IXUnhook<XC_MethodHook> {
|
||||||
private final Member hookMethod;
|
private final Member hookMethod;
|
||||||
|
|
||||||
/*package*/ Unhook(Member hookMethod) {
|
/*package*/ Unhook(Member hookMethod) {
|
||||||
this.hookMethod = hookMethod;
|
this.hookMethod = hookMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the method/constructor that has been hooked.
|
* Returns the method/constructor that has been hooked.
|
||||||
*/
|
*/
|
||||||
public Member getHookedMethod() {
|
public Member getHookedMethod() {
|
||||||
return hookMethod;
|
return hookMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XC_MethodHook getCallback() {
|
public XC_MethodHook getCallback() {
|
||||||
return XC_MethodHook.this;
|
return XC_MethodHook.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void unhook() {
|
public void unhook() {
|
||||||
XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);
|
XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,83 +26,88 @@ import de.robv.android.xposed.callbacks.XCallback;
|
||||||
* A special case of {@link XC_MethodHook} which completely replaces the original method.
|
* A special case of {@link XC_MethodHook} which completely replaces the original method.
|
||||||
*/
|
*/
|
||||||
public abstract class XC_MethodReplacement extends XC_MethodHook {
|
public abstract class XC_MethodReplacement extends XC_MethodHook {
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with default priority.
|
* Creates a new callback with default priority.
|
||||||
*/
|
*/
|
||||||
public XC_MethodReplacement() {
|
public XC_MethodReplacement() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with a specific priority.
|
* Creates a new callback with a specific priority.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
*/
|
*/
|
||||||
public XC_MethodReplacement(int priority) {
|
public XC_MethodReplacement(int priority) {
|
||||||
super(priority);
|
super(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
*/
|
||||||
try {
|
@Override
|
||||||
Object result = replaceHookedMethod(param);
|
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
param.setResult(result);
|
try {
|
||||||
} catch (Throwable t) {
|
Object result = replaceHookedMethod(param);
|
||||||
XposedBridge.log(t);
|
param.setResult(result);
|
||||||
param.setThrowable(t);
|
} catch (Throwable t) {
|
||||||
}
|
XposedBridge.log(t);
|
||||||
}
|
param.setThrowable(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
@SuppressWarnings("EmptyMethod")
|
*/
|
||||||
protected final void afterHookedMethod(MethodHookParam param) throws Throwable {}
|
@Override
|
||||||
|
@SuppressWarnings("EmptyMethod")
|
||||||
|
protected final void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for replacing a method completely. Whatever is returned/thrown here is taken
|
* Shortcut for replacing a method completely. Whatever is returned/thrown here is taken
|
||||||
* instead of the result of the original method (which will not be called).
|
* instead of the result of the original method (which will not be called).
|
||||||
*
|
*
|
||||||
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
|
||||||
*
|
*
|
||||||
* @param param Information about the method call.
|
* @param param Information about the method call.
|
||||||
* @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.
|
* @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnusedParameters")
|
@SuppressWarnings("UnusedParameters")
|
||||||
protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
|
protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Predefined callback that skips the method without replacements.
|
* Predefined callback that skips the method without replacements.
|
||||||
*/
|
*/
|
||||||
public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST*2) {
|
public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST * 2) {
|
||||||
@Override
|
@Override
|
||||||
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
|
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a callback which always returns a specific value.
|
* Creates a callback which always returns a specific value.
|
||||||
*
|
*
|
||||||
* @param result The value that should be returned to callers of the hooked method.
|
* @param result The value that should be returned to callers of the hooked method.
|
||||||
*/
|
*/
|
||||||
public static XC_MethodReplacement returnConstant(final Object result) {
|
public static XC_MethodReplacement returnConstant(final Object result) {
|
||||||
return returnConstant(PRIORITY_DEFAULT, result);
|
return returnConstant(PRIORITY_DEFAULT, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.
|
* Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
* @param result The value that should be returned to callers of the hooked method.
|
* @param result The value that should be returned to callers of the hooked method.
|
||||||
*/
|
*/
|
||||||
public static XC_MethodReplacement returnConstant(int priority, final Object result) {
|
public static XC_MethodReplacement returnConstant(int priority, final Object result) {
|
||||||
return new XC_MethodReplacement(priority) {
|
return new XC_MethodReplacement(priority) {
|
||||||
@Override
|
@Override
|
||||||
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
|
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
|
|
||||||
import org.lsposed.lspd.BuildConfig;
|
import org.lsposed.lspd.BuildConfig;
|
||||||
import org.lsposed.lspd.util.MetaDataReader;
|
import org.lsposed.lspd.util.MetaDataReader;
|
||||||
|
|
||||||
|
|
@ -99,7 +100,8 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
Path dir = (Path) key.watchable();
|
Path dir = (Path) key.watchable();
|
||||||
Path path = dir.resolve((Path) event.context());
|
Path path = dir.resolve((Path) event.context());
|
||||||
String pathStr = path.toString();
|
String pathStr = path.toString();
|
||||||
if (BuildConfig.DEBUG) Log.v(TAG, "File " + path.toString() + " event: " + kind.name());
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.v(TAG, "File " + path.toString() + " event: " + kind.name());
|
||||||
// We react to both real and backup files due to rare race conditions
|
// We react to both real and backup files due to rare race conditions
|
||||||
if (pathStr.endsWith(".bak")) {
|
if (pathStr.endsWith(".bak")) {
|
||||||
if (kind != StandardWatchEventKinds.ENTRY_DELETE) {
|
if (kind != StandardWatchEventKinds.ENTRY_DELETE) {
|
||||||
|
|
@ -114,7 +116,8 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
try {
|
try {
|
||||||
l.onSharedPreferenceChanged(data.mPrefs, null);
|
l.onSharedPreferenceChanged(data.mPrefs, null);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (BuildConfig.DEBUG) Log.e(TAG, "Fail in preference change listener", t);
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.e(TAG, "Fail in preference change listener", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +186,7 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newModule) {
|
if (newModule) {
|
||||||
mFile = new File(serviceClient.getPrefsPath( packageName ), prefFileName + ".xml");
|
mFile = new File(serviceClient.getPrefsPath(packageName), prefFileName + ".xml");
|
||||||
} else {
|
} else {
|
||||||
mFile = new File(Environment.getDataDirectory(), "data/" + packageName + "/shared_prefs/" + prefFileName + ".xml");
|
mFile = new File(Environment.getDataDirectory(), "data/" + packageName + "/shared_prefs/" + prefFileName + ".xml");
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +212,8 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
if (sWatcherDaemon == null || !sWatcherDaemon.isAlive()) {
|
if (sWatcherDaemon == null || !sWatcherDaemon.isAlive()) {
|
||||||
initWatcherDaemon();
|
initWatcherDaemon();
|
||||||
}
|
}
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "tryRegisterWatcher: registered file watcher for " + path);
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(TAG, "tryRegisterWatcher: registered file watcher for " + path);
|
||||||
} catch (AccessDeniedException accDeniedEx) {
|
} catch (AccessDeniedException accDeniedEx) {
|
||||||
if (BuildConfig.DEBUG) Log.e(TAG, "tryRegisterWatcher: access denied to " + path);
|
if (BuildConfig.DEBUG) Log.e(TAG, "tryRegisterWatcher: access denied to " + path);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -232,7 +236,8 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
if (!atLeastOneValid) {
|
if (!atLeastOneValid) {
|
||||||
try {
|
try {
|
||||||
sWatcher.close();
|
sWatcher.close();
|
||||||
} catch (Exception ignore) { }
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -522,7 +527,7 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
if (listener == null)
|
if (listener == null)
|
||||||
throw new IllegalArgumentException("listener cannot be null");
|
throw new IllegalArgumentException("listener cannot be null");
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized (this) {
|
||||||
if (mListeners.put(listener, sContent) == null) {
|
if (mListeners.put(listener, sContent) == null) {
|
||||||
tryRegisterWatcher();
|
tryRegisterWatcher();
|
||||||
}
|
}
|
||||||
|
|
@ -537,7 +542,7 @@ public final class XSharedPreferences implements SharedPreferences {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||||
synchronized(this) {
|
synchronized (this) {
|
||||||
if (mListeners.remove(listener) != null && mListeners.isEmpty()) {
|
if (mListeners.remove(listener) != null && mListeners.isEmpty()) {
|
||||||
tryUnregisterWatcher();
|
tryUnregisterWatcher();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,16 @@
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
package de.robv.android.xposed;
|
||||||
|
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.setObjectField;
|
||||||
|
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.lsposed.lspd.BuildConfig;
|
import org.lsposed.lspd.BuildConfig;
|
||||||
|
import org.lsposed.lspd.nativebridge.ModuleLogger;
|
||||||
|
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
||||||
|
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
|
|
@ -43,12 +48,6 @@ import java.util.Set;
|
||||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
||||||
import de.robv.android.xposed.callbacks.XC_InitZygote;
|
import de.robv.android.xposed.callbacks.XC_InitZygote;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import org.lsposed.lspd.nativebridge.ModuleLogger;
|
|
||||||
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
|
||||||
import org.lsposed.lspd.yahfa.dexmaker.DynamicBridge;
|
|
||||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
|
||||||
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.setObjectField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains most of Xposed's central logic, such as initialization and callbacks used by
|
* This class contains most of Xposed's central logic, such as initialization and callbacks used by
|
||||||
|
|
@ -56,377 +55,373 @@ import static de.robv.android.xposed.XposedHelpers.setObjectField;
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("JniMissingFunction")
|
@SuppressWarnings("JniMissingFunction")
|
||||||
public final class XposedBridge {
|
public final class XposedBridge {
|
||||||
/**
|
/**
|
||||||
* The system class loader which can be used to locate Android framework classes.
|
* The system class loader which can be used to locate Android framework classes.
|
||||||
* Application classes cannot be retrieved from it.
|
* Application classes cannot be retrieved from it.
|
||||||
*
|
*
|
||||||
* @see ClassLoader#getSystemClassLoader
|
* @see ClassLoader#getSystemClassLoader
|
||||||
*/
|
*/
|
||||||
public static final ClassLoader BOOTCLASSLOADER = XposedBridge.class.getClassLoader();
|
public static final ClassLoader BOOTCLASSLOADER = XposedBridge.class.getClassLoader();
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
public static final String TAG = "LSPosed-Bridge";
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String TAG = "LSPosed-Bridge";
|
||||||
|
|
||||||
/** @deprecated Use {@link #getXposedVersion()} instead. */
|
/**
|
||||||
@Deprecated
|
* @deprecated Use {@link #getXposedVersion()} instead.
|
||||||
public static int XPOSED_BRIDGE_VERSION;
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int XPOSED_BRIDGE_VERSION;
|
||||||
|
|
||||||
/*package*/ static boolean isZygote = true; // ed: RuntimeInit.main() tool process not supported yet
|
private static final Object[] EMPTY_ARRAY = new Object[0];
|
||||||
|
|
||||||
// This field is set "magically" on MIUI.
|
// built-in handlers
|
||||||
/*package*/ static long BOOT_START_TIME;
|
private static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new NoValuesHashMap<>();
|
||||||
|
public static final CopyOnWriteSortedSet<XC_LoadPackage> sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>();
|
||||||
|
/*package*/ static final CopyOnWriteSortedSet<XC_InitPackageResources> sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>();
|
||||||
|
/*package*/ static final CopyOnWriteSortedSet<XC_InitZygote> sInitZygoteCallbacks = new CopyOnWriteSortedSet<>();
|
||||||
|
|
||||||
private static final Object[] EMPTY_ARRAY = new Object[0];
|
private XposedBridge() {
|
||||||
|
}
|
||||||
|
|
||||||
// built-in handlers
|
public static volatile ClassLoader dummyClassLoader = null;
|
||||||
private static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new NoValuesHashMap<>();
|
|
||||||
public static final CopyOnWriteSortedSet<XC_LoadPackage> sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>();
|
|
||||||
/*package*/ static final CopyOnWriteSortedSet<XC_InitPackageResources> sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>();
|
|
||||||
/*package*/ static final CopyOnWriteSortedSet<XC_InitZygote> sInitZygoteCallbacks = new CopyOnWriteSortedSet<>();
|
|
||||||
|
|
||||||
private XposedBridge() {}
|
public static void initXResources() {
|
||||||
|
|
||||||
public static volatile ClassLoader dummyClassLoader = null;
|
|
||||||
|
|
||||||
public static void initXResources() {
|
|
||||||
if (dummyClassLoader != null) {
|
if (dummyClassLoader != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Resources res = Resources.getSystem();
|
Resources res = Resources.getSystem();
|
||||||
Class<?> resClass = res.getClass();
|
Class<?> resClass = res.getClass();
|
||||||
Class<?> taClass = TypedArray.class;
|
Class<?> taClass = TypedArray.class;
|
||||||
try {
|
try {
|
||||||
TypedArray ta = res.obtainTypedArray(res.getIdentifier(
|
TypedArray ta = res.obtainTypedArray(res.getIdentifier(
|
||||||
"preloaded_drawables", "array", "android"));
|
"preloaded_drawables", "array", "android"));
|
||||||
taClass = ta.getClass();
|
taClass = ta.getClass();
|
||||||
ta.recycle();
|
ta.recycle();
|
||||||
} catch (Resources.NotFoundException nfe) {
|
} catch (Resources.NotFoundException nfe) {
|
||||||
XposedBridge.log(nfe);
|
XposedBridge.log(nfe);
|
||||||
}
|
}
|
||||||
ResourcesHook.removeFinalFlagNative(resClass);
|
ResourcesHook.removeFinalFlagNative(resClass);
|
||||||
ResourcesHook.removeFinalFlagNative(taClass);
|
ResourcesHook.removeFinalFlagNative(taClass);
|
||||||
ClassLoader myCL = XposedBridge.class.getClassLoader();
|
ClassLoader myCL = XposedBridge.class.getClassLoader();
|
||||||
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
|
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
|
||||||
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
|
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
|
||||||
dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass");
|
dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass");
|
||||||
setObjectField(myCL, "parent", dummyClassLoader);
|
setObjectField(myCL, "parent", dummyClassLoader);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
XposedBridge.log(throwable);
|
XposedBridge.log(throwable);
|
||||||
XposedInit.disableResources = true;
|
XposedInit.disableResources = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the currently installed version of the Xposed framework.
|
* Returns the currently installed version of the Xposed framework.
|
||||||
*/
|
*/
|
||||||
public static int getXposedVersion() {
|
public static int getXposedVersion() {
|
||||||
return BuildConfig.API_CODE;
|
return BuildConfig.API_CODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a message to the Xposed modules log.
|
* Writes a message to the Xposed modules log.
|
||||||
*
|
*
|
||||||
* <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.
|
* <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.
|
||||||
* If you want to write information/debug messages, use logcat.
|
* If you want to write information/debug messages, use logcat.
|
||||||
*
|
*
|
||||||
* @param text The log message.
|
* @param text The log message.
|
||||||
*/
|
*/
|
||||||
public synchronized static void log(String text) {
|
public synchronized static void log(String text) {
|
||||||
Log.i(TAG, text);
|
Log.i(TAG, text);
|
||||||
ModuleLogger.log(text, false);
|
ModuleLogger.log(text, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a stack trace to the Xposed modules log.
|
* Logs a stack trace to the Xposed modules log.
|
||||||
*
|
*
|
||||||
* <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.
|
* <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.
|
||||||
* If you want to write information/debug messages, use logcat.
|
* If you want to write information/debug messages, use logcat.
|
||||||
*
|
*
|
||||||
* @param t The Throwable object for the stack trace.
|
* @param t The Throwable object for the stack trace.
|
||||||
*/
|
*/
|
||||||
public synchronized static void log(Throwable t) {
|
public synchronized static void log(Throwable t) {
|
||||||
String logStr = Log.getStackTraceString(t);
|
String logStr = Log.getStackTraceString(t);
|
||||||
Log.e(TAG, logStr);
|
Log.e(TAG, logStr);
|
||||||
ModuleLogger.log(logStr, true);
|
ModuleLogger.log(logStr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook any method (or constructor) with the specified callback. See below for some wrappers
|
* Hook any method (or constructor) with the specified callback. See below for some wrappers
|
||||||
* that make it easier to find a method/constructor in one step.
|
* that make it easier to find a method/constructor in one step.
|
||||||
*
|
*
|
||||||
* @param hookMethod The method to be hooked.
|
* @param hookMethod The method to be hooked.
|
||||||
* @param callback The callback to be executed when the hooked method is called.
|
* @param callback The callback to be executed when the hooked method is called.
|
||||||
* @return An object that can be used to remove the hook.
|
* @return An object that can be used to remove the hook.
|
||||||
*
|
* @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
|
||||||
* @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
|
* @see XposedHelpers#findAndHookMethod(Class, String, Object...)
|
||||||
* @see XposedHelpers#findAndHookMethod(Class, String, Object...)
|
* @see #hookAllMethods
|
||||||
* @see #hookAllMethods
|
* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
|
||||||
* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
|
* @see XposedHelpers#findAndHookConstructor(Class, Object...)
|
||||||
* @see XposedHelpers#findAndHookConstructor(Class, Object...)
|
* @see #hookAllConstructors
|
||||||
* @see #hookAllConstructors
|
*/
|
||||||
*/
|
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
|
||||||
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
|
if (!(hookMethod instanceof Executable)) {
|
||||||
if (!(hookMethod instanceof Executable)) {
|
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
|
||||||
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
|
}
|
||||||
}
|
// No check interface because there may be default methods
|
||||||
// No check interface because there may be default methods
|
|
||||||
/*else if (hookMethod.getDeclaringClass().isInterface()) {
|
/*else if (hookMethod.getDeclaringClass().isInterface()) {
|
||||||
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
|
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
|
||||||
}*/
|
}*/
|
||||||
else if (Modifier.isAbstract(hookMethod.getModifiers())) {
|
else if (Modifier.isAbstract(hookMethod.getModifiers())) {
|
||||||
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
|
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Executable targetMethod = (Executable) hookMethod;
|
Executable targetMethod = (Executable) hookMethod;
|
||||||
|
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
throw new IllegalArgumentException("callback should not be null!");
|
throw new IllegalArgumentException("callback should not be null!");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newMethod = false;
|
boolean newMethod = false;
|
||||||
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
||||||
synchronized (sHookedMethodCallbacks) {
|
synchronized (sHookedMethodCallbacks) {
|
||||||
callbacks = sHookedMethodCallbacks.get(targetMethod);
|
callbacks = sHookedMethodCallbacks.get(targetMethod);
|
||||||
if (callbacks == null) {
|
if (callbacks == null) {
|
||||||
callbacks = new CopyOnWriteSortedSet<>();
|
callbacks = new CopyOnWriteSortedSet<>();
|
||||||
sHookedMethodCallbacks.put(targetMethod, callbacks);
|
sHookedMethodCallbacks.put(targetMethod, callbacks);
|
||||||
newMethod = true;
|
newMethod = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callbacks.add(callback);
|
callbacks.add(callback);
|
||||||
|
|
||||||
if (newMethod) {
|
if (newMethod) {
|
||||||
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
|
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
|
||||||
if (!YahfaHooker.shouldDelayHook(targetMethod)) {
|
if (!YahfaHooker.shouldDelayHook(targetMethod)) {
|
||||||
YahfaHooker.hookMethod(targetMethod, additionalInfo);
|
YahfaHooker.hookMethod(targetMethod, additionalInfo);
|
||||||
} else {
|
} else {
|
||||||
PendingHooks.recordPendingMethod((Method)hookMethod, additionalInfo);
|
PendingHooks.recordPendingMethod((Method) hookMethod, additionalInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback.new Unhook(hookMethod);
|
return callback.new Unhook(hookMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the callback for a hooked method/constructor.
|
* Removes the callback for a hooked method/constructor.
|
||||||
*
|
*
|
||||||
* @deprecated Use {@link XC_MethodHook.Unhook#unhook} instead. An instance of the {@code Unhook}
|
* @param hookMethod The method for which the callback should be removed.
|
||||||
* class is returned when you hook the method.
|
* @param callback The reference to the callback as specified in {@link #hookMethod}.
|
||||||
*
|
* @deprecated Use {@link XC_MethodHook.Unhook#unhook} instead. An instance of the {@code Unhook}
|
||||||
* @param hookMethod The method for which the callback should be removed.
|
* class is returned when you hook the method.
|
||||||
* @param callback The reference to the callback as specified in {@link #hookMethod}.
|
*/
|
||||||
*/
|
@Deprecated
|
||||||
@Deprecated
|
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
|
||||||
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
|
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
||||||
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
synchronized (sHookedMethodCallbacks) {
|
||||||
synchronized (sHookedMethodCallbacks) {
|
callbacks = sHookedMethodCallbacks.get(hookMethod);
|
||||||
callbacks = sHookedMethodCallbacks.get(hookMethod);
|
if (callbacks == null)
|
||||||
if (callbacks == null)
|
return;
|
||||||
return;
|
}
|
||||||
}
|
callbacks.remove(callback);
|
||||||
callbacks.remove(callback);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks all methods with a certain name that were declared in the specified class. Inherited
|
* Hooks all methods with a certain name that were declared in the specified class. Inherited
|
||||||
* methods and constructors are not considered. For constructors, use
|
* methods and constructors are not considered. For constructors, use
|
||||||
* {@link #hookAllConstructors} instead.
|
* {@link #hookAllConstructors} instead.
|
||||||
*
|
*
|
||||||
* @param hookClass The class to check for declared methods.
|
* @param hookClass The class to check for declared methods.
|
||||||
* @param methodName The name of the method(s) to hook.
|
* @param methodName The name of the method(s) to hook.
|
||||||
* @param callback The callback to be executed when the hooked methods are called.
|
* @param callback The callback to be executed when the hooked methods are called.
|
||||||
* @return A set containing one object for each found method which can be used to unhook it.
|
* @return A set containing one object for each found method which can be used to unhook it.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
|
public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
|
||||||
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
|
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
|
||||||
for (Member method : hookClass.getDeclaredMethods())
|
for (Member method : hookClass.getDeclaredMethods())
|
||||||
if (method.getName().equals(methodName))
|
if (method.getName().equals(methodName))
|
||||||
unhooks.add(hookMethod(method, callback));
|
unhooks.add(hookMethod(method, callback));
|
||||||
return unhooks;
|
return unhooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook all constructors of the specified class.
|
* Hook all constructors of the specified class.
|
||||||
*
|
*
|
||||||
* @param hookClass The class to check for constructors.
|
* @param hookClass The class to check for constructors.
|
||||||
* @param callback The callback to be executed when the hooked constructors are called.
|
* @param callback The callback to be executed when the hooked constructors are called.
|
||||||
* @return A set containing one object for each found constructor which can be used to unhook it.
|
* @return A set containing one object for each found constructor which can be used to unhook it.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {
|
public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {
|
||||||
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
|
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
|
||||||
for (Member constructor : hookClass.getDeclaredConstructors())
|
for (Member constructor : hookClass.getDeclaredConstructors())
|
||||||
unhooks.add(hookMethod(constructor, callback));
|
unhooks.add(hookMethod(constructor, callback));
|
||||||
return unhooks;
|
return unhooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to be executed when an app ("Android package") is loaded.
|
* Adds a callback to be executed when an app ("Android package") is loaded.
|
||||||
*
|
*
|
||||||
* <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookLoadPackage}
|
* <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookLoadPackage}
|
||||||
* in your module class and Xposed will take care of registering it as a callback.
|
* in your module class and Xposed will take care of registering it as a callback.
|
||||||
*
|
*
|
||||||
* @param callback The callback to be executed.
|
* @param callback The callback to be executed.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public static void hookLoadPackage(XC_LoadPackage callback) {
|
public static void hookLoadPackage(XC_LoadPackage callback) {
|
||||||
synchronized (sLoadedPackageCallbacks) {
|
synchronized (sLoadedPackageCallbacks) {
|
||||||
sLoadedPackageCallbacks.add(callback);
|
sLoadedPackageCallbacks.add(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearLoadedPackages() {
|
public static void clearLoadedPackages() {
|
||||||
synchronized (sLoadedPackageCallbacks) {
|
synchronized (sLoadedPackageCallbacks) {
|
||||||
sLoadedPackageCallbacks.clear();
|
sLoadedPackageCallbacks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to be executed when the resources for an app are initialized.
|
* Adds a callback to be executed when the resources for an app are initialized.
|
||||||
*
|
*
|
||||||
* <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookInitPackageResources}
|
* <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookInitPackageResources}
|
||||||
* in your module class and Xposed will take care of registering it as a callback.
|
* in your module class and Xposed will take care of registering it as a callback.
|
||||||
*
|
*
|
||||||
* @param callback The callback to be executed.
|
* @param callback The callback to be executed.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public static void hookInitPackageResources(XC_InitPackageResources callback) {
|
public static void hookInitPackageResources(XC_InitPackageResources callback) {
|
||||||
synchronized (sInitPackageResourcesCallbacks) {
|
synchronized (sInitPackageResourcesCallbacks) {
|
||||||
sInitPackageResourcesCallbacks.add(callback);
|
sInitPackageResourcesCallbacks.add(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void hookInitZygote(XC_InitZygote callback) {
|
public static void hookInitZygote(XC_InitZygote callback) {
|
||||||
synchronized (sInitZygoteCallbacks) {
|
synchronized (sInitZygoteCallbacks) {
|
||||||
sInitZygoteCallbacks.add(callback);
|
sInitZygoteCallbacks.add(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basically the same as {@link Method#invoke}, but calls the original method
|
* Basically the same as {@link Method#invoke}, but calls the original method
|
||||||
* as it was before the interception by Xposed. Also, access permissions are not checked.
|
* as it was before the interception by Xposed. Also, access permissions are not checked.
|
||||||
*
|
*
|
||||||
* <p class="caution">There are very few cases where this method is needed. A common mistake is
|
* <p class="caution">There are very few cases where this method is needed. A common mistake is
|
||||||
* to replace a method and then invoke the original one based on dynamic conditions. This
|
* to replace a method and then invoke the original one based on dynamic conditions. This
|
||||||
* creates overhead and skips further hooks by other modules. Instead, just hook (don't replace)
|
* creates overhead and skips further hooks by other modules. Instead, just hook (don't replace)
|
||||||
* the method and call {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod}
|
* the method and call {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod}
|
||||||
* if the original method should be skipped.
|
* if the original method should be skipped.
|
||||||
*
|
*
|
||||||
* @param method The method to be called.
|
* @param method The method to be called.
|
||||||
* @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.
|
* @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.
|
||||||
* @param args Arguments for the method call as Object[] array.
|
* @param args Arguments for the method call as Object[] array.
|
||||||
* @return The result returned from the invoked method.
|
* @return The result returned from the invoked method.
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if {@code receiver == null} for a non-static method
|
||||||
* if {@code receiver == null} for a non-static method
|
* @throws IllegalAccessException if this method is not accessible (see {@link AccessibleObject})
|
||||||
* @throws IllegalAccessException
|
* @throws IllegalArgumentException if the number of arguments doesn't match the number of parameters, the receiver
|
||||||
* if this method is not accessible (see {@link AccessibleObject})
|
* is incompatible with the declaring class, or an argument could not be unboxed
|
||||||
* @throws IllegalArgumentException
|
* or converted by a widening conversion to the corresponding parameter type
|
||||||
* if the number of arguments doesn't match the number of parameters, the receiver
|
* @throws InvocationTargetException if an exception was thrown by the invoked method
|
||||||
* is incompatible with the declaring class, or an argument could not be unboxed
|
*/
|
||||||
* or converted by a widening conversion to the corresponding parameter type
|
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
|
||||||
* @throws InvocationTargetException
|
throws Throwable {
|
||||||
* if an exception was thrown by the invoked method
|
if (args == null) {
|
||||||
*/
|
args = EMPTY_ARRAY;
|
||||||
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
|
}
|
||||||
throws Throwable {
|
|
||||||
if (args == null) {
|
|
||||||
args = EMPTY_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(method instanceof Executable)) {
|
if (!(method instanceof Executable)) {
|
||||||
throw new IllegalArgumentException("method must be of type Method or Constructor");
|
throw new IllegalArgumentException("method must be of type Method or Constructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
return YahfaHooker.invokeOriginalMethod((Executable) method, thisObject, args);
|
return YahfaHooker.invokeOriginalMethod((Executable) method, thisObject, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NoValuesHashMap<K,V> extends HashMap<K,V> {
|
private static class NoValuesHashMap<K, V> extends HashMap<K, V> {
|
||||||
@Override
|
@Override
|
||||||
public Collection values() {
|
public Collection values() {
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<K> keySet() {
|
public Set<K> keySet() {
|
||||||
return Collections.EMPTY_SET;
|
return Collections.EMPTY_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<K, V>> entrySet() {
|
public Set<Entry<K, V>> entrySet() {
|
||||||
return Collections.EMPTY_SET;
|
return Collections.EMPTY_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
public static final class CopyOnWriteSortedSet<E> {
|
* @hide
|
||||||
private transient volatile Object[] elements = EMPTY_ARRAY;
|
*/
|
||||||
|
public static final class CopyOnWriteSortedSet<E> {
|
||||||
|
private transient volatile Object[] elements = EMPTY_ARRAY;
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public synchronized boolean add(E e) {
|
public synchronized boolean add(E e) {
|
||||||
int index = indexOf(e);
|
int index = indexOf(e);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Object[] newElements = new Object[elements.length + 1];
|
Object[] newElements = new Object[elements.length + 1];
|
||||||
System.arraycopy(elements, 0, newElements, 0, elements.length);
|
System.arraycopy(elements, 0, newElements, 0, elements.length);
|
||||||
newElements[elements.length] = e;
|
newElements[elements.length] = e;
|
||||||
Arrays.sort(newElements);
|
Arrays.sort(newElements);
|
||||||
elements = newElements;
|
elements = newElements;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public synchronized boolean remove(E e) {
|
public synchronized boolean remove(E e) {
|
||||||
int index = indexOf(e);
|
int index = indexOf(e);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Object[] newElements = new Object[elements.length - 1];
|
Object[] newElements = new Object[elements.length - 1];
|
||||||
System.arraycopy(elements, 0, newElements, 0, index);
|
System.arraycopy(elements, 0, newElements, 0, index);
|
||||||
System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);
|
System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);
|
||||||
elements = newElements;
|
elements = newElements;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int indexOf(Object o) {
|
private int indexOf(Object o) {
|
||||||
for (int i = 0; i < elements.length; i++) {
|
for (int i = 0; i < elements.length; i++) {
|
||||||
if (o.equals(elements[i]))
|
if (o.equals(elements[i]))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getSnapshot() {
|
public Object[] getSnapshot() {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clear() {
|
public synchronized void clear() {
|
||||||
elements = EMPTY_ARRAY;
|
elements = EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AdditionalHookInfo {
|
public static class AdditionalHookInfo {
|
||||||
public final CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
public final CopyOnWriteSortedSet<XC_MethodHook> callbacks;
|
||||||
|
|
||||||
private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks) {
|
private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,6 +20,18 @@
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
package de.robv.android.xposed;
|
||||||
|
|
||||||
|
import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sInitZygoteCallbacks;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sLoadedPackageCallbacks;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.closeSilently;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.getObjectField;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.getParameterIndexByType;
|
||||||
|
import static de.robv.android.xposed.XposedHelpers.setStaticObjectField;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
|
@ -32,7 +44,8 @@ import android.os.Process;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.os.ZygoteInit;
|
import org.lsposed.lspd.nativebridge.NativeAPI;
|
||||||
|
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -52,56 +65,14 @@ import de.robv.android.xposed.callbacks.XC_InitZygote;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import de.robv.android.xposed.callbacks.XCallback;
|
import de.robv.android.xposed.callbacks.XCallback;
|
||||||
import hidden.HiddenApiBridge;
|
import hidden.HiddenApiBridge;
|
||||||
import org.lsposed.lspd.nativebridge.NativeAPI;
|
|
||||||
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
|
||||||
|
|
||||||
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
|
|
||||||
import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks;
|
|
||||||
import static de.robv.android.xposed.XposedBridge.sInitZygoteCallbacks;
|
|
||||||
import static de.robv.android.xposed.XposedBridge.sLoadedPackageCallbacks;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.closeSilently;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.findClass;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.findFieldIfExists;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.getObjectField;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.getParameterIndexByType;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.setStaticBooleanField;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.setStaticLongField;
|
|
||||||
import static de.robv.android.xposed.XposedHelpers.setStaticObjectField;
|
|
||||||
import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
|
||||||
|
|
||||||
public final class XposedInit {
|
public final class XposedInit {
|
||||||
private static final String TAG = XposedBridge.TAG;
|
private static final String TAG = XposedBridge.TAG;
|
||||||
public static boolean startsSystemServer = false;
|
public static boolean startsSystemServer = false;
|
||||||
private static final String startClassName = ""; // ed: no support for tool process anymore
|
|
||||||
|
|
||||||
public static volatile boolean disableResources = false;
|
public static volatile boolean disableResources = false;
|
||||||
|
|
||||||
private XposedInit() {
|
public static void hookResources() throws Throwable {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook some methods which we want to create an easier interface for developers.
|
|
||||||
*/
|
|
||||||
/*package*/
|
|
||||||
public static void initForZygote() throws Throwable {
|
|
||||||
// TODO Are these still needed for us?
|
|
||||||
// MIUI
|
|
||||||
if (findFieldIfExists(ZygoteInit.class, "BOOT_START_TIME") != null) {
|
|
||||||
setStaticLongField(ZygoteInit.class, "BOOT_START_TIME", XposedBridge.BOOT_START_TIME);
|
|
||||||
}
|
|
||||||
// Samsung
|
|
||||||
Class<?> zygote = findClass("com.android.internal.os.Zygote", null);
|
|
||||||
try {
|
|
||||||
setStaticBooleanField(zygote, "isEnhancedZygoteASLREnabled", false);
|
|
||||||
} catch (NoSuchFieldError ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
hookResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void hookResources() throws Throwable {
|
|
||||||
if (!serviceClient.isResourcesHookEnabled() || disableResources) {
|
if (!serviceClient.isResourcesHookEnabled() || disableResources) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +213,7 @@ public final class XposedInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean loadModules(boolean callInitZygote) throws IOException {
|
public static boolean loadModules() throws IOException {
|
||||||
boolean hasLoaded = !modulesLoaded.compareAndSet(false, true);
|
boolean hasLoaded = !modulesLoaded.compareAndSet(false, true);
|
||||||
if (hasLoaded) {
|
if (hasLoaded) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -256,7 +227,7 @@ public final class XposedInit {
|
||||||
newLoadedApk.add(apk);
|
newLoadedApk.add(apk);
|
||||||
} else {
|
} else {
|
||||||
loadedModules.add(apk); // temporarily add it for XSharedPreference
|
loadedModules.add(apk); // temporarily add it for XSharedPreference
|
||||||
boolean loadSuccess = loadModule(apk, callInitZygote);
|
boolean loadSuccess = loadModule(apk);
|
||||||
if (loadSuccess) {
|
if (loadSuccess) {
|
||||||
newLoadedApk.add(apk);
|
newLoadedApk.add(apk);
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +297,7 @@ public final class XposedInit {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean initModule(ClassLoader mcl, String apk, boolean callInitZygote) {
|
private static boolean initModule(ClassLoader mcl, String apk) {
|
||||||
InputStream is = mcl.getResourceAsStream("assets/xposed_init");
|
InputStream is = mcl.getResourceAsStream("assets/xposed_init");
|
||||||
if (is == null) {
|
if (is == null) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -359,9 +330,7 @@ public final class XposedInit {
|
||||||
|
|
||||||
XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper(
|
XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper(
|
||||||
(IXposedHookZygoteInit) moduleInstance, param));
|
(IXposedHookZygoteInit) moduleInstance, param));
|
||||||
if (callInitZygote) {
|
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
|
||||||
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleInstance instanceof IXposedHookLoadPackage)
|
if (moduleInstance instanceof IXposedHookLoadPackage)
|
||||||
|
|
@ -390,7 +359,7 @@ public final class XposedInit {
|
||||||
* in <code>assets/xposed_init</code>.
|
* in <code>assets/xposed_init</code>.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
private static boolean loadModule(String apk, boolean callInitZygote) {
|
private static boolean loadModule(String apk) {
|
||||||
Log.i(TAG, "Loading modules from " + apk);
|
Log.i(TAG, "Loading modules from " + apk);
|
||||||
|
|
||||||
if (!new File(apk).exists()) {
|
if (!new File(apk).exists()) {
|
||||||
|
|
@ -418,7 +387,7 @@ public final class XposedInit {
|
||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean res = initModule(mcl, apk, callInitZygote);
|
boolean res = initModule(mcl, apk);
|
||||||
res = res && initNativeModule(mcl, apk);
|
res = res && initNativeModule(mcl, apk);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ import de.robv.android.xposed.IXposedHookZygoteInit;
|
||||||
* @param <T> The class of the callback.
|
* @param <T> The class of the callback.
|
||||||
*/
|
*/
|
||||||
public interface IXUnhook<T> {
|
public interface IXUnhook<T> {
|
||||||
/**
|
/**
|
||||||
* Returns the callback that has been registered.
|
* Returns the callback that has been registered.
|
||||||
*/
|
*/
|
||||||
T getCallback();
|
T getCallback();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the callback.
|
* Removes the callback.
|
||||||
*/
|
*/
|
||||||
void unhook();
|
void unhook();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,48 +30,55 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
* subclass.
|
* subclass.
|
||||||
*/
|
*/
|
||||||
public abstract class XC_InitPackageResources extends XCallback implements IXposedHookInitPackageResources {
|
public abstract class XC_InitPackageResources extends XCallback implements IXposedHookInitPackageResources {
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with default priority.
|
* Creates a new callback with default priority.
|
||||||
* @hide
|
*
|
||||||
*/
|
* @hide
|
||||||
@SuppressWarnings("deprecation")
|
*/
|
||||||
public XC_InitPackageResources() {
|
@SuppressWarnings("deprecation")
|
||||||
super();
|
public XC_InitPackageResources() {
|
||||||
}
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with a specific priority.
|
* Creates a new callback with a specific priority.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public XC_InitPackageResources(int priority) {
|
public XC_InitPackageResources(int priority) {
|
||||||
super(priority);
|
super(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps information about the resources being initialized.
|
* Wraps information about the resources being initialized.
|
||||||
*/
|
*/
|
||||||
public static final class InitPackageResourcesParam extends XCallback.Param {
|
public static final class InitPackageResourcesParam extends XCallback.Param {
|
||||||
/** @hide */
|
/**
|
||||||
public InitPackageResourcesParam(CopyOnWriteSortedSet<XC_InitPackageResources> callbacks) {
|
* @hide
|
||||||
super(callbacks);
|
*/
|
||||||
}
|
public InitPackageResourcesParam(CopyOnWriteSortedSet<XC_InitPackageResources> callbacks) {
|
||||||
|
super(callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
/** The name of the package for which resources are being loaded. */
|
/**
|
||||||
public String packageName;
|
* The name of the package for which resources are being loaded.
|
||||||
|
*/
|
||||||
|
public String packageName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the resources that can be used for calls to
|
* Reference to the resources that can be used for calls to
|
||||||
* {@link XResources#setReplacement(String, String, String, Object)}.
|
* {@link XResources#setReplacement(String, String, String, Object)}.
|
||||||
*/
|
*/
|
||||||
public XResources res;
|
public XResources res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
protected void call(Param param) throws Throwable {
|
*/
|
||||||
if (param instanceof InitPackageResourcesParam)
|
@Override
|
||||||
handleInitPackageResources((InitPackageResourcesParam) param);
|
protected void call(Param param) throws Throwable {
|
||||||
}
|
if (param instanceof InitPackageResourcesParam)
|
||||||
|
handleInitPackageResources((InitPackageResourcesParam) param);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,89 +31,103 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
* and its variants.
|
* and its variants.
|
||||||
*/
|
*/
|
||||||
public abstract class XC_LayoutInflated extends XCallback {
|
public abstract class XC_LayoutInflated extends XCallback {
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with default priority.
|
* Creates a new callback with default priority.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public XC_LayoutInflated() {
|
public XC_LayoutInflated() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with a specific priority.
|
* Creates a new callback with a specific priority.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
*/
|
*/
|
||||||
public XC_LayoutInflated(int priority) {
|
public XC_LayoutInflated(int priority) {
|
||||||
super(priority);
|
super(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps information about the inflated layout.
|
* Wraps information about the inflated layout.
|
||||||
*/
|
*/
|
||||||
public static final class LayoutInflatedParam extends XCallback.Param {
|
public static final class LayoutInflatedParam extends XCallback.Param {
|
||||||
/** @hide */
|
/**
|
||||||
public LayoutInflatedParam(CopyOnWriteSortedSet<XC_LayoutInflated> callbacks) {
|
* @hide
|
||||||
super(callbacks);
|
*/
|
||||||
}
|
public LayoutInflatedParam(CopyOnWriteSortedSet<XC_LayoutInflated> callbacks) {
|
||||||
|
super(callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
/** The view that has been created from the layout. */
|
/**
|
||||||
public View view;
|
* The view that has been created from the layout.
|
||||||
|
*/
|
||||||
|
public View view;
|
||||||
|
|
||||||
/** Container with the ID and name of the underlying resource. */
|
/**
|
||||||
public ResourceNames resNames;
|
* Container with the ID and name of the underlying resource.
|
||||||
|
*/
|
||||||
|
public ResourceNames resNames;
|
||||||
|
|
||||||
/** Directory from which the layout was actually loaded (e.g. "layout-sw600dp"). */
|
/**
|
||||||
public String variant;
|
* Directory from which the layout was actually loaded (e.g. "layout-sw600dp").
|
||||||
|
*/
|
||||||
|
public String variant;
|
||||||
|
|
||||||
/** Resources containing the layout. */
|
/**
|
||||||
public XResources res;
|
* Resources containing the layout.
|
||||||
}
|
*/
|
||||||
|
public XResources res;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
protected void call(Param param) throws Throwable {
|
*/
|
||||||
if (param instanceof LayoutInflatedParam)
|
@Override
|
||||||
handleLayoutInflated((LayoutInflatedParam) param);
|
protected void call(Param param) throws Throwable {
|
||||||
}
|
if (param instanceof LayoutInflatedParam)
|
||||||
|
handleLayoutInflated((LayoutInflatedParam) param);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called when the hooked layout has been inflated.
|
* This method is called when the hooked layout has been inflated.
|
||||||
*
|
*
|
||||||
* @param liparam Information about the layout and the inflated view.
|
* @param liparam Information about the layout and the inflated view.
|
||||||
* @throws Throwable Everything the callback throws is caught and logged.
|
* @throws Throwable Everything the callback throws is caught and logged.
|
||||||
*/
|
*/
|
||||||
public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable;
|
public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object with which the callback can be removed.
|
* An object with which the callback can be removed.
|
||||||
*/
|
*/
|
||||||
public class Unhook implements IXUnhook<XC_LayoutInflated> {
|
public class Unhook implements IXUnhook<XC_LayoutInflated> {
|
||||||
private final String resDir;
|
private final String resDir;
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
public Unhook(String resDir, int id) {
|
* @hide
|
||||||
this.resDir = resDir;
|
*/
|
||||||
this.id = id;
|
public Unhook(String resDir, int id) {
|
||||||
}
|
this.resDir = resDir;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the resource ID of the hooked layout.
|
* Returns the resource ID of the hooked layout.
|
||||||
*/
|
*/
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XC_LayoutInflated getCallback() {
|
public XC_LayoutInflated getCallback() {
|
||||||
return XC_LayoutInflated.this;
|
return XC_LayoutInflated.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unhook() {
|
public void unhook() {
|
||||||
XResources.unhookLayout(resDir, id, XC_LayoutInflated.this);
|
XResources.unhookLayout(resDir, id, XC_LayoutInflated.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,54 +30,69 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
* subclass.
|
* subclass.
|
||||||
*/
|
*/
|
||||||
public abstract class XC_LoadPackage extends XCallback implements IXposedHookLoadPackage {
|
public abstract class XC_LoadPackage extends XCallback implements IXposedHookLoadPackage {
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with default priority.
|
* Creates a new callback with default priority.
|
||||||
* @hide
|
*
|
||||||
*/
|
* @hide
|
||||||
@SuppressWarnings("deprecation")
|
*/
|
||||||
public XC_LoadPackage() {
|
@SuppressWarnings("deprecation")
|
||||||
super();
|
public XC_LoadPackage() {
|
||||||
}
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new callback with a specific priority.
|
* Creates a new callback with a specific priority.
|
||||||
*
|
*
|
||||||
* @param priority See {@link XCallback#priority}.
|
* @param priority See {@link XCallback#priority}.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public XC_LoadPackage(int priority) {
|
public XC_LoadPackage(int priority) {
|
||||||
super(priority);
|
super(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps information about the app being loaded.
|
* Wraps information about the app being loaded.
|
||||||
*/
|
*/
|
||||||
public static final class LoadPackageParam extends XCallback.Param {
|
public static final class LoadPackageParam extends XCallback.Param {
|
||||||
/** @hide */
|
/**
|
||||||
public LoadPackageParam(CopyOnWriteSortedSet<XC_LoadPackage> callbacks) {
|
* @hide
|
||||||
super(callbacks);
|
*/
|
||||||
}
|
public LoadPackageParam(CopyOnWriteSortedSet<XC_LoadPackage> callbacks) {
|
||||||
|
super(callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
/** The name of the package being loaded. */
|
/**
|
||||||
public String packageName;
|
* The name of the package being loaded.
|
||||||
|
*/
|
||||||
|
public String packageName;
|
||||||
|
|
||||||
/** The process in which the package is executed. */
|
/**
|
||||||
public String processName;
|
* The process in which the package is executed.
|
||||||
|
*/
|
||||||
|
public String processName;
|
||||||
|
|
||||||
/** The ClassLoader used for this package. */
|
/**
|
||||||
public ClassLoader classLoader;
|
* The ClassLoader used for this package.
|
||||||
|
*/
|
||||||
|
public ClassLoader classLoader;
|
||||||
|
|
||||||
/** More information about the application being loaded. */
|
/**
|
||||||
public ApplicationInfo appInfo;
|
* More information about the application being loaded.
|
||||||
|
*/
|
||||||
|
public ApplicationInfo appInfo;
|
||||||
|
|
||||||
/** Set to {@code true} if this is the first (and main) application for this process. */
|
/**
|
||||||
public boolean isFirstApplication;
|
* Set to {@code true} if this is the first (and main) application for this process.
|
||||||
}
|
*/
|
||||||
|
public boolean isFirstApplication;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
protected void call(Param param) throws Throwable {
|
*/
|
||||||
if (param instanceof LoadPackageParam)
|
@Override
|
||||||
handleLoadPackage((LoadPackageParam) param);
|
protected void call(Param param) throws Throwable {
|
||||||
}
|
if (param instanceof LoadPackageParam)
|
||||||
|
handleLoadPackage((LoadPackageParam) param);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,147 +27,174 @@ import java.io.Serializable;
|
||||||
import de.robv.android.xposed.IModuleContext;
|
import de.robv.android.xposed.IModuleContext;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
|
|
||||||
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
|
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for Xposed callbacks.
|
* Base class for Xposed callbacks.
|
||||||
*
|
* <p>
|
||||||
* This class only keeps a priority for ordering multiple callbacks.
|
* This class only keeps a priority for ordering multiple callbacks.
|
||||||
* The actual (abstract) callback methods are added by subclasses.
|
* The actual (abstract) callback methods are added by subclasses.
|
||||||
*/
|
*/
|
||||||
public abstract class XCallback implements Comparable<XCallback>, IModuleContext {
|
public abstract class XCallback implements Comparable<XCallback>, IModuleContext {
|
||||||
/**
|
/**
|
||||||
* Callback priority, higher number means earlier execution.
|
* Callback priority, higher number means earlier execution.
|
||||||
*
|
*
|
||||||
* <p>This is usually set to {@link #PRIORITY_DEFAULT}. However, in case a certain callback should
|
* <p>This is usually set to {@link #PRIORITY_DEFAULT}. However, in case a certain callback should
|
||||||
* be executed earlier or later a value between {@link #PRIORITY_HIGHEST} and {@link #PRIORITY_LOWEST}
|
* be executed earlier or later a value between {@link #PRIORITY_HIGHEST} and {@link #PRIORITY_LOWEST}
|
||||||
* can be set instead. The values are just for orientation though, Xposed doesn't enforce any
|
* can be set instead. The values are just for orientation though, Xposed doesn't enforce any
|
||||||
* boundaries on the priority values.
|
* boundaries on the priority values.
|
||||||
*/
|
*/
|
||||||
public final int priority;
|
public final int priority;
|
||||||
|
|
||||||
/** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */
|
/**
|
||||||
@Deprecated
|
* @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!
|
||||||
public XCallback() {
|
*/
|
||||||
this.priority = PRIORITY_DEFAULT;
|
@Deprecated
|
||||||
}
|
public XCallback() {
|
||||||
|
this.priority = PRIORITY_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
public XCallback(int priority) {
|
* @hide
|
||||||
this.priority = priority;
|
*/
|
||||||
}
|
public XCallback(int priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for Xposed callback parameters.
|
* Base class for Xposed callback parameters.
|
||||||
*/
|
*/
|
||||||
public static abstract class Param {
|
public static abstract class Param {
|
||||||
/** @hide */
|
/**
|
||||||
public final Object[] callbacks;
|
* @hide
|
||||||
private Bundle extra;
|
*/
|
||||||
|
public final Object[] callbacks;
|
||||||
|
private Bundle extra;
|
||||||
|
|
||||||
/** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */
|
/**
|
||||||
@Deprecated
|
* @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!
|
||||||
protected Param() {
|
*/
|
||||||
callbacks = null;
|
@Deprecated
|
||||||
}
|
protected Param() {
|
||||||
|
callbacks = null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
protected Param(CopyOnWriteSortedSet<? extends XCallback> callbacks) {
|
* @hide
|
||||||
this.callbacks = callbacks.getSnapshot();
|
*/
|
||||||
}
|
protected Param(CopyOnWriteSortedSet<? extends XCallback> callbacks) {
|
||||||
|
this.callbacks = callbacks.getSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This can be used to store any data for the scope of the callback.
|
* This can be used to store any data for the scope of the callback.
|
||||||
*
|
*
|
||||||
* <p>Use this instead of instance variables, as it has a clear reference to e.g. each
|
* <p>Use this instead of instance variables, as it has a clear reference to e.g. each
|
||||||
* separate call to a method, even when the same method is called recursively.
|
* separate call to a method, even when the same method is called recursively.
|
||||||
*
|
*
|
||||||
* @see #setObjectExtra
|
* @see #setObjectExtra
|
||||||
* @see #getObjectExtra
|
* @see #getObjectExtra
|
||||||
*/
|
*/
|
||||||
public synchronized Bundle getExtra() {
|
public synchronized Bundle getExtra() {
|
||||||
if (extra == null)
|
if (extra == null)
|
||||||
extra = new Bundle();
|
extra = new Bundle();
|
||||||
return extra;
|
return extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object stored with {@link #setObjectExtra}.
|
* Returns an object stored with {@link #setObjectExtra}.
|
||||||
*/
|
*/
|
||||||
public Object getObjectExtra(String key) {
|
public Object getObjectExtra(String key) {
|
||||||
Serializable o = getExtra().getSerializable(key);
|
Serializable o = getExtra().getSerializable(key);
|
||||||
if (o instanceof SerializeWrapper)
|
if (o instanceof SerializeWrapper)
|
||||||
return ((SerializeWrapper) o).object;
|
return ((SerializeWrapper) o).object;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores any object for the scope of the callback. For data types that support it, use
|
* Stores any object for the scope of the callback. For data types that support it, use
|
||||||
* the {@link Bundle} returned by {@link #getExtra} instead.
|
* the {@link Bundle} returned by {@link #getExtra} instead.
|
||||||
*/
|
*/
|
||||||
public void setObjectExtra(String key, Object o) {
|
public void setObjectExtra(String key, Object o) {
|
||||||
getExtra().putSerializable(key, new SerializeWrapper(o));
|
getExtra().putSerializable(key, new SerializeWrapper(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SerializeWrapper implements Serializable {
|
private static class SerializeWrapper implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final Object object;
|
private final Object object;
|
||||||
public SerializeWrapper(Object o) {
|
|
||||||
object = o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @hide */
|
public SerializeWrapper(Object o) {
|
||||||
public static void callAll(Param param) {
|
object = o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (param instanceof XC_LoadPackage.LoadPackageParam) {
|
/**
|
||||||
// deopt methods in system apps or priv-apps, this would be not necessary
|
* @hide
|
||||||
|
*/
|
||||||
|
public static void callAll(Param param) {
|
||||||
|
|
||||||
|
if (param instanceof XC_LoadPackage.LoadPackageParam) {
|
||||||
|
// deopt methods in system apps or priv-apps, this would be not necessary
|
||||||
// only if we found out how to recompile their apks
|
// only if we found out how to recompile their apks
|
||||||
XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;
|
XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;
|
||||||
PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);
|
PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.callbacks == null)
|
if (param.callbacks == null)
|
||||||
throw new IllegalStateException("This object was not created for use with callAll");
|
throw new IllegalStateException("This object was not created for use with callAll");
|
||||||
|
|
||||||
for (int i = 0; i < param.callbacks.length; i++) {
|
for (int i = 0; i < param.callbacks.length; i++) {
|
||||||
try {
|
try {
|
||||||
((XCallback) param.callbacks[i]).call(param);
|
((XCallback) param.callbacks[i]).call(param);
|
||||||
} catch (Throwable t) { XposedBridge.log(t); }
|
} catch (Throwable t) {
|
||||||
}
|
XposedBridge.log(t);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
protected void call(Param param) throws Throwable {}
|
* @hide
|
||||||
|
*/
|
||||||
|
protected void call(Param param) throws Throwable {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApkPath() {
|
public String getApkPath() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
public int compareTo(XCallback other) {
|
*/
|
||||||
if (this == other)
|
@Override
|
||||||
return 0;
|
public int compareTo(XCallback other) {
|
||||||
|
if (this == other)
|
||||||
|
return 0;
|
||||||
|
|
||||||
// order descending by priority
|
// order descending by priority
|
||||||
if (other.priority != this.priority)
|
if (other.priority != this.priority)
|
||||||
return other.priority - this.priority;
|
return other.priority - this.priority;
|
||||||
// then randomly
|
// then randomly
|
||||||
else if (System.identityHashCode(this) < System.identityHashCode(other))
|
else if (System.identityHashCode(this) < System.identityHashCode(other))
|
||||||
return -1;
|
return -1;
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The default priority, see {@link #priority}. */
|
/**
|
||||||
public static final int PRIORITY_DEFAULT = 50;
|
* The default priority, see {@link #priority}.
|
||||||
|
*/
|
||||||
|
public static final int PRIORITY_DEFAULT = 50;
|
||||||
|
|
||||||
/** Execute this callback late, see {@link #priority}. */
|
/**
|
||||||
public static final int PRIORITY_LOWEST = -10000;
|
* Execute this callback late, see {@link #priority}.
|
||||||
|
*/
|
||||||
|
public static final int PRIORITY_LOWEST = -10000;
|
||||||
|
|
||||||
/** Execute this callback early, see {@link #priority}. */
|
/**
|
||||||
public static final int PRIORITY_HIGHEST = 10000;
|
* Execute this callback early, see {@link #priority}.
|
||||||
|
*/
|
||||||
|
public static final int PRIORITY_HIGHEST = 10000;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,167 +33,178 @@ import de.robv.android.xposed.SELinuxHelper;
|
||||||
* <p>References to a concrete subclass should generally be retrieved from {@link SELinuxHelper}.
|
* <p>References to a concrete subclass should generally be retrieved from {@link SELinuxHelper}.
|
||||||
*/
|
*/
|
||||||
public abstract class BaseService {
|
public abstract class BaseService {
|
||||||
/** Flag for {@link #checkFileAccess}: Read access. */
|
/**
|
||||||
public static final int R_OK = 4;
|
* Flag for {@link #checkFileAccess}: Read access.
|
||||||
/** Flag for {@link #checkFileAccess}: Write access. */
|
*/
|
||||||
public static final int W_OK = 2;
|
public static final int R_OK = 4;
|
||||||
/** Flag for {@link #checkFileAccess}: Executable access. */
|
/**
|
||||||
public static final int X_OK = 1;
|
* Flag for {@link #checkFileAccess}: Write access.
|
||||||
/** Flag for {@link #checkFileAccess}: File/directory exists. */
|
*/
|
||||||
public static final int F_OK = 0;
|
public static final int W_OK = 2;
|
||||||
|
/**
|
||||||
|
* Flag for {@link #checkFileAccess}: Executable access.
|
||||||
|
*/
|
||||||
|
public static final int X_OK = 1;
|
||||||
|
/**
|
||||||
|
* Flag for {@link #checkFileAccess}: File/directory exists.
|
||||||
|
*/
|
||||||
|
public static final int F_OK = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the services accesses files directly (instead of using IPC).
|
* Checks whether the services accesses files directly (instead of using IPC).
|
||||||
*
|
*
|
||||||
* @return {@code true} in case direct access is possible.
|
* @return {@code true} in case direct access is possible.
|
||||||
*/
|
*/
|
||||||
public boolean hasDirectFileAccess() {
|
public boolean hasDirectFileAccess() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a file is accessible. SELinux might enforce stricter checks.
|
* Check whether a file is accessible. SELinux might enforce stricter checks.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to check.
|
* @param filename The absolute path of the file to check.
|
||||||
* @param mode The mode for POSIX's {@code access()} function.
|
* @param mode The mode for POSIX's {@code access()} function.
|
||||||
* @return The result of the {@code access()} function.
|
* @return The result of the {@code access()} function.
|
||||||
*/
|
*/
|
||||||
public abstract boolean checkFileAccess(String filename, int mode);
|
public abstract boolean checkFileAccess(String filename, int mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a file exists.
|
* Check whether a file exists.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to check.
|
* @param filename The absolute path of the file to check.
|
||||||
* @return The result of the {@code access()} function.
|
* @return The result of the {@code access()} function.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
public boolean checkFileExists(String filename) {
|
public boolean checkFileExists(String filename) {
|
||||||
return checkFileAccess(filename, F_OK);
|
return checkFileAccess(filename, F_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the size and modification time of a file.
|
* Determine the size and modification time of a file.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to check.
|
* @param filename The absolute path of the file to check.
|
||||||
* @return A {@link FileResult} object holding the result.
|
* @return A {@link FileResult} object holding the result.
|
||||||
* @throws IOException In case an error occurred while retrieving the information.
|
* @throws IOException In case an error occurred while retrieving the information.
|
||||||
*/
|
*/
|
||||||
public abstract FileResult statFile(String filename) throws IOException;
|
public abstract FileResult statFile(String filename) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the size time of a file.
|
* Determine the size time of a file.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to check.
|
* @param filename The absolute path of the file to check.
|
||||||
* @return The file size.
|
* @return The file size.
|
||||||
* @throws IOException In case an error occurred while retrieving the information.
|
* @throws IOException In case an error occurred while retrieving the information.
|
||||||
*/
|
*/
|
||||||
public long getFileSize(String filename) throws IOException {
|
public long getFileSize(String filename) throws IOException {
|
||||||
return statFile(filename).size;
|
return statFile(filename).size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the size time of a file.
|
* Determine the size time of a file.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to check.
|
* @param filename The absolute path of the file to check.
|
||||||
* @return The file modification time.
|
* @return The file modification time.
|
||||||
* @throws IOException In case an error occurred while retrieving the information.
|
* @throws IOException In case an error occurred while retrieving the information.
|
||||||
*/
|
*/
|
||||||
public long getFileModificationTime(String filename) throws IOException {
|
public long getFileModificationTime(String filename) throws IOException {
|
||||||
return statFile(filename).mtime;
|
return statFile(filename).mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a file into memory.
|
* Read a file into memory.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to read.
|
* @param filename The absolute path of the file to read.
|
||||||
* @return A {@code byte} array with the file content.
|
* @return A {@code byte} array with the file content.
|
||||||
* @throws IOException In case an error occurred while reading the file.
|
* @throws IOException In case an error occurred while reading the file.
|
||||||
*/
|
*/
|
||||||
public abstract byte[] readFile(String filename) throws IOException;
|
public abstract byte[] readFile(String filename) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a file into memory, but only if it has changed since the last time.
|
* Read a file into memory, but only if it has changed since the last time.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to read.
|
* @param filename The absolute path of the file to read.
|
||||||
* @param previousSize File size of last read.
|
* @param previousSize File size of last read.
|
||||||
* @param previousTime File modification time of last read.
|
* @param previousTime File modification time of last read.
|
||||||
* @return A {@link FileResult} object holding the result.
|
* @return A {@link FileResult} object holding the result.
|
||||||
* <p>The {@link FileResult#content} field might be {@code null} if the file
|
* <p>The {@link FileResult#content} field might be {@code null} if the file
|
||||||
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
||||||
* @throws IOException In case an error occurred while reading the file.
|
* @throws IOException In case an error occurred while reading the file.
|
||||||
*/
|
*/
|
||||||
public abstract FileResult readFile(String filename, long previousSize, long previousTime) throws IOException;
|
public abstract FileResult readFile(String filename, long previousSize, long previousTime) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a file into memory, optionally only if it has changed since the last time.
|
* Read a file into memory, optionally only if it has changed since the last time.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to read.
|
* @param filename The absolute path of the file to read.
|
||||||
* @param offset Number of bytes to skip at the beginning of the file.
|
* @param offset Number of bytes to skip at the beginning of the file.
|
||||||
* @param length Number of bytes to read (0 means read to end of file).
|
* @param length Number of bytes to read (0 means read to end of file).
|
||||||
* @param previousSize Optional: File size of last read.
|
* @param previousSize Optional: File size of last read.
|
||||||
* @param previousTime Optional: File modification time of last read.
|
* @param previousTime Optional: File modification time of last read.
|
||||||
* @return A {@link FileResult} object holding the result.
|
* @return A {@link FileResult} object holding the result.
|
||||||
* <p>The {@link FileResult#content} field might be {@code null} if the file
|
* <p>The {@link FileResult#content} field might be {@code null} if the file
|
||||||
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
||||||
* @throws IOException In case an error occurred while reading the file.
|
* @throws IOException In case an error occurred while reading the file.
|
||||||
*/
|
*/
|
||||||
public abstract FileResult readFile(String filename, int offset, int length,
|
public abstract FileResult readFile(String filename, int offset, int length,
|
||||||
long previousSize, long previousTime) throws IOException;
|
long previousSize, long previousTime) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a stream to the file content.
|
* Get a stream to the file content.
|
||||||
* Depending on the service, it may or may not be read completely into memory.
|
* Depending on the service, it may or may not be read completely into memory.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to read.
|
* @param filename The absolute path of the file to read.
|
||||||
* @return An {@link InputStream} to the file content.
|
* @return An {@link InputStream} to the file content.
|
||||||
* @throws IOException In case an error occurred while reading the file.
|
* @throws IOException In case an error occurred while reading the file.
|
||||||
*/
|
*/
|
||||||
public InputStream getFileInputStream(String filename) throws IOException {
|
public InputStream getFileInputStream(String filename) throws IOException {
|
||||||
return new ByteArrayInputStream(readFile(filename));
|
return new ByteArrayInputStream(readFile(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a stream to the file content, but only if it has changed since the last time.
|
* Get a stream to the file content, but only if it has changed since the last time.
|
||||||
* Depending on the service, it may or may not be read completely into memory.
|
* Depending on the service, it may or may not be read completely into memory.
|
||||||
*
|
*
|
||||||
* @param filename The absolute path of the file to read.
|
* @param filename The absolute path of the file to read.
|
||||||
* @param previousSize Optional: File size of last read.
|
* @param previousSize Optional: File size of last read.
|
||||||
* @param previousTime Optional: File modification time of last read.
|
* @param previousTime Optional: File modification time of last read.
|
||||||
* @return A {@link FileResult} object holding the result.
|
* @return A {@link FileResult} object holding the result.
|
||||||
* <p>The {@link FileResult#stream} field might be {@code null} if the file
|
* <p>The {@link FileResult#stream} field might be {@code null} if the file
|
||||||
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
|
||||||
* @throws IOException In case an error occurred while reading the file.
|
* @throws IOException In case an error occurred while reading the file.
|
||||||
*/
|
*/
|
||||||
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
|
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
|
||||||
FileResult result = readFile(filename, previousSize, previousTime);
|
FileResult result = readFile(filename, previousSize, previousTime);
|
||||||
if (result.content == null)
|
if (result.content == null)
|
||||||
return result;
|
return result;
|
||||||
return new FileResult(new ByteArrayInputStream(result.content), result.size, result.mtime);
|
return new FileResult(new ByteArrayInputStream(result.content), result.size, result.mtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/*package*/ BaseService() {}
|
/*package*/ BaseService() {
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ static void ensureAbsolutePath(String filename) {
|
/*package*/
|
||||||
if (!filename.startsWith("/")) {
|
static void ensureAbsolutePath(String filename) {
|
||||||
throw new IllegalArgumentException("Only absolute filenames are allowed: " + filename);
|
if (!filename.startsWith("/")) {
|
||||||
}
|
throw new IllegalArgumentException("Only absolute filenames are allowed: " + filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ static void throwCommonIOException(int errno, String errorMsg, String filename, String defaultText) throws IOException {
|
/*package*/
|
||||||
switch (errno) {
|
static void throwCommonIOException(int errno, String errorMsg, String filename, String defaultText) throws IOException {
|
||||||
case 1: // EPERM
|
switch (errno) {
|
||||||
case 13: // EACCES
|
case 1: // EPERM
|
||||||
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Permission denied: " + filename);
|
case 13: // EACCES
|
||||||
case 2: // ENOENT
|
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Permission denied: " + filename);
|
||||||
throw new FileNotFoundException(errorMsg != null ? errorMsg : "No such file or directory: " + filename);
|
case 2: // ENOENT
|
||||||
case 12: // ENOMEM
|
throw new FileNotFoundException(errorMsg != null ? errorMsg : "No such file or directory: " + filename);
|
||||||
throw new OutOfMemoryError(errorMsg);
|
case 12: // ENOMEM
|
||||||
case 21: // EISDIR
|
throw new OutOfMemoryError(errorMsg);
|
||||||
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Is a directory: " + filename);
|
case 21: // EISDIR
|
||||||
default:
|
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Is a directory: " + filename);
|
||||||
throw new IOException(errorMsg != null ? errorMsg : "Error " + errno + defaultText + filename);
|
default:
|
||||||
}
|
throw new IOException(errorMsg != null ? errorMsg : "Error " + errno + defaultText + filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,108 +26,110 @@ import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
public final class DirectAccessService extends BaseService {
|
public final class DirectAccessService extends BaseService {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDirectFileAccess() {
|
public boolean hasDirectFileAccess() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("RedundantIfStatement")
|
@SuppressWarnings("RedundantIfStatement")
|
||||||
@Override
|
@Override
|
||||||
public boolean checkFileAccess(String filename, int mode) {
|
public boolean checkFileAccess(String filename, int mode) {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
if (mode == F_OK && !file.exists()) return false;
|
if (mode == F_OK && !file.exists()) return false;
|
||||||
if ((mode & R_OK) != 0 && !file.canRead()) return false;
|
if ((mode & R_OK) != 0 && !file.canRead()) return false;
|
||||||
if ((mode & W_OK) != 0 && !file.canWrite()) return false;
|
if ((mode & W_OK) != 0 && !file.canWrite()) return false;
|
||||||
if ((mode & X_OK) != 0 && !file.canExecute()) return false;
|
if ((mode & X_OK) != 0 && !file.canExecute()) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkFileExists(String filename) {
|
public boolean checkFileExists(String filename) {
|
||||||
return new File(filename).exists();
|
return new File(filename).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResult statFile(String filename) throws IOException {
|
public FileResult statFile(String filename) throws IOException {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
return new FileResult(file.length(), file.lastModified());
|
return new FileResult(file.length(), file.lastModified());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] readFile(String filename) throws IOException {
|
public byte[] readFile(String filename) throws IOException {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
byte content[] = new byte[(int)file.length()];
|
byte content[] = new byte[(int) file.length()];
|
||||||
FileInputStream fis = new FileInputStream(file);
|
FileInputStream fis = new FileInputStream(file);
|
||||||
fis.read(content);
|
fis.read(content);
|
||||||
fis.close();
|
fis.close();
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException {
|
public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
long size = file.length();
|
long size = file.length();
|
||||||
long time = file.lastModified();
|
long time = file.lastModified();
|
||||||
if (previousSize == size && previousTime == time)
|
if (previousSize == size && previousTime == time)
|
||||||
return new FileResult(size, time);
|
return new FileResult(size, time);
|
||||||
return new FileResult(readFile(filename), size, time);
|
return new FileResult(readFile(filename), size, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResult readFile(String filename, int offset, int length, long previousSize, long previousTime) throws IOException {
|
public FileResult readFile(String filename, int offset, int length, long previousSize, long previousTime) throws IOException {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
long size = file.length();
|
long size = file.length();
|
||||||
long time = file.lastModified();
|
long time = file.lastModified();
|
||||||
if (previousSize == size && previousTime == time)
|
if (previousSize == size && previousTime == time)
|
||||||
return new FileResult(size, time);
|
return new FileResult(size, time);
|
||||||
|
|
||||||
// Shortcut for the simple case
|
// Shortcut for the simple case
|
||||||
if (offset <= 0 && length <= 0)
|
if (offset <= 0 && length <= 0)
|
||||||
return new FileResult(readFile(filename), size, time);
|
return new FileResult(readFile(filename), size, time);
|
||||||
|
|
||||||
// Check range
|
// Check range
|
||||||
if (offset > 0 && offset >= size) {
|
if (offset > 0 && offset >= size) {
|
||||||
throw new IllegalArgumentException("Offset " + offset + " is out of range for " + filename);
|
throw new IllegalArgumentException("Offset " + offset + " is out of range for " + filename);
|
||||||
} else if (offset < 0) {
|
} else if (offset < 0) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 0 && (offset + length) > size) {
|
if (length > 0 && (offset + length) > size) {
|
||||||
throw new IllegalArgumentException("Length " + length + " is out of range for " + filename);
|
throw new IllegalArgumentException("Length " + length + " is out of range for " + filename);
|
||||||
} else if (length <= 0) {
|
} else if (length <= 0) {
|
||||||
length = (int) (size - offset);
|
length = (int) (size - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte content[] = new byte[length];
|
byte content[] = new byte[length];
|
||||||
FileInputStream fis = new FileInputStream(file);
|
FileInputStream fis = new FileInputStream(file);
|
||||||
fis.skip(offset);
|
fis.skip(offset);
|
||||||
fis.read(content);
|
fis.read(content);
|
||||||
fis.close();
|
fis.close();
|
||||||
return new FileResult(content, size, time);
|
return new FileResult(content, size, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
|
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public InputStream getFileInputStream(String filename) throws IOException {
|
public InputStream getFileInputStream(String filename) throws IOException {
|
||||||
return new BufferedInputStream(new FileInputStream(filename), 16*1024);
|
return new BufferedInputStream(new FileInputStream(filename), 16 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
|
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
|
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
long size = file.length();
|
long size = file.length();
|
||||||
long time = file.lastModified();
|
long time = file.lastModified();
|
||||||
if (previousSize == size && previousTime == time)
|
if (previousSize == size && previousTime == time)
|
||||||
return new FileResult(size, time);
|
return new FileResult(size, time);
|
||||||
return new FileResult(new BufferedInputStream(new FileInputStream(filename), 16*1024), size, time);
|
return new FileResult(new BufferedInputStream(new FileInputStream(filename), 16 * 1024), size, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,55 +26,65 @@ import java.io.InputStream;
|
||||||
* Holder for the result of a {@link BaseService#readFile} or {@link BaseService#statFile} call.
|
* Holder for the result of a {@link BaseService#readFile} or {@link BaseService#statFile} call.
|
||||||
*/
|
*/
|
||||||
public final class FileResult {
|
public final class FileResult {
|
||||||
/** File content, might be {@code null} if the file wasn't read. */
|
/**
|
||||||
public final byte[] content;
|
* File content, might be {@code null} if the file wasn't read.
|
||||||
/** File input stream, might be {@code null} if the file wasn't read. */
|
*/
|
||||||
public final InputStream stream;
|
public final byte[] content;
|
||||||
/** File size. */
|
/**
|
||||||
public final long size;
|
* File input stream, might be {@code null} if the file wasn't read.
|
||||||
/** File last modification time. */
|
*/
|
||||||
public final long mtime;
|
public final InputStream stream;
|
||||||
|
/**
|
||||||
|
* File size.
|
||||||
|
*/
|
||||||
|
public final long size;
|
||||||
|
/**
|
||||||
|
* File last modification time.
|
||||||
|
*/
|
||||||
|
public final long mtime;
|
||||||
|
|
||||||
/*package*/ FileResult(long size, long mtime) {
|
/*package*/ FileResult(long size, long mtime) {
|
||||||
this.content = null;
|
this.content = null;
|
||||||
this.stream = null;
|
this.stream = null;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.mtime = mtime;
|
this.mtime = mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ FileResult(byte[] content, long size, long mtime) {
|
/*package*/ FileResult(byte[] content, long size, long mtime) {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.stream = null;
|
this.stream = null;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.mtime = mtime;
|
this.mtime = mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ FileResult(InputStream stream, long size, long mtime) {
|
/*package*/ FileResult(InputStream stream, long size, long mtime) {
|
||||||
this.content = null;
|
this.content = null;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.mtime = mtime;
|
this.mtime = mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/**
|
||||||
@Override
|
* @hide
|
||||||
public String toString() {
|
*/
|
||||||
StringBuilder sb = new StringBuilder("{");
|
@Override
|
||||||
if (content != null) {
|
public String toString() {
|
||||||
sb.append("content.length: ");
|
StringBuilder sb = new StringBuilder("{");
|
||||||
sb.append(content.length);
|
if (content != null) {
|
||||||
sb.append(", ");
|
sb.append("content.length: ");
|
||||||
}
|
sb.append(content.length);
|
||||||
if (stream != null) {
|
sb.append(", ");
|
||||||
sb.append("stream: ");
|
}
|
||||||
sb.append(stream.toString());
|
if (stream != null) {
|
||||||
sb.append(", ");
|
sb.append("stream: ");
|
||||||
}
|
sb.append(stream.toString());
|
||||||
sb.append("size: ");
|
sb.append(", ");
|
||||||
sb.append(size);
|
}
|
||||||
sb.append(", mtime: ");
|
sb.append("size: ");
|
||||||
sb.append(mtime);
|
sb.append(size);
|
||||||
sb.append("}");
|
sb.append(", mtime: ");
|
||||||
return sb.toString();
|
sb.append(mtime);
|
||||||
}
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,15 +79,15 @@ public class Main {
|
||||||
// Initialize the Xposed framework
|
// Initialize the Xposed framework
|
||||||
try {
|
try {
|
||||||
startBootstrapHook(isSystem, appDataDir);
|
startBootstrapHook(isSystem, appDataDir);
|
||||||
XposedInit.initForZygote();
|
XposedInit.hookResources();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Utils.logE("error during Xposed initialization", t);
|
Utils.logE("error during Xposed initialization", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadModulesSafely(boolean callInitZygote) {
|
private static void loadModulesSafely() {
|
||||||
try {
|
try {
|
||||||
XposedInit.loadModules(callInitZygote);
|
XposedInit.loadModules();
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
Utils.logE("error loading module list", exception);
|
Utils.logE("error loading module list", exception);
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ public class Main {
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||||
installBootstrapHooks(isSystem, appDataDir);
|
installBootstrapHooks(isSystem, appDataDir);
|
||||||
Utils.logI("Loading modules for " + niceName);
|
Utils.logI("Loading modules for " + niceName);
|
||||||
loadModulesSafely(true);
|
loadModulesSafely();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {
|
public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
||||||
|
|
||||||
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
||||||
public class HandleBindAppHooker extends XC_MethodHook {
|
public class HandleBindAppHooker extends XC_MethodHook {
|
||||||
String appDataDir = null;
|
String appDataDir;
|
||||||
|
|
||||||
public HandleBindAppHooker(String appDataDir) {
|
public HandleBindAppHooker(String appDataDir) {
|
||||||
this.appDataDir = appDataDir;
|
this.appDataDir = appDataDir;
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@
|
||||||
|
|
||||||
package org.lsposed.lspd.hooker;
|
package org.lsposed.lspd.hooker;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import org.lsposed.lspd.util.Hookers;
|
import org.lsposed.lspd.util.Hookers;
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodHook;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
|
|
@ -60,7 +58,7 @@ public class StartBootstrapServicesHooker extends XC_MethodHook {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
|
String className = "com.android.server.pm.PackageDexOptimizer";
|
||||||
findAndHookMethod(className, SystemMainHooker.systemServerCL,
|
findAndHookMethod(className, SystemMainHooker.systemServerCL,
|
||||||
"dexEntryExists", String.class,
|
"dexEntryExists", String.class,
|
||||||
XC_MethodReplacement.returnConstant(true));
|
XC_MethodReplacement.returnConstant(true));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue