diff --git a/app/src/main/java/de/robv/android/xposed/XposedBridge.java b/app/src/main/java/de/robv/android/xposed/XposedBridge.java index a5d20e61..a5a2bf9a 100644 --- a/app/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/app/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -486,6 +486,9 @@ public static Object invokeOriginalMethod(Member method, Object thisObject, Obje private static native Object cloneToSubclassNative(Object obj, Class targetClazz); + /*package*/ static native void closeFilesBeforeForkNative(); + /*package*/ static native void reopenFilesAfterForkNative(); + /** @hide */ public static final class CopyOnWriteSortedSet { private transient volatile Object[] elements = EMPTY_ARRAY; diff --git a/app/src/main/java/de/robv/android/xposed/XposedHelpers.java b/app/src/main/java/de/robv/android/xposed/XposedHelpers.java index cdb65391..b5edf90b 100644 --- a/app/src/main/java/de/robv/android/xposed/XposedHelpers.java +++ b/app/src/main/java/de/robv/android/xposed/XposedHelpers.java @@ -3,9 +3,12 @@ import android.content.res.AssetManager; import android.content.res.Resources; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.Closeable; +import java.io.File; import java.io.FileInputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -1598,6 +1601,24 @@ protected AtomicInteger initialValue() { } } + /*package*/ static boolean fileContains(File file, String str) throws IOException { + // There are certainly more efficient algorithms (e.g. Boyer-Moore used in grep), + // but the naive approach should be sufficient here. + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(file)); + String line; + while ((line = in.readLine()) != null) { + if (line.contains(str)) { + return true; + } + } + return false; + } finally { + closeSilently(in); + } + } + //################################################################################################# // TODO helpers for view traversing /*To make it easier, I will try and implement some more helpers: diff --git a/app/src/main/java/de/robv/android/xposed/XposedInit.java b/app/src/main/java/de/robv/android/xposed/XposedInit.java index 716aa01a..bc18298e 100644 --- a/app/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/app/src/main/java/de/robv/android/xposed/XposedInit.java @@ -11,6 +11,7 @@ import android.content.res.TypedArray; import android.content.res.XResources; import android.os.Build; +import android.os.Environment; import android.os.Process; import android.util.Log; @@ -36,7 +37,9 @@ import static de.robv.android.xposed.XposedBridge.hookAllConstructors; import static de.robv.android.xposed.XposedBridge.hookAllMethods; import static de.robv.android.xposed.XposedHelpers.closeSilently; +import static de.robv.android.xposed.XposedHelpers.fileContains; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; +import static de.robv.android.xposed.XposedHelpers.findClass; import static de.robv.android.xposed.XposedHelpers.getBooleanField; import static de.robv.android.xposed.XposedHelpers.getObjectField; import static de.robv.android.xposed.XposedHelpers.setObjectField; @@ -61,6 +64,24 @@ private XposedInit() {} * Hook some methods which we want to create an easier interface for developers. */ /*package*/ static void initForZygote() throws Throwable { + if (needsToCloseFilesForFork()) { + XC_MethodHook callback = new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + XposedBridge.closeFilesBeforeForkNative(); + } + + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + XposedBridge.reopenFilesAfterForkNative(); + } + }; + + Class zygote = findClass("com.android.internal.os.Zygote", null); + hookAllMethods(zygote, "nativeForkAndSpecialize", callback); + hookAllMethods(zygote, "nativeForkSystemServer", callback); + } + final HashSet loadedPackagesInProcess = new HashSet<>(1); // normal process initialization (for new Activity, Service, BroadcastReceiver etc.) @@ -332,6 +353,23 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { XResources.init(latestResKey); } + private static boolean needsToCloseFilesForFork() { + if (Build.VERSION.SDK_INT >= 24) { + return true; + } else if (Build.VERSION.SDK_INT < 21) { + return false; + } + + File lib = new File(Environment.getRootDirectory(), "lib/libandroid_runtime.so"); + try { + return fileContains(lib, "Unable to construct file descriptor table"); + } catch (IOException e) { + Log.e(TAG, "Could not check whether " + lib + " has security patch level 5"); + // In doubt, just do it. The worst case should be unnecessary work and log messages. + return true; + } + } + private static void hookXposedInstaller(ClassLoader classLoader) { try { findAndHookMethod(INSTALLER_PACKAGE_NAME + ".XposedApp", classLoader, "getActiveXposedVersion",