[core] Remove useless codes (#505)

* [core] Replace tabs with spaces

* [core] Remove useless codes
This commit is contained in:
tehcneko 2021-04-24 14:59:35 +08:00 committed by GitHub
parent 86304f11ce
commit d7897b67d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 3080 additions and 2874 deletions

View File

@ -28,21 +28,29 @@ package de.robv.android.xposed;
* implemented anymore.
*/
public interface IXposedHookCmdInit extends IXposedMod {
/**
* 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.
*/
void initCmdApp(StartupParam startupParam) throws Throwable;
/**
* 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.
*/
void initCmdApp(StartupParam startupParam) throws Throwable;
/** Data holder for {@link #initCmdApp}. */
final class StartupParam {
/*package*/ StartupParam() {}
/**
* Data holder for {@link #initCmdApp}.
*/
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;
}
}

View File

@ -33,33 +33,35 @@ import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResou
* registering it as a callback automatically.
*/
public interface IXposedHookInitPackageResources extends IXposedMod {
/**
* 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.
*
* @param resparam Information about the resources.
* @throws Throwable Everything the callback throws is caught and logged.
*/
void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;
/**
* 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.
*
* @param resparam Information about the resources.
* @throws Throwable Everything the callback throws is caught and logged.
*/
void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;
/** @hide */
final class Wrapper extends XC_InitPackageResources {
private final IXposedHookInitPackageResources instance;
private final String apkPath;
/**
* @hide
*/
final class Wrapper extends XC_InitPackageResources {
private final IXposedHookInitPackageResources instance;
private final String apkPath;
public Wrapper(IXposedHookInitPackageResources instance, String apkPath) {
this.instance = instance;
this.apkPath = apkPath;
}
public Wrapper(IXposedHookInitPackageResources instance, String apkPath) {
this.instance = instance;
this.apkPath = apkPath;
}
@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
instance.handleInitPackageResources(resparam);
}
@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
instance.handleInitPackageResources(resparam);
}
@Override
public String getApkPath() {
return apkPath;
}
}
@Override
public String getApkPath() {
return apkPath;
}
}
}

View File

@ -33,33 +33,36 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
* registering it as a callback automatically.
*/
public interface IXposedHookLoadPackage extends IXposedMod {
/**
* This method is called when an app is loaded. It's called very early, even before
* {@link Application#onCreate} is called.
* Modules can set up their app-specific hooks here.
*
* @param lpparam Information about the app.
* @throws Throwable Everything the callback throws is caught and logged.
*/
void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;
/**
* This method is called when an app is loaded. It's called very early, even before
* {@link Application#onCreate} is called.
* Modules can set up their app-specific hooks here.
*
* @param lpparam Information about the app.
* @throws Throwable Everything the callback throws is caught and logged.
*/
void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;
/** @hide */
final class Wrapper extends XC_LoadPackage {
private final IXposedHookLoadPackage instance;
private final String apkPath;
/**
* @hide
*/
final class Wrapper extends XC_LoadPackage {
private final IXposedHookLoadPackage instance;
private final String apkPath;
public Wrapper(IXposedHookLoadPackage instance, String apkPath) {
this.instance = instance;
this.apkPath = apkPath;
}
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
instance.handleLoadPackage(lpparam);
}
public Wrapper(IXposedHookLoadPackage instance, String apkPath) {
this.instance = instance;
this.apkPath = apkPath;
}
@Override
public String getApkPath() {
return apkPath;
}
}
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
instance.handleLoadPackage(lpparam);
}
@Override
public String getApkPath() {
return apkPath;
}
}
}

View File

@ -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.
*/
public interface IXposedHookZygoteInit extends IXposedMod {
/**
* 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.
*/
void initZygote(StartupParam startupParam) throws Throwable;
/**
* 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.
*/
void initZygote(StartupParam startupParam) throws Throwable;
/** Data holder for {@link #initZygote}. */
final class StartupParam extends XCallback.Param {
/*package*/ StartupParam() {}
/**
* Data holder for {@link #initZygote}.
*/
final class StartupParam extends XCallback.Param {
/*package*/ StartupParam() {
}
/**
* @param callbacks
@ -54,15 +58,17 @@ public interface IXposedHookZygoteInit extends IXposedMod {
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
* process that starts the system_server.
*/
public boolean startsSystemServer;
}
/**
* Always {@code true} on 32-bit ROMs. On 64-bit, it's only {@code true} for the primary
* process that starts the system_server.
*/
public boolean startsSystemServer;
}
/**
* @hide
@ -83,9 +89,9 @@ public interface IXposedHookZygoteInit extends IXposedMod {
instance.initZygote(this.startupParam);
}
@Override
public String getApkPath() {
return startupParam.modulePath;
}
}
@Override
public String getApkPath() {
return startupParam.modulePath;
}
}
}

View File

@ -20,5 +20,8 @@
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 {
}

View File

@ -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.
*/
public final class SELinuxHelper {
private SELinuxHelper() {}
private SELinuxHelper() {
}
/**
* Determines whether SELinux is disabled or enabled.
*
* @return A boolean indicating whether SELinux is enabled.
*/
public static boolean isSELinuxEnabled() {
// lsp: always enabled
return true;
}
/**
* Determines whether SELinux is disabled or enabled.
*
* @return A boolean indicating whether SELinux is enabled.
*/
public static boolean isSELinuxEnabled() {
// lsp: always enabled
return true;
}
/**
* Determines whether SELinux is permissive or enforcing.
*
* @return A boolean indicating whether SELinux is enforcing.
*/
public static boolean isSELinuxEnforced() {
// lsp: always enforcing
return true;
}
/**
* Determines whether SELinux is permissive or enforcing.
*
* @return A boolean indicating whether SELinux is enforcing.
*/
public static boolean isSELinuxEnforced() {
// lsp: always enforcing
return true;
}
/**
* Gets the security context of the current process.
*
* @return A String representing the security context of the current process.
*/
public static String getContext() {
return null;
}
/**
* Gets the security context of the current process.
*
* @return A String representing the security context of the current process.
*/
public static String getContext() {
return null;
}
/**
* 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,
* don't re-use the result in different process!
*
* @return An instance of the service.
*/
public static BaseService getAppDataFileService() {
/**
* 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,
* don't re-use the result in different process!
*
* @return An instance of the service.
*/
public static BaseService getAppDataFileService() {
return sServiceAppDataFile;
}
private static final BaseService sServiceAppDataFile = new DirectAccessService(); // ed: initialized directly
private static final BaseService sServiceAppDataFile = new DirectAccessService(); // ed: initialized directly
}

View File

@ -32,157 +32,175 @@ import de.robv.android.xposed.callbacks.XCallback;
* {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}.
*/
public abstract class XC_MethodHook extends XCallback {
/**
* Creates a new callback with default priority.
*/
@SuppressWarnings("deprecation")
public XC_MethodHook() {
super();
}
/**
* Creates a new callback with default priority.
*/
@SuppressWarnings("deprecation")
public XC_MethodHook() {
super();
}
/**
* Creates a new callback with a specific priority.
*
* <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
* final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e.
* highest priority first.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_MethodHook(int priority) {
super(priority);
}
/**
* Creates a new callback with a specific priority.
*
* <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
* final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e.
* highest priority first.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_MethodHook(int priority) {
super(priority);
}
/**
* Called before the invocation of the method.
*
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
* to prevent the original method from being called.
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {}
/**
* Called before the invocation of the method.
*
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
* to prevent the original method from being called.
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
public void callBeforeHookedMethod(MethodHookParam param) throws Throwable {
beforeHookedMethod(param);
}
public void callBeforeHookedMethod(MethodHookParam param) throws Throwable {
beforeHookedMethod(param);
}
/**
* Called after the invocation of the method.
*
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
* to modify the return value of the original method.
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void afterHookedMethod(MethodHookParam param) throws Throwable {}
/**
* Called after the invocation of the method.
*
* <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}
* to modify the return value of the original method.
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
}
public void callAfterHookedMethod(MethodHookParam param) throws Throwable {
afterHookedMethod(param);
}
public void callAfterHookedMethod(MethodHookParam param) throws Throwable {
afterHookedMethod(param);
}
/**
* Wraps information about the method call and allows to influence it.
*/
public static final class MethodHookParam extends XCallback.Param {
/** @hide */
@SuppressWarnings("deprecation")
public MethodHookParam() {
super();
}
/**
* Wraps information about the method call and allows to influence it.
*/
public static final class MethodHookParam extends XCallback.Param {
/**
* @hide
*/
@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 Throwable throwable = null;
public boolean returnEarly = false;
private Object result = null;
private Throwable throwable = null;
public boolean returnEarly = false;
/** Returns the result of the method call. */
public Object getResult() {
return result;
}
/**
* Returns the result of the method call.
*/
public Object getResult() {
return result;
}
/**
* Modify the result of the method call.
*
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
*/
public void setResult(Object result) {
this.result = result;
this.throwable = null;
this.returnEarly = true;
}
/**
* Modify the result of the method call.
*
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
*/
public void setResult(Object result) {
this.result = result;
this.throwable = null;
this.returnEarly = true;
}
/** Returns the {@link Throwable} thrown by the method, or {@code null}. */
public Throwable getThrowable() {
return throwable;
}
/**
* Returns the {@link Throwable} thrown by the method, or {@code null}.
*/
public Throwable getThrowable() {
return throwable;
}
/** Returns true if an exception was thrown by the method. */
public boolean hasThrowable() {
return throwable != null;
}
/**
* Returns true if an exception was thrown by the method.
*/
public boolean hasThrowable() {
return throwable != null;
}
/**
* Modify the exception thrown of the method call.
*
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
*/
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
this.result = null;
this.returnEarly = true;
}
/**
* Modify the exception thrown of the method call.
*
* <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
*/
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
this.result = null;
this.returnEarly = true;
}
/** Returns the result of the method call, or throws the Throwable caused by it. */
public Object getResultOrThrowable() throws Throwable {
if (throwable != null)
throw throwable;
return result;
}
}
/**
* Returns the result of the method call, or throws the Throwable caused by it.
*/
public Object getResultOrThrowable() throws Throwable {
if (throwable != null)
throw throwable;
return result;
}
}
/**
* An object with which the method/constructor can be unhooked.
*/
public class Unhook implements IXUnhook<XC_MethodHook> {
private final Member hookMethod;
/**
* An object with which the method/constructor can be unhooked.
*/
public class Unhook implements IXUnhook<XC_MethodHook> {
private final Member hookMethod;
/*package*/ Unhook(Member hookMethod) {
this.hookMethod = hookMethod;
}
/*package*/ Unhook(Member hookMethod) {
this.hookMethod = hookMethod;
}
/**
* Returns the method/constructor that has been hooked.
*/
public Member getHookedMethod() {
return hookMethod;
}
/**
* Returns the method/constructor that has been hooked.
*/
public Member getHookedMethod() {
return hookMethod;
}
@Override
public XC_MethodHook getCallback() {
return XC_MethodHook.this;
}
@Override
public XC_MethodHook getCallback() {
return XC_MethodHook.this;
}
@SuppressWarnings("deprecation")
@Override
public void unhook() {
XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);
}
@SuppressWarnings("deprecation")
@Override
public void unhook() {
XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);
}
}
}
}

View File

@ -26,83 +26,88 @@ import de.robv.android.xposed.callbacks.XCallback;
* A special case of {@link XC_MethodHook} which completely replaces the original method.
*/
public abstract class XC_MethodReplacement extends XC_MethodHook {
/**
* Creates a new callback with default priority.
*/
public XC_MethodReplacement() {
super();
}
/**
* Creates a new callback with default priority.
*/
public XC_MethodReplacement() {
super();
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_MethodReplacement(int priority) {
super(priority);
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_MethodReplacement(int priority) {
super(priority);
}
/** @hide */
@Override
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
try {
Object result = replaceHookedMethod(param);
param.setResult(result);
} catch (Throwable t) {
XposedBridge.log(t);
param.setThrowable(t);
}
}
/**
* @hide
*/
@Override
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
try {
Object result = replaceHookedMethod(param);
param.setResult(result);
} catch (Throwable t) {
XposedBridge.log(t);
param.setThrowable(t);
}
}
/** @hide */
@Override
@SuppressWarnings("EmptyMethod")
protected final void afterHookedMethod(MethodHookParam param) throws Throwable {}
/**
* @hide
*/
@Override
@SuppressWarnings("EmptyMethod")
protected final void afterHookedMethod(MethodHookParam param) throws Throwable {
}
/**
* 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).
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.
*/
@SuppressWarnings("UnusedParameters")
protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
/**
* 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).
*
* <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.
*
* @param param Information about the method call.
* @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.
*/
@SuppressWarnings("UnusedParameters")
protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
/**
* Predefined callback that skips the method without replacements.
*/
public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST*2) {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
};
/**
* Predefined callback that skips the method without replacements.
*/
public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST * 2) {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
};
/**
* Creates a callback which always returns a specific value.
*
* @param result The value that should be returned to callers of the hooked method.
*/
public static XC_MethodReplacement returnConstant(final Object result) {
return returnConstant(PRIORITY_DEFAULT, result);
}
/**
* Creates a callback which always returns a specific value.
*
* @param result The value that should be returned to callers of the hooked method.
*/
public static XC_MethodReplacement returnConstant(final Object result) {
return returnConstant(PRIORITY_DEFAULT, result);
}
/**
* Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.
*
* @param priority See {@link XCallback#priority}.
* @param result The value that should be returned to callers of the hooked method.
*/
public static XC_MethodReplacement returnConstant(int priority, final Object result) {
return new XC_MethodReplacement(priority) {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return result;
}
};
}
/**
* Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.
*
* @param priority See {@link XCallback#priority}.
* @param result The value that should be returned to callers of the hooked method.
*/
public static XC_MethodReplacement returnConstant(int priority, final Object result) {
return new XC_MethodReplacement(priority) {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return result;
}
};
}
}

View File

@ -28,6 +28,7 @@ import android.preference.PreferenceManager;
import android.util.Log;
import com.android.internal.util.XmlUtils;
import org.lsposed.lspd.BuildConfig;
import org.lsposed.lspd.util.MetaDataReader;
@ -99,7 +100,8 @@ public final class XSharedPreferences implements SharedPreferences {
Path dir = (Path) key.watchable();
Path path = dir.resolve((Path) event.context());
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
if (pathStr.endsWith(".bak")) {
if (kind != StandardWatchEventKinds.ENTRY_DELETE) {
@ -114,7 +116,8 @@ public final class XSharedPreferences implements SharedPreferences {
try {
l.onSharedPreferenceChanged(data.mPrefs, null);
} 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) {
mFile = new File(serviceClient.getPrefsPath( packageName ), prefFileName + ".xml");
mFile = new File(serviceClient.getPrefsPath(packageName), prefFileName + ".xml");
} else {
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()) {
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) {
if (BuildConfig.DEBUG) Log.e(TAG, "tryRegisterWatcher: access denied to " + path);
} catch (Exception e) {
@ -232,7 +236,8 @@ public final class XSharedPreferences implements SharedPreferences {
if (!atLeastOneValid) {
try {
sWatcher.close();
} catch (Exception ignore) { }
} catch (Exception ignore) {
}
}
}
}
@ -522,7 +527,7 @@ public final class XSharedPreferences implements SharedPreferences {
if (listener == null)
throw new IllegalArgumentException("listener cannot be null");
synchronized(this) {
synchronized (this) {
if (mListeners.put(listener, sContent) == null) {
tryRegisterWatcher();
}
@ -537,7 +542,7 @@ public final class XSharedPreferences implements SharedPreferences {
*/
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
synchronized (this) {
if (mListeners.remove(listener) != null && mListeners.isEmpty()) {
tryUnregisterWatcher();
}

View File

@ -20,11 +20,16 @@
package de.robv.android.xposed;
import static de.robv.android.xposed.XposedHelpers.setObjectField;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.Log;
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.Executable;
@ -43,12 +48,6 @@ import java.util.Set;
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
import de.robv.android.xposed.callbacks.XC_InitZygote;
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
@ -56,377 +55,373 @@ import static de.robv.android.xposed.XposedHelpers.setObjectField;
*/
@SuppressWarnings("JniMissingFunction")
public final class XposedBridge {
/**
* The system class loader which can be used to locate Android framework classes.
* Application classes cannot be retrieved from it.
*
* @see ClassLoader#getSystemClassLoader
*/
public static final ClassLoader BOOTCLASSLOADER = XposedBridge.class.getClassLoader();
/**
* The system class loader which can be used to locate Android framework classes.
* Application classes cannot be retrieved from it.
*
* @see ClassLoader#getSystemClassLoader
*/
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
public static int XPOSED_BRIDGE_VERSION;
/**
* @deprecated Use {@link #getXposedVersion()} instead.
*/
@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.
/*package*/ static long BOOT_START_TIME;
// built-in handlers
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
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<>();
public static volatile ClassLoader dummyClassLoader = null;
private XposedBridge() {}
public static volatile ClassLoader dummyClassLoader = null;
public static void initXResources() {
public static void initXResources() {
if (dummyClassLoader != null) {
return;
}
try {
Resources res = Resources.getSystem();
Class<?> resClass = res.getClass();
Class<?> taClass = TypedArray.class;
try {
TypedArray ta = res.obtainTypedArray(res.getIdentifier(
"preloaded_drawables", "array", "android"));
taClass = ta.getClass();
ta.recycle();
} catch (Resources.NotFoundException nfe) {
XposedBridge.log(nfe);
}
ResourcesHook.removeFinalFlagNative(resClass);
ResourcesHook.removeFinalFlagNative(taClass);
ClassLoader myCL = XposedBridge.class.getClassLoader();
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass");
setObjectField(myCL, "parent", dummyClassLoader);
} catch (Throwable throwable) {
XposedBridge.log(throwable);
XposedInit.disableResources = true;
}
}
return;
}
try {
Resources res = Resources.getSystem();
Class<?> resClass = res.getClass();
Class<?> taClass = TypedArray.class;
try {
TypedArray ta = res.obtainTypedArray(res.getIdentifier(
"preloaded_drawables", "array", "android"));
taClass = ta.getClass();
ta.recycle();
} catch (Resources.NotFoundException nfe) {
XposedBridge.log(nfe);
}
ResourcesHook.removeFinalFlagNative(resClass);
ResourcesHook.removeFinalFlagNative(taClass);
ClassLoader myCL = XposedBridge.class.getClassLoader();
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass");
setObjectField(myCL, "parent", dummyClassLoader);
} catch (Throwable throwable) {
XposedBridge.log(throwable);
XposedInit.disableResources = true;
}
}
/**
* Returns the currently installed version of the Xposed framework.
*/
public static int getXposedVersion() {
return BuildConfig.API_CODE;
}
/**
* Returns the currently installed version of the Xposed framework.
*/
public static int getXposedVersion() {
return BuildConfig.API_CODE;
}
/**
* 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.
* If you want to write information/debug messages, use logcat.
*
* @param text The log message.
*/
public synchronized static void log(String text) {
Log.i(TAG, text);
ModuleLogger.log(text, false);
}
/**
* 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.
* If you want to write information/debug messages, use logcat.
*
* @param text The log message.
*/
public synchronized static void log(String text) {
Log.i(TAG, text);
ModuleLogger.log(text, false);
}
/**
* 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.
* If you want to write information/debug messages, use logcat.
*
* @param t The Throwable object for the stack trace.
*/
public synchronized static void log(Throwable t) {
String logStr = Log.getStackTraceString(t);
Log.e(TAG, logStr);
ModuleLogger.log(logStr, true);
}
/**
* 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.
* If you want to write information/debug messages, use logcat.
*
* @param t The Throwable object for the stack trace.
*/
public synchronized static void log(Throwable t) {
String logStr = Log.getStackTraceString(t);
Log.e(TAG, logStr);
ModuleLogger.log(logStr, true);
}
/**
* 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.
*
* @param hookMethod The method to be hooked.
* @param callback The callback to be executed when the hooked method is called.
* @return An object that can be used to remove the hook.
*
* @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
* @see XposedHelpers#findAndHookMethod(Class, String, Object...)
* @see #hookAllMethods
* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
* @see XposedHelpers#findAndHookConstructor(Class, Object...)
* @see #hookAllConstructors
*/
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
if (!(hookMethod instanceof Executable)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
}
// No check interface because there may be default methods
/**
* 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.
*
* @param hookMethod The method to be hooked.
* @param callback The callback to be executed when the hooked method is called.
* @return An object that can be used to remove the hook.
* @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
* @see XposedHelpers#findAndHookMethod(Class, String, Object...)
* @see #hookAllMethods
* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
* @see XposedHelpers#findAndHookConstructor(Class, Object...)
* @see #hookAllConstructors
*/
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
if (!(hookMethod instanceof Executable)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
}
// No check interface because there may be default methods
/*else if (hookMethod.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
}*/
else if (Modifier.isAbstract(hookMethod.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
}
else if (Modifier.isAbstract(hookMethod.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
}
Executable targetMethod = (Executable) hookMethod;
Executable targetMethod = (Executable) hookMethod;
if (callback == null) {
throw new IllegalArgumentException("callback should not be null!");
}
if (callback == null) {
throw new IllegalArgumentException("callback should not be null!");
}
boolean newMethod = false;
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(targetMethod);
if (callbacks == null) {
callbacks = new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(targetMethod, callbacks);
newMethod = true;
}
}
callbacks.add(callback);
boolean newMethod = false;
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(targetMethod);
if (callbacks == null) {
callbacks = new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(targetMethod, callbacks);
newMethod = true;
}
}
callbacks.add(callback);
if (newMethod) {
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
if (newMethod) {
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
if (!YahfaHooker.shouldDelayHook(targetMethod)) {
YahfaHooker.hookMethod(targetMethod, additionalInfo);
} else {
PendingHooks.recordPendingMethod((Method)hookMethod, additionalInfo);
}
YahfaHooker.hookMethod(targetMethod, additionalInfo);
} else {
PendingHooks.recordPendingMethod((Method) hookMethod, additionalInfo);
}
}
return callback.new Unhook(hookMethod);
}
}
/**
* Removes the callback for a hooked method/constructor.
*
* @deprecated Use {@link XC_MethodHook.Unhook#unhook} instead. An instance of the {@code Unhook}
* class is returned when you hook the method.
*
* @param hookMethod The method for which the callback should be removed.
* @param callback The reference to the callback as specified in {@link #hookMethod}.
*/
@Deprecated
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod);
if (callbacks == null)
return;
}
callbacks.remove(callback);
}
/**
* Removes the callback for a hooked method/constructor.
*
* @param hookMethod The method for which the callback should be removed.
* @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}
* class is returned when you hook the method.
*/
@Deprecated
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod);
if (callbacks == null)
return;
}
callbacks.remove(callback);
}
/**
* Hooks all methods with a certain name that were declared in the specified class. Inherited
* methods and constructors are not considered. For constructors, use
* {@link #hookAllConstructors} instead.
*
* @param hookClass The class to check for declared methods.
* @param methodName The name of the method(s) to hook.
* @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.
*/
@SuppressWarnings("UnusedReturnValue")
public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
for (Member method : hookClass.getDeclaredMethods())
if (method.getName().equals(methodName))
unhooks.add(hookMethod(method, callback));
return unhooks;
}
/**
* Hooks all methods with a certain name that were declared in the specified class. Inherited
* methods and constructors are not considered. For constructors, use
* {@link #hookAllConstructors} instead.
*
* @param hookClass The class to check for declared methods.
* @param methodName The name of the method(s) to hook.
* @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.
*/
@SuppressWarnings("UnusedReturnValue")
public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
for (Member method : hookClass.getDeclaredMethods())
if (method.getName().equals(methodName))
unhooks.add(hookMethod(method, callback));
return unhooks;
}
/**
* Hook all constructors of the specified class.
*
* @param hookClass The class to check for constructors.
* @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.
*/
@SuppressWarnings("UnusedReturnValue")
public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
for (Member constructor : hookClass.getDeclaredConstructors())
unhooks.add(hookMethod(constructor, callback));
return unhooks;
}
/**
* Hook all constructors of the specified class.
*
* @param hookClass The class to check for constructors.
* @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.
*/
@SuppressWarnings("UnusedReturnValue")
public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {
Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();
for (Member constructor : hookClass.getDeclaredConstructors())
unhooks.add(hookMethod(constructor, callback));
return unhooks;
}
/**
* 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}
* in your module class and Xposed will take care of registering it as a callback.
*
* @param callback The callback to be executed.
* @hide
*/
public static void hookLoadPackage(XC_LoadPackage callback) {
synchronized (sLoadedPackageCallbacks) {
sLoadedPackageCallbacks.add(callback);
}
}
/**
* 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}
* in your module class and Xposed will take care of registering it as a callback.
*
* @param callback The callback to be executed.
* @hide
*/
public static void hookLoadPackage(XC_LoadPackage callback) {
synchronized (sLoadedPackageCallbacks) {
sLoadedPackageCallbacks.add(callback);
}
}
public static void clearLoadedPackages() {
synchronized (sLoadedPackageCallbacks) {
sLoadedPackageCallbacks.clear();
}
}
public static void clearLoadedPackages() {
synchronized (sLoadedPackageCallbacks) {
sLoadedPackageCallbacks.clear();
}
}
/**
* 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}
* in your module class and Xposed will take care of registering it as a callback.
*
* @param callback The callback to be executed.
* @hide
*/
public static void hookInitPackageResources(XC_InitPackageResources callback) {
synchronized (sInitPackageResourcesCallbacks) {
sInitPackageResourcesCallbacks.add(callback);
}
}
/**
* 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}
* in your module class and Xposed will take care of registering it as a callback.
*
* @param callback The callback to be executed.
* @hide
*/
public static void hookInitPackageResources(XC_InitPackageResources callback) {
synchronized (sInitPackageResourcesCallbacks) {
sInitPackageResourcesCallbacks.add(callback);
}
}
public static void hookInitZygote(XC_InitZygote callback) {
synchronized (sInitZygoteCallbacks) {
sInitZygoteCallbacks.add(callback);
}
}
public static void hookInitZygote(XC_InitZygote callback) {
synchronized (sInitZygoteCallbacks) {
sInitZygoteCallbacks.add(callback);
}
}
/**
* 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.
*
* <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
* 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}
* if the original method should be skipped.
*
* @param method The method to be called.
* @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.
* @param args Arguments for the method call as Object[] array.
* @return The result returned from the invoked method.
* @throws NullPointerException
* if {@code receiver == null} for a non-static method
* @throws IllegalAccessException
* if this method is not accessible (see {@link AccessibleObject})
* @throws IllegalArgumentException
* if the number of arguments doesn't match the number of parameters, the receiver
* is incompatible with the declaring class, or an argument could not be unboxed
* or converted by a widening conversion to the corresponding parameter type
* @throws InvocationTargetException
* if an exception was thrown by the invoked method
*/
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
throws Throwable {
if (args == null) {
args = EMPTY_ARRAY;
}
/**
* 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.
*
* <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
* 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}
* if the original method should be skipped.
*
* @param method The method to be called.
* @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.
* @param args Arguments for the method call as Object[] array.
* @return The result returned from the invoked method.
* @throws NullPointerException if {@code receiver == null} for a non-static method
* @throws IllegalAccessException if this method is not accessible (see {@link AccessibleObject})
* @throws IllegalArgumentException if the number of arguments doesn't match the number of parameters, the receiver
* is incompatible with the declaring class, or an argument could not be unboxed
* or converted by a widening conversion to the corresponding parameter type
* @throws InvocationTargetException if an exception was thrown by the invoked method
*/
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
throws Throwable {
if (args == null) {
args = EMPTY_ARRAY;
}
if (!(method instanceof Executable)) {
throw new IllegalArgumentException("method must be of type Method or Constructor");
}
if (!(method instanceof Executable)) {
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> {
@Override
public Collection values() {
return Collections.EMPTY_LIST;
}
private static class NoValuesHashMap<K, V> extends HashMap<K, V> {
@Override
public Collection values() {
return Collections.EMPTY_LIST;
}
@Override
public void clear() {
@Override
public void clear() {
}
}
@Override
public Set<K> keySet() {
return Collections.EMPTY_SET;
}
@Override
public Set<K> keySet() {
return Collections.EMPTY_SET;
}
@Override
public Set<Entry<K, V>> entrySet() {
return Collections.EMPTY_SET;
}
@Override
public Set<Entry<K, V>> entrySet() {
return Collections.EMPTY_SET;
}
@Override
public int size() {
return 0;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
}
@Override
public boolean isEmpty() {
return true;
}
}
/** @hide */
public static final class CopyOnWriteSortedSet<E> {
private transient volatile Object[] elements = EMPTY_ARRAY;
/**
* @hide
*/
public static final class CopyOnWriteSortedSet<E> {
private transient volatile Object[] elements = EMPTY_ARRAY;
@SuppressWarnings("UnusedReturnValue")
public synchronized boolean add(E e) {
int index = indexOf(e);
if (index >= 0)
return false;
@SuppressWarnings("UnusedReturnValue")
public synchronized boolean add(E e) {
int index = indexOf(e);
if (index >= 0)
return false;
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
Arrays.sort(newElements);
elements = newElements;
return true;
}
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
Arrays.sort(newElements);
elements = newElements;
return true;
}
@SuppressWarnings("UnusedReturnValue")
public synchronized boolean remove(E e) {
int index = indexOf(e);
if (index == -1)
return false;
@SuppressWarnings("UnusedReturnValue")
public synchronized boolean remove(E e) {
int index = indexOf(e);
if (index == -1)
return false;
Object[] newElements = new Object[elements.length - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);
elements = newElements;
return true;
}
Object[] newElements = new Object[elements.length - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);
elements = newElements;
return true;
}
private int indexOf(Object o) {
for (int i = 0; i < elements.length; i++) {
if (o.equals(elements[i]))
return i;
}
return -1;
}
private int indexOf(Object o) {
for (int i = 0; i < elements.length; i++) {
if (o.equals(elements[i]))
return i;
}
return -1;
}
public Object[] getSnapshot() {
return elements;
}
public Object[] getSnapshot() {
return elements;
}
public synchronized void clear() {
elements = EMPTY_ARRAY;
}
}
public synchronized void clear() {
elements = EMPTY_ARRAY;
}
}
public static class AdditionalHookInfo {
public final CopyOnWriteSortedSet<XC_MethodHook> callbacks;
public static class AdditionalHookInfo {
public final CopyOnWriteSortedSet<XC_MethodHook> callbacks;
private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks) {
this.callbacks = callbacks;
}
}
private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks) {
this.callbacks = callbacks;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,18 @@
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.content.pm.ApplicationInfo;
import android.content.res.Resources;
@ -32,7 +44,8 @@ import android.os.Process;
import android.util.ArraySet;
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.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.XCallback;
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 {
private static final String TAG = XposedBridge.TAG;
public static boolean startsSystemServer = false;
private static final String startClassName = ""; // ed: no support for tool process anymore
public static volatile boolean disableResources = false;
private XposedInit() {
}
/**
* 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 {
public static void hookResources() throws Throwable {
if (!serviceClient.isResourcesHookEnabled() || disableResources) {
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);
if (hasLoaded) {
return false;
@ -256,7 +227,7 @@ public final class XposedInit {
newLoadedApk.add(apk);
} else {
loadedModules.add(apk); // temporarily add it for XSharedPreference
boolean loadSuccess = loadModule(apk, callInitZygote);
boolean loadSuccess = loadModule(apk);
if (loadSuccess) {
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");
if (is == null) {
return true;
@ -359,9 +330,7 @@ public final class XposedInit {
XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper(
(IXposedHookZygoteInit) moduleInstance, param));
if (callInitZygote) {
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
}
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
}
if (moduleInstance instanceof IXposedHookLoadPackage)
@ -390,7 +359,7 @@ public final class XposedInit {
* in <code>assets/xposed_init</code>.
*/
@SuppressLint("PrivateApi")
private static boolean loadModule(String apk, boolean callInitZygote) {
private static boolean loadModule(String apk) {
Log.i(TAG, "Loading modules from " + apk);
if (!new File(apk).exists()) {
@ -418,7 +387,7 @@ public final class XposedInit {
} catch (ClassNotFoundException ignored) {
}
boolean res = initModule(mcl, apk, callInitZygote);
boolean res = initModule(mcl, apk);
res = res && initNativeModule(mcl, apk);
return res;
}

View File

@ -33,13 +33,13 @@ import de.robv.android.xposed.IXposedHookZygoteInit;
* @param <T> The class of the callback.
*/
public interface IXUnhook<T> {
/**
* Returns the callback that has been registered.
*/
T getCallback();
/**
* Returns the callback that has been registered.
*/
T getCallback();
/**
* Removes the callback.
*/
void unhook();
/**
* Removes the callback.
*/
void unhook();
}

View File

@ -30,48 +30,55 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
* subclass.
*/
public abstract class XC_InitPackageResources extends XCallback implements IXposedHookInitPackageResources {
/**
* Creates a new callback with default priority.
* @hide
*/
@SuppressWarnings("deprecation")
public XC_InitPackageResources() {
super();
}
/**
* Creates a new callback with default priority.
*
* @hide
*/
@SuppressWarnings("deprecation")
public XC_InitPackageResources() {
super();
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
* @hide
*/
public XC_InitPackageResources(int priority) {
super(priority);
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
* @hide
*/
public XC_InitPackageResources(int priority) {
super(priority);
}
/**
* Wraps information about the resources being initialized.
*/
public static final class InitPackageResourcesParam extends XCallback.Param {
/** @hide */
public InitPackageResourcesParam(CopyOnWriteSortedSet<XC_InitPackageResources> callbacks) {
super(callbacks);
}
/**
* Wraps information about the resources being initialized.
*/
public static final class InitPackageResourcesParam extends XCallback.Param {
/**
* @hide
*/
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
* {@link XResources#setReplacement(String, String, String, Object)}.
*/
public XResources res;
}
/**
* Reference to the resources that can be used for calls to
* {@link XResources#setReplacement(String, String, String, Object)}.
*/
public XResources res;
}
/** @hide */
@Override
protected void call(Param param) throws Throwable {
if (param instanceof InitPackageResourcesParam)
handleInitPackageResources((InitPackageResourcesParam) param);
}
/**
* @hide
*/
@Override
protected void call(Param param) throws Throwable {
if (param instanceof InitPackageResourcesParam)
handleInitPackageResources((InitPackageResourcesParam) param);
}
}

View File

@ -31,89 +31,103 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
* and its variants.
*/
public abstract class XC_LayoutInflated extends XCallback {
/**
* Creates a new callback with default priority.
*/
@SuppressWarnings("deprecation")
public XC_LayoutInflated() {
super();
}
/**
* Creates a new callback with default priority.
*/
@SuppressWarnings("deprecation")
public XC_LayoutInflated() {
super();
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_LayoutInflated(int priority) {
super(priority);
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
*/
public XC_LayoutInflated(int priority) {
super(priority);
}
/**
* Wraps information about the inflated layout.
*/
public static final class LayoutInflatedParam extends XCallback.Param {
/** @hide */
public LayoutInflatedParam(CopyOnWriteSortedSet<XC_LayoutInflated> callbacks) {
super(callbacks);
}
/**
* Wraps information about the inflated layout.
*/
public static final class LayoutInflatedParam extends XCallback.Param {
/**
* @hide
*/
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
protected void call(Param param) throws Throwable {
if (param instanceof LayoutInflatedParam)
handleLayoutInflated((LayoutInflatedParam) param);
}
/**
* @hide
*/
@Override
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.
*
* @param liparam Information about the layout and the inflated view.
* @throws Throwable Everything the callback throws is caught and logged.
*/
public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable;
/**
* This method is called when the hooked layout has been inflated.
*
* @param liparam Information about the layout and the inflated view.
* @throws Throwable Everything the callback throws is caught and logged.
*/
public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable;
/**
* An object with which the callback can be removed.
*/
public class Unhook implements IXUnhook<XC_LayoutInflated> {
private final String resDir;
private final int id;
/**
* An object with which the callback can be removed.
*/
public class Unhook implements IXUnhook<XC_LayoutInflated> {
private final String resDir;
private final int id;
/** @hide */
public Unhook(String resDir, int id) {
this.resDir = resDir;
this.id = id;
}
/**
* @hide
*/
public Unhook(String resDir, int id) {
this.resDir = resDir;
this.id = id;
}
/**
* Returns the resource ID of the hooked layout.
*/
public int getId() {
return id;
}
/**
* Returns the resource ID of the hooked layout.
*/
public int getId() {
return id;
}
@Override
public XC_LayoutInflated getCallback() {
return XC_LayoutInflated.this;
}
@Override
public XC_LayoutInflated getCallback() {
return XC_LayoutInflated.this;
}
@Override
public void unhook() {
XResources.unhookLayout(resDir, id, XC_LayoutInflated.this);
}
@Override
public void unhook() {
XResources.unhookLayout(resDir, id, XC_LayoutInflated.this);
}
}
}
}

View File

@ -30,54 +30,69 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
* subclass.
*/
public abstract class XC_LoadPackage extends XCallback implements IXposedHookLoadPackage {
/**
* Creates a new callback with default priority.
* @hide
*/
@SuppressWarnings("deprecation")
public XC_LoadPackage() {
super();
}
/**
* Creates a new callback with default priority.
*
* @hide
*/
@SuppressWarnings("deprecation")
public XC_LoadPackage() {
super();
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
* @hide
*/
public XC_LoadPackage(int priority) {
super(priority);
}
/**
* Creates a new callback with a specific priority.
*
* @param priority See {@link XCallback#priority}.
* @hide
*/
public XC_LoadPackage(int priority) {
super(priority);
}
/**
* Wraps information about the app being loaded.
*/
public static final class LoadPackageParam extends XCallback.Param {
/** @hide */
public LoadPackageParam(CopyOnWriteSortedSet<XC_LoadPackage> callbacks) {
super(callbacks);
}
/**
* Wraps information about the app being loaded.
*/
public static final class LoadPackageParam extends XCallback.Param {
/**
* @hide
*/
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
protected void call(Param param) throws Throwable {
if (param instanceof LoadPackageParam)
handleLoadPackage((LoadPackageParam) param);
}
/**
* @hide
*/
@Override
protected void call(Param param) throws Throwable {
if (param instanceof LoadPackageParam)
handleLoadPackage((LoadPackageParam) param);
}
}

View File

@ -27,147 +27,174 @@ import java.io.Serializable;
import de.robv.android.xposed.IModuleContext;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
/**
* Base class for Xposed callbacks.
*
* <p>
* This class only keeps a priority for ordering multiple callbacks.
* The actual (abstract) callback methods are added by subclasses.
*/
public abstract class XCallback implements Comparable<XCallback>, IModuleContext {
/**
* Callback priority, higher number means earlier execution.
*
* <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}
* can be set instead. The values are just for orientation though, Xposed doesn't enforce any
* boundaries on the priority values.
*/
public final int priority;
/**
* Callback priority, higher number means earlier execution.
*
* <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}
* can be set instead. The values are just for orientation though, Xposed doesn't enforce any
* boundaries on the priority values.
*/
public final int priority;
/** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */
@Deprecated
public XCallback() {
this.priority = PRIORITY_DEFAULT;
}
/**
* @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!
*/
@Deprecated
public XCallback() {
this.priority = PRIORITY_DEFAULT;
}
/** @hide */
public XCallback(int priority) {
this.priority = priority;
}
/**
* @hide
*/
public XCallback(int priority) {
this.priority = priority;
}
/**
* Base class for Xposed callback parameters.
*/
public static abstract class Param {
/** @hide */
public final Object[] callbacks;
private Bundle extra;
/**
* Base class for Xposed callback parameters.
*/
public static abstract class Param {
/**
* @hide
*/
public final Object[] callbacks;
private Bundle extra;
/** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */
@Deprecated
protected Param() {
callbacks = null;
}
/**
* @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!
*/
@Deprecated
protected Param() {
callbacks = null;
}
/** @hide */
protected Param(CopyOnWriteSortedSet<? extends XCallback> callbacks) {
this.callbacks = callbacks.getSnapshot();
}
/**
* @hide
*/
protected Param(CopyOnWriteSortedSet<? extends XCallback> callbacks) {
this.callbacks = callbacks.getSnapshot();
}
/**
* 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
* separate call to a method, even when the same method is called recursively.
*
* @see #setObjectExtra
* @see #getObjectExtra
*/
public synchronized Bundle getExtra() {
if (extra == null)
extra = new Bundle();
return extra;
}
/**
* 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
* separate call to a method, even when the same method is called recursively.
*
* @see #setObjectExtra
* @see #getObjectExtra
*/
public synchronized Bundle getExtra() {
if (extra == null)
extra = new Bundle();
return extra;
}
/**
* Returns an object stored with {@link #setObjectExtra}.
*/
public Object getObjectExtra(String key) {
Serializable o = getExtra().getSerializable(key);
if (o instanceof SerializeWrapper)
return ((SerializeWrapper) o).object;
return null;
}
/**
* Returns an object stored with {@link #setObjectExtra}.
*/
public Object getObjectExtra(String key) {
Serializable o = getExtra().getSerializable(key);
if (o instanceof SerializeWrapper)
return ((SerializeWrapper) o).object;
return null;
}
/**
* Stores any object for the scope of the callback. For data types that support it, use
* the {@link Bundle} returned by {@link #getExtra} instead.
*/
public void setObjectExtra(String key, Object o) {
getExtra().putSerializable(key, new SerializeWrapper(o));
}
/**
* Stores any object for the scope of the callback. For data types that support it, use
* the {@link Bundle} returned by {@link #getExtra} instead.
*/
public void setObjectExtra(String key, Object o) {
getExtra().putSerializable(key, new SerializeWrapper(o));
}
private static class SerializeWrapper implements Serializable {
private static final long serialVersionUID = 1L;
private final Object object;
public SerializeWrapper(Object o) {
object = o;
}
}
}
private static class SerializeWrapper implements Serializable {
private static final long serialVersionUID = 1L;
private final Object object;
/** @hide */
public static void callAll(Param param) {
public SerializeWrapper(Object o) {
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
XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;
PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);
}
XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;
PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);
}
if (param.callbacks == null)
throw new IllegalStateException("This object was not created for use with callAll");
if (param.callbacks == null)
throw new IllegalStateException("This object was not created for use with callAll");
for (int i = 0; i < param.callbacks.length; i++) {
try {
((XCallback) param.callbacks[i]).call(param);
} catch (Throwable t) { XposedBridge.log(t); }
}
}
for (int i = 0; i < param.callbacks.length; i++) {
try {
((XCallback) param.callbacks[i]).call(param);
} catch (Throwable t) {
XposedBridge.log(t);
}
}
}
/** @hide */
protected void call(Param param) throws Throwable {}
/**
* @hide
*/
protected void call(Param param) throws Throwable {
}
@Override
public String getApkPath() {
return "";
}
@Override
public String getApkPath() {
return "";
}
/** @hide */
@Override
public int compareTo(XCallback other) {
if (this == other)
return 0;
/**
* @hide
*/
@Override
public int compareTo(XCallback other) {
if (this == other)
return 0;
// order descending by priority
if (other.priority != this.priority)
return other.priority - this.priority;
// then randomly
else if (System.identityHashCode(this) < System.identityHashCode(other))
return -1;
else
return 1;
}
// order descending by priority
if (other.priority != this.priority)
return other.priority - this.priority;
// then randomly
else if (System.identityHashCode(this) < System.identityHashCode(other))
return -1;
else
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;
}

View File

@ -33,167 +33,178 @@ import de.robv.android.xposed.SELinuxHelper;
* <p>References to a concrete subclass should generally be retrieved from {@link SELinuxHelper}.
*/
public abstract class BaseService {
/** Flag for {@link #checkFileAccess}: Read access. */
public static final int R_OK = 4;
/** Flag for {@link #checkFileAccess}: Write access. */
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;
/**
* Flag for {@link #checkFileAccess}: Read access.
*/
public static final int R_OK = 4;
/**
* Flag for {@link #checkFileAccess}: Write access.
*/
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).
*
* @return {@code true} in case direct access is possible.
*/
public boolean hasDirectFileAccess() {
return false;
}
/**
* Checks whether the services accesses files directly (instead of using IPC).
*
* @return {@code true} in case direct access is possible.
*/
public boolean hasDirectFileAccess() {
return false;
}
/**
* Check whether a file is accessible. SELinux might enforce stricter checks.
*
* @param filename The absolute path of the file to check.
* @param mode The mode for POSIX's {@code access()} function.
* @return The result of the {@code access()} function.
*/
public abstract boolean checkFileAccess(String filename, int mode);
/**
* Check whether a file is accessible. SELinux might enforce stricter checks.
*
* @param filename The absolute path of the file to check.
* @param mode The mode for POSIX's {@code access()} function.
* @return The result of the {@code access()} function.
*/
public abstract boolean checkFileAccess(String filename, int mode);
/**
* Check whether a file exists.
*
* @param filename The absolute path of the file to check.
* @return The result of the {@code access()} function.
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean checkFileExists(String filename) {
return checkFileAccess(filename, F_OK);
}
/**
* Check whether a file exists.
*
* @param filename The absolute path of the file to check.
* @return The result of the {@code access()} function.
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean checkFileExists(String filename) {
return checkFileAccess(filename, F_OK);
}
/**
* Determine the size and modification time of a file.
*
* @param filename The absolute path of the file to check.
* @return A {@link FileResult} object holding the result.
* @throws IOException In case an error occurred while retrieving the information.
*/
public abstract FileResult statFile(String filename) throws IOException;
/**
* Determine the size and modification time of a file.
*
* @param filename The absolute path of the file to check.
* @return A {@link FileResult} object holding the result.
* @throws IOException In case an error occurred while retrieving the information.
*/
public abstract FileResult statFile(String filename) throws IOException;
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file size.
* @throws IOException In case an error occurred while retrieving the information.
*/
public long getFileSize(String filename) throws IOException {
return statFile(filename).size;
}
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file size.
* @throws IOException In case an error occurred while retrieving the information.
*/
public long getFileSize(String filename) throws IOException {
return statFile(filename).size;
}
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file modification time.
* @throws IOException In case an error occurred while retrieving the information.
*/
public long getFileModificationTime(String filename) throws IOException {
return statFile(filename).mtime;
}
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file modification time.
* @throws IOException In case an error occurred while retrieving the information.
*/
public long getFileModificationTime(String filename) throws IOException {
return statFile(filename).mtime;
}
/**
* Read a file into memory.
*
* @param filename The absolute path of the file to read.
* @return A {@code byte} array with the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public abstract byte[] readFile(String filename) throws IOException;
/**
* Read a file into memory.
*
* @param filename The absolute path of the file to read.
* @return A {@code byte} array with the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public abstract byte[] readFile(String filename) throws IOException;
/**
* 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 previousSize File size of last read.
* @param previousTime File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public abstract FileResult readFile(String filename, long previousSize, long previousTime) throws IOException;
/**
* 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 previousSize File size of last read.
* @param previousTime File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
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.
*
* @param filename The absolute path of the file to read.
* @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 previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public abstract FileResult readFile(String filename, int offset, int length,
long previousSize, long previousTime) throws IOException;
/**
* 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 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 previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public abstract FileResult readFile(String filename, int offset, int length,
long previousSize, long previousTime) throws IOException;
/**
* Get a stream to the file content.
* Depending on the service, it may or may not be read completely into memory.
*
* @param filename The absolute path of the file to read.
* @return An {@link InputStream} to the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public InputStream getFileInputStream(String filename) throws IOException {
return new ByteArrayInputStream(readFile(filename));
}
/**
* Get a stream to the file content.
* Depending on the service, it may or may not be read completely into memory.
*
* @param filename The absolute path of the file to read.
* @return An {@link InputStream} to the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public InputStream getFileInputStream(String filename) throws IOException {
return new ByteArrayInputStream(readFile(filename));
}
/**
* 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.
*
* @param filename The absolute path of the file to read.
* @param previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#stream} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
FileResult result = readFile(filename, previousSize, previousTime);
if (result.content == null)
return result;
return new FileResult(new ByteArrayInputStream(result.content), result.size, result.mtime);
}
/**
* 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.
*
* @param filename The absolute path of the file to read.
* @param previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#stream} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
FileResult result = readFile(filename, previousSize, previousTime);
if (result.content == null)
return result;
return new FileResult(new ByteArrayInputStream(result.content), result.size, result.mtime);
}
// ----------------------------------------------------------------------------
/*package*/ BaseService() {}
// ----------------------------------------------------------------------------
/*package*/ BaseService() {
}
/*package*/ static void ensureAbsolutePath(String filename) {
if (!filename.startsWith("/")) {
throw new IllegalArgumentException("Only absolute filenames are allowed: " + filename);
}
}
/*package*/
static void ensureAbsolutePath(String 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 {
switch (errno) {
case 1: // EPERM
case 13: // EACCES
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Permission denied: " + filename);
case 2: // ENOENT
throw new FileNotFoundException(errorMsg != null ? errorMsg : "No such file or directory: " + filename);
case 12: // ENOMEM
throw new OutOfMemoryError(errorMsg);
case 21: // EISDIR
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Is a directory: " + filename);
default:
throw new IOException(errorMsg != null ? errorMsg : "Error " + errno + defaultText + filename);
}
}
/*package*/
static void throwCommonIOException(int errno, String errorMsg, String filename, String defaultText) throws IOException {
switch (errno) {
case 1: // EPERM
case 13: // EACCES
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Permission denied: " + filename);
case 2: // ENOENT
throw new FileNotFoundException(errorMsg != null ? errorMsg : "No such file or directory: " + filename);
case 12: // ENOMEM
throw new OutOfMemoryError(errorMsg);
case 21: // EISDIR
throw new FileNotFoundException(errorMsg != null ? errorMsg : "Is a directory: " + filename);
default:
throw new IOException(errorMsg != null ? errorMsg : "Error " + errno + defaultText + filename);
}
}
}

View File

@ -26,108 +26,110 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/** @hide */
/**
* @hide
*/
public final class DirectAccessService extends BaseService {
@Override
public boolean hasDirectFileAccess() {
return true;
}
@Override
public boolean hasDirectFileAccess() {
return true;
}
@SuppressWarnings("RedundantIfStatement")
@Override
public boolean checkFileAccess(String filename, int mode) {
File file = new File(filename);
if (mode == F_OK && !file.exists()) return false;
if ((mode & R_OK) != 0 && !file.canRead()) return false;
if ((mode & W_OK) != 0 && !file.canWrite()) return false;
if ((mode & X_OK) != 0 && !file.canExecute()) return false;
return true;
}
@SuppressWarnings("RedundantIfStatement")
@Override
public boolean checkFileAccess(String filename, int mode) {
File file = new File(filename);
if (mode == F_OK && !file.exists()) return false;
if ((mode & R_OK) != 0 && !file.canRead()) return false;
if ((mode & W_OK) != 0 && !file.canWrite()) return false;
if ((mode & X_OK) != 0 && !file.canExecute()) return false;
return true;
}
@Override
public boolean checkFileExists(String filename) {
return new File(filename).exists();
}
@Override
public boolean checkFileExists(String filename) {
return new File(filename).exists();
}
@Override
public FileResult statFile(String filename) throws IOException {
File file = new File(filename);
return new FileResult(file.length(), file.lastModified());
}
@Override
public FileResult statFile(String filename) throws IOException {
File file = new File(filename);
return new FileResult(file.length(), file.lastModified());
}
@Override
public byte[] readFile(String filename) throws IOException {
File file = new File(filename);
byte content[] = new byte[(int)file.length()];
FileInputStream fis = new FileInputStream(file);
fis.read(content);
fis.close();
return content;
}
@Override
public byte[] readFile(String filename) throws IOException {
File file = new File(filename);
byte content[] = new byte[(int) file.length()];
FileInputStream fis = new FileInputStream(file);
fis.read(content);
fis.close();
return content;
}
@Override
public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
return new FileResult(readFile(filename), size, time);
}
@Override
public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
return new FileResult(readFile(filename), size, time);
}
@Override
public FileResult readFile(String filename, int offset, int length, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
@Override
public FileResult readFile(String filename, int offset, int length, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
// Shortcut for the simple case
if (offset <= 0 && length <= 0)
return new FileResult(readFile(filename), size, time);
// Shortcut for the simple case
if (offset <= 0 && length <= 0)
return new FileResult(readFile(filename), size, time);
// Check range
if (offset > 0 && offset >= size) {
throw new IllegalArgumentException("Offset " + offset + " is out of range for " + filename);
} else if (offset < 0) {
offset = 0;
}
// Check range
if (offset > 0 && offset >= size) {
throw new IllegalArgumentException("Offset " + offset + " is out of range for " + filename);
} else if (offset < 0) {
offset = 0;
}
if (length > 0 && (offset + length) > size) {
throw new IllegalArgumentException("Length " + length + " is out of range for " + filename);
} else if (length <= 0) {
length = (int) (size - offset);
}
if (length > 0 && (offset + length) > size) {
throw new IllegalArgumentException("Length " + length + " is out of range for " + filename);
} else if (length <= 0) {
length = (int) (size - offset);
}
byte content[] = new byte[length];
FileInputStream fis = new FileInputStream(file);
fis.skip(offset);
fis.read(content);
fis.close();
return new FileResult(content, size, time);
}
byte content[] = new byte[length];
FileInputStream fis = new FileInputStream(file);
fis.skip(offset);
fis.read(content);
fis.close();
return new FileResult(content, size, time);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public InputStream getFileInputStream(String filename) throws IOException {
return new BufferedInputStream(new FileInputStream(filename), 16*1024);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public InputStream getFileInputStream(String filename) throws IOException {
return new BufferedInputStream(new FileInputStream(filename), 16 * 1024);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
return new FileResult(new BufferedInputStream(new FileInputStream(filename), 16*1024), size, time);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {
File file = new File(filename);
long size = file.length();
long time = file.lastModified();
if (previousSize == size && previousTime == time)
return new FileResult(size, time);
return new FileResult(new BufferedInputStream(new FileInputStream(filename), 16 * 1024), size, time);
}
}

View File

@ -26,55 +26,65 @@ import java.io.InputStream;
* Holder for the result of a {@link BaseService#readFile} or {@link BaseService#statFile} call.
*/
public final class FileResult {
/** File content, might be {@code null} if the file wasn't read. */
public final byte[] content;
/** File input stream, might be {@code null} if the file wasn't read. */
public final InputStream stream;
/** File size. */
public final long size;
/** File last modification time. */
public final long mtime;
/**
* File content, might be {@code null} if the file wasn't read.
*/
public final byte[] content;
/**
* File input stream, might be {@code null} if the file wasn't read.
*/
public final InputStream stream;
/**
* File size.
*/
public final long size;
/**
* File last modification time.
*/
public final long mtime;
/*package*/ FileResult(long size, long mtime) {
this.content = null;
this.stream = null;
this.size = size;
this.mtime = mtime;
}
/*package*/ FileResult(long size, long mtime) {
this.content = null;
this.stream = null;
this.size = size;
this.mtime = mtime;
}
/*package*/ FileResult(byte[] content, long size, long mtime) {
this.content = content;
this.stream = null;
this.size = size;
this.mtime = mtime;
}
/*package*/ FileResult(byte[] content, long size, long mtime) {
this.content = content;
this.stream = null;
this.size = size;
this.mtime = mtime;
}
/*package*/ FileResult(InputStream stream, long size, long mtime) {
this.content = null;
this.stream = stream;
this.size = size;
this.mtime = mtime;
}
/*package*/ FileResult(InputStream stream, long size, long mtime) {
this.content = null;
this.stream = stream;
this.size = size;
this.mtime = mtime;
}
/** @hide */
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
if (content != null) {
sb.append("content.length: ");
sb.append(content.length);
sb.append(", ");
}
if (stream != null) {
sb.append("stream: ");
sb.append(stream.toString());
sb.append(", ");
}
sb.append("size: ");
sb.append(size);
sb.append(", mtime: ");
sb.append(mtime);
sb.append("}");
return sb.toString();
}
/**
* @hide
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
if (content != null) {
sb.append("content.length: ");
sb.append(content.length);
sb.append(", ");
}
if (stream != null) {
sb.append("stream: ");
sb.append(stream.toString());
sb.append(", ");
}
sb.append("size: ");
sb.append(size);
sb.append(", mtime: ");
sb.append(mtime);
sb.append("}");
return sb.toString();
}
}

View File

@ -79,15 +79,15 @@ public class Main {
// Initialize the Xposed framework
try {
startBootstrapHook(isSystem, appDataDir);
XposedInit.initForZygote();
XposedInit.hookResources();
} catch (Throwable t) {
Utils.logE("error during Xposed initialization", t);
}
}
private static void loadModulesSafely(boolean callInitZygote) {
private static void loadModulesSafely() {
try {
XposedInit.loadModules(callInitZygote);
XposedInit.loadModules();
} catch (Exception exception) {
Utils.logE("error loading module list", exception);
}
@ -101,7 +101,7 @@ public class Main {
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
installBootstrapHooks(isSystem, appDataDir);
Utils.logI("Loading modules for " + niceName);
loadModulesSafely(true);
loadModulesSafely();
}
public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {

View File

@ -48,7 +48,7 @@ import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
public class HandleBindAppHooker extends XC_MethodHook {
String appDataDir = null;
String appDataDir;
public HandleBindAppHooker(String appDataDir) {
this.appDataDir = appDataDir;

View File

@ -20,8 +20,6 @@
package org.lsposed.lspd.hooker;
import android.os.Build;
import org.lsposed.lspd.util.Hookers;
import de.robv.android.xposed.XC_MethodHook;
@ -60,7 +58,7 @@ public class StartBootstrapServicesHooker extends XC_MethodHook {
}
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,
"dexEntryExists", String.class,
XC_MethodReplacement.returnConstant(true));