From 4e0f2f473f9cc94fe17410de14529433efbf3aec Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 24 Nov 2018 21:53:46 +0100 Subject: [PATCH 01/29] Doc adjustment: add notice about non-SDK restrictions --- librootjava/README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/librootjava/README.md b/librootjava/README.md index 94d4558..214647d 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -308,6 +308,43 @@ If for some reason Gradle does not pick up these rules automatically, copy/paste them from the ```proguard.txt``` file into your own ProGuard ruleset. +## Restrictions on non-SDK interfaces + +Android 9.0 (Pie, API 28) introduces restrictions on the use of non-SDK +interfaces, whether directly, via reflection, or via JNI. See [this page +on the Android site](https://developer.android.com/about/versions/pie/restrictions-non-sdk-interfaces) +for details. + +We do use non-SDK interfaces in the part of the code that runs as root, +and currently it seems this is exempt from the restrictions. My +preliminary interpretation of the relevant sources in AOSP is that +this only applies to code running in a process spawned from Zygote, +which our code running as root isn't. + +It appears to currently (November 2018) be implemented as runtime +checks only. It may create some problems if these checks in the future +are also done at the ahead-of-time (AOT) compilation stage at app +install. There is currently no indication if that is or isn't likely to +happen (but if it does I expect the runtime checks to remain as well). + +In my experiments so far, if API 28 is targeted and you use a non-SDK +call in a normal app, a warning is written to the logs. If you go one +step further and enable strict-mode, a full stack-trace is logged: + +``` +StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectNonSdkApiUsage() + .build()); +} +``` + +However, making a non-SDK call from the code running as root doesn't +seem to trigger anything, either with or without explicitly enabling +strict mode. So it appears for now we are safe. + +TODO: This needs further investigation and monitoring, especially when +an Android 10 preview comes out! + ## Gradle ``` From 607db34ff03ab5afef2303363321e23382480371 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 24 Nov 2018 22:03:33 +0100 Subject: [PATCH 02/29] Doc adjustments #5 --- .../src/main/java/eu/chainfire/librootjava/Reflection.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java index 466d351..55c4545 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java @@ -73,10 +73,8 @@ static Context getSystemContext() { @SuppressLint("PrivateApi") @SuppressWarnings({"JavaReflectionMemberAccess"}) private static Object getActivityManager() { - // We could possibly cast this to ActivityManager instead of Object, but we don't currently - // need that for our usage, and it would require retesting everything. Maybe ActivityManager - // is even wrong and it should be ActivityManagerService, for which we don't have the class - // definition anyway. TODO: investigate further. + // Return object is AIDL interface IActivityManager, not an ActivityManager or + // ActivityManagerService synchronized (lock) { if (oActivityManager != null) { From 2b5793d3dfe68686a04100a9878e610bb2468514 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Tue, 11 Dec 2018 20:53:02 +0100 Subject: [PATCH 03/29] Gradle modifications for example project to use library sources directly for development (commented out) --- librootjava_example/build.gradle | 9 +++++++-- librootjavadaemon_example/build.gradle | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index c98df69..e113d7b 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -29,9 +29,14 @@ dependencies { implementation 'eu.chainfire:libsuperuser:1.0.0.+' - /* Uncomment this line and commment the other one during development to use the local maven - repository version, installed by the installMavenLocal Gradle task */ + // --- librootjava dependency --- + + /* Use module sources directly */ + //implementation project(':librootjava') + + /* Use local Maven repository version, installed by installMavenLocal Gradle task */ //implementation('eu.chainfire:librootjava:1.0.0-SNAPSHOT') { changing = true } + /* Use bintray/jcenter version */ implementation 'eu.chainfire:librootjava:1.0.0' } diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 435d320..d8db7a9 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -29,11 +29,17 @@ dependencies { implementation 'eu.chainfire:libsuperuser:1.0.0.+' - /* Uncomment these lines and commment the other ones during development to use the local maven - repository version, installed by the installMavenLocal Gradle task */ + // --- librootjava and librootjavadaemon dependencies --- + + /* Use module sources directly */ + //implementation project(':librootjava') + //implementation project(':librootjavadaemon') + + /* Use local Maven repository version, installed by installMavenLocal Gradle task */ //implementation('eu.chainfire:librootjava:1.0.0-SNAPSHOT') { changing = true } //implementation('eu.chainfire:librootjavadaemon:1.0.0-SNAPSHOT') { changing = true } + /* Use bintray/jcenter version */ implementation 'eu.chainfire:librootjava:1.0.0' implementation 'eu.chainfire:librootjavadaemon:1.0.0' } From d7a957d65d5a85c7a6cbbb75add414c47034053b Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Tue, 11 Dec 2018 22:13:41 +0100 Subject: [PATCH 04/29] Comment updates --- .../eu/chainfire/librootjava/Reflection.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java index 55c4545..b8f0bd8 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java @@ -23,7 +23,9 @@ class Reflection { private static Context systemContext = null; /** - * Stability: unlikely to change, this implementation works from 1.6 through 9.0 + * Retrieve system context
+ *
+ * Stability: unlikely to change, this implementation works from 1.6 through 9.0
* * @see RootJava#getSystemContext() * @@ -66,7 +68,9 @@ static Context getSystemContext() { private static Object oActivityManager = null; /** - * Retrieve ActivityManager instance without needing a context + * Retrieve ActivityManager instance without needing a context
+ *
+ * Stability: has changed before, might change again, rare * * @return ActivityManager */ @@ -108,7 +112,9 @@ private static Object getActivityManager() { private static Integer FLAG_RECEIVER_FROM_SHELL = null; /** - * Retrieve value of Intent.FLAG_RECEIVER_FROM_SHELL, if it exists + * Retrieve value of Intent.FLAG_RECEIVER_FROM_SHELL, if it exists
+ *
+ * Stability: stable, even if the flag goes away again this is unlikely to affect things * * @return FLAG_RECEIVER_FROM_SHELL or 0 */ @@ -167,8 +173,10 @@ private static Method getBroadcastIntent(Class cActivityManager) { } /** - * Stability: the implementation for this will definitely change over time - * + * Broadcast intent
+ *
+ * Stability: the implementation for this will definitely change over time
+ *
* This implementation does not require us to have a context * * @see RootJava#sendBroadcast(Intent) @@ -203,6 +211,11 @@ static void sendBroadcast(Intent intent) { throw new RuntimeException("librootjava: unable to send broadcast"); } + /** + * Internal class to retrieve an interface from a Binder (Proxy) + * + * @param Interface + */ @SuppressWarnings("unchecked") static class InterfaceRetriever { /** From 7ac33389cb741c9fb7fda5f36e3c85eeba55bb51 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 12 Dec 2018 00:13:32 +0100 Subject: [PATCH 05/29] Debugging support --- librootjava/README.md | 89 +++++++++-- librootjava/build.gradle | 4 +- .../eu/chainfire/librootjava/Debugger.java | 138 ++++++++++++++++++ .../eu/chainfire/librootjava/Reflection.java | 39 +++++ .../eu/chainfire/librootjava/RootJava.java | 14 +- librootjava_example/build.gradle | 4 +- librootjavadaemon/README.md | 2 +- librootjavadaemon/build.gradle | 15 +- librootjavadaemon_example/build.gradle | 8 +- 9 files changed, 285 insertions(+), 28 deletions(-) create mode 100644 librootjava/src/main/java/eu/chainfire/librootjava/Debugger.java diff --git a/librootjava/README.md b/librootjava/README.md index 214647d..949e43a 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -6,6 +6,7 @@ Run Java (and Kotlin) code as root! - Access to all the classes in your projects - Access to Android classes - Easy Binder-based IPC/RPC +- Debugging support ## License @@ -69,14 +70,6 @@ While this library was originally built to support Android 4.2+ devices, it only officially supports 5.0+. The first public GitHub release was tested specifically on 5.0, 7.0, 8.0, and 9.0. -## Debugging - -Debugging the code running as root is currently not supported. I made -some headway getting the jdwp server running, but I've not been able -to successfully connect jdb or AndroidStudio to it. If you want to take -a stab at it, there are some comments in -```RootJava.getLaunchString()``` related to it. - ## Recommended reading I strongly recommend you read the library's source code in its entirety @@ -270,6 +263,84 @@ non-root code. (See the [example project](../librootjava_example) for a more elaborate example for this entire process) +#### Debugging + +Debugging is supported since version 1.1.0, but disabled by default. + +To enable debugging, first we must tell the non-root process to launch +our root process with debug support enabled. We do this by calling +```Debugger.setEnabled()``` *before* calling +```RootJava.getLaunchScript()```: + +``` +public class MyActivity { + // ... + private void launchRootProcess() { + // ... + + Debugger.setEnabled(BuildConfig.DEBUG); + rootShell.addCommand(RootJava.getLaunchScript(this, RootMain.class, null, null, null, BuildConfig.APPLICATION_ID + ":root")); + } +} +``` + +We use ```BuildConfig.DEBUG``` instead of ```true``` to prevent +potential issues with release builds. + +In the code running as root, we then call ```Debugger.waitFor()``` +to pause execution (of the current thread) until a debugger is connected: + +``` +public class RootMain { + public static void main(String[] args) { + RootJava.restoreOriginalLdLibraryPath(); // call this first! + + if (BuildConfig.DEBUG) { + Debugger.waitFor(true); // wait for connection + } + + setYourBreakpointHere(); + } +} +``` + +We wrap the call inside a ```BuildConfig.DEBUG``` check, again to prevent +issues with release builds. + +Note that for long-running processes (such as daemons) you may not want +to explicitly wait for a debugger connection, in that case you can use +the ```Debugger.setName()``` method instead. That method may also be +called *before* ```Debugger.waitFor()``` to customize the debugger +display name, as by default the process name is used. + +Now that debugging is enabled, we still need to actually connect to +the process. You can do this in Android Studio via the *Attach +debugger to Android process* option in the *Run* menu. Once the root +process is running, it will be listed in the popup window. + +Note that you can debug *both* the non-root *and* root process at the +same time. While Android Studio can only debug a single application +*launched* in debug mode, you can *attach* to multiple processes. So +you can simply run your application in debug mode, have it launch the +root process, then *attach* to that root process, and debug both +simultaneously. + +Android Studio of course has no knowledge of the relation between the +multiple processes being debugged, so stepping into an IPC call in one +process will not automatically break on the implementation code in the +other process. You will have to set those breakpoints manually. + +#### BuildConfig + +The example snippets and projects make extensive use of the BuildConfig +class. This class is generated during the build process for every +module, library, etc. Double check you are importing the correct +BuildConfig class, the one from your application's package (unless you +know exactly what you're doing). Note that this complicates putting this +code inside libraries and modules, as you cannot reference the +application's BuildConfig class from a library. Work-arounds are beyond +the scope of this README. + #### Daemonizing For some use-cases you may want to run your root process as a daemon, @@ -348,7 +419,7 @@ an Android 10 preview comes out! ## Gradle ``` -implementation 'eu.chainfire:librootjava:1.0.0' +implementation 'eu.chainfire:librootjava:1.1.0' ``` ## Notes diff --git a/librootjava/build.gradle b/librootjava/build.gradle index d2d8a82..991c36e 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -51,7 +51,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.0.0' + libraryVersion = '1.1.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' @@ -72,7 +72,7 @@ task installMavenLocal(type: Upload) { packaging 'aar' groupId = publishedGroupId artifactId = artifact - version = '1.0.0-SNAPSHOT' + version = libraryVersion + '-SNAPSHOT' } } } diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Debugger.java b/librootjava/src/main/java/eu/chainfire/librootjava/Debugger.java new file mode 100644 index 0000000..27e2a31 --- /dev/null +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Debugger.java @@ -0,0 +1,138 @@ +package eu.chainfire.librootjava; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; + +/** + * Utility methods to support debugging + */ +@SuppressWarnings({"unused", "WeakerAccess"}) +public class Debugger { + /** + * Is debugging enabled ? + */ + static volatile boolean enabled = false; + + /** + * Is debugging enabled ?
+ *
+ * If called from non-root, this will return if we are launching new processes with debugging + * enabled. If called from root, this will return if the current process was launched + * with debugging enabled. + * + * @return Debugging enabled + */ + public static boolean isEnabled() { + if (android.os.Process.myUid() >= 10000) { + return enabled; + } else { + return Reflection.isDebuggingEnabled(); + } + } + + /** + * Launch root processes with debugging enabled ? + *
+ * To prevent issues on release builds, BuildConfig.DEBUG should be respected. So instead + * of passing true you would pass BuildConfig.DEBUG, while false + * remains false. + * + * @param enabled Enable debugging (default: false) + */ + public static void setEnabled(boolean enabled) { + Debugger.enabled = enabled; + } + + /** + * Cache for name to present to debugger. Really only used to determine if we have manually + * set a name already. + */ + private static volatile String name = null; + + /** + * Set name to present to debugger
+ *
+ * This method should only be called from the process running as root.
+ *
+ * Debugging will not work if this method has not been called, but the + * {@link #waitFor(boolean)} method will call it for you, if used.
+ *
+ * {@link RootJava#restoreOriginalLdLibraryPath()} should have been called before calling + * this method.
+ *
+ * To prevent issues with release builds, this call should be wrapped in a BuildConfig.DEBUG + * check. + * + * @param name Name to present to debugger, or null to use process name + * + * @see #waitFor(boolean) + */ + public static void setName(String name) { + if (Debugger.name == null) { + if (name == null) { + final File cmdline = new File("/proc/" + android.os.Process.myPid() + "/cmdline"); + try (BufferedReader reader = new BufferedReader(new FileReader(cmdline))) { + name = reader.readLine(); + if (name.indexOf(' ') > 0) name = name.substring(0, name.indexOf(' ')); + if (name.indexOf('\0') > 0) name = name.substring(0, name.indexOf('\0')); + } catch (IOException e) { + name = "librootjava:unknown"; + } + } + Debugger.name = name; + Reflection.setAppName(name); + } + } + + /** + * Wait for debugger to connect
+ *
+ * This method should only be called from the process running as root.
+ *
+ * If {@link #setName(String)} has not been called manually, the display name for the + * debugger will be set to the current process name.
+ *
+ * After this method has been called, you can connect AndroidStudio's debugger to the root + * process via Run->Attach Debugger to Android process.
+ *
+ * {@link RootJava#restoreOriginalLdLibraryPath()} should have been called before calling + * this method.
+ *
+ * Android's internal debugger code will print to STDOUT during this call using System.println, + * which may be annoying if your non-root process communicates with the root process through + * STDIN/STDOUT/STDERR. If the swallowOutput parameter is set to true, System.println + * will be temporarily redirected, and reset back to STDOUT afterwards.
+ *
+ * To prevent issues with release builds, this call should be wrapped in a BuildConfig.DEBUG + * check: + * + *
+     * {@code
+     * if (BuildConfig.DEBUG) {
+     *     Debugger.waitFor(true);
+     * }
+     * }
+     * 
+ * + * @param swallowOutput Temporarily redirect STDOUT ? + */ + public static void waitFor(boolean swallowOutput) { + if (Reflection.isDebuggingEnabled()) { + if (swallowOutput) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + System.setOut(new PrintStream(buffer)); + } + setName(null); + android.os.Debug.waitForDebugger(); + if (swallowOutput) { + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } + } + } +} diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java index b8f0bd8..e89f81b 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java @@ -211,6 +211,45 @@ static void sendBroadcast(Intent intent) { throw new RuntimeException("librootjava: unable to send broadcast"); } + /** + * Determine if debugging is enabled on the VM level
+ *
+ * Stability: unlikely to change, this implementation works from 1.6 through 9.0
+ * + * @see Debugger#isEnabled() + * + * @return Debugging enabled + */ + @SuppressLint("PrivateApi") + static boolean isDebuggingEnabled() { + try { + Class cVMDebug = Class.forName("dalvik.system.VMDebug"); + Method mIsDebuggingEnabled = cVMDebug.getDeclaredMethod("isDebuggingEnabled"); + return (Boolean)mIsDebuggingEnabled.invoke(null); + } catch (Exception e) { + Logger.ex(e); + return false; + } + } + + /** + * Set app name for debugger connection + *
+ * Stability: unlikely to change, this implementation works from 1.6 through 9.0
+ * + * @see Debugger#setName(String) + */ + @SuppressLint("PrivateApi") + static void setAppName(String name) { + try { + Class cDdmHandleAppName = Class.forName("android.ddm.DdmHandleAppName"); + Method m = cDdmHandleAppName.getDeclaredMethod("setAppName", String.class, int.class); + m.invoke(null, name, 0); + } catch (Exception e) { + Logger.ex(e); + } + } + /** * Internal class to retrieve an interface from a Binder (Proxy) * diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java b/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java index f50f58f..10c88e0 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java @@ -52,6 +52,7 @@ * @see #getSystemContext() * @see #getPackageContext(String) * @see #getLibraryPath(Context, String) + * @see Debugger#setEnabled(boolean) */ @SuppressWarnings({"unused", "WeakerAccess", "Convert2Diamond"}) public class RootJava { @@ -182,10 +183,15 @@ public static String getLaunchString(String packageCodePath, String clazz, Strin if (niceName != null) { extraParams += " --nice-name=" + niceName; } - //TODO debugging; the next line is successful in creating a jdwp server on Pie, and it's listed in 'adb jdwp', but connecting to it seemingly does nothing - //vmParams += " -XjdwpProvider:adbconnection -XjdwpOptions:transport=dt_android_adb,suspend=n,server=y"; - //vmParams += " -agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; - // android.os.Debug.waitForDebugger(); can be used in root's Main + if (Debugger.enabled) { // we don't use isEnabled() because that has a different meaning when called as root, and though rare we might call this method from root too + vmParams += " -Xcompiler-option --debuggable"; + if (Build.VERSION.SDK_INT >= 28) { + // Android 9.0 Pie changed things up a bit + vmParams += " -XjdwpProvider:internal -XjdwpOptions:transport=dt_android_adb,suspend=n,server=y"; + } else { + vmParams += " -agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; + } + } String ret = String.format("NO_ADDR_COMPAT_LAYOUT_FIXUP=1 %sCLASSPATH=%s %s%s /system/bin%s %s", prefix.toString(), packageCodePath, app_process, vmParams, extraParams, clazz); if (params != null) { StringBuilder full = new StringBuilder(ret); diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index e113d7b..b6ecd15 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -35,8 +35,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.0.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.0.0' + implementation 'eu.chainfire:librootjava:1.1.0' } diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index dedda54..28d23bc 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -140,7 +140,7 @@ on the Android site. ## Gradle ``` -implementation 'eu.chainfire:librootjavadaemon:1.0.0' +implementation 'eu.chainfire:librootjavadaemon:1.1.0' ``` You should update to the latest libRootJava and libRootJavaDaemon at the diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 1fed488..575680a 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -23,11 +23,14 @@ android { } dependencies { - /* Uncomment this line and commment the other one during development to use the local maven - repository version, installed by the installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.0.0-SNAPSHOT') { changing = true } + /* Use module sources directly */ + //implementation project(':librootjava') - implementation 'eu.chainfire:librootjava:1.0.0' + /* Use local Maven repository version, installed by installMavenLocal Gradle task */ + //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } + + /* Use bintray/jcenter version */ + implementation 'eu.chainfire:librootjava:1.1.0' } task sourcesJar(type: Jar) { @@ -61,7 +64,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.0.0' + libraryVersion = '1.1.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' @@ -82,7 +85,7 @@ task installMavenLocal(type: Upload) { packaging 'aar' groupId = publishedGroupId artifactId = artifact - version = '1.0.0-SNAPSHOT' + version = libraryVersion + '-SNAPSHOT' } } } diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index d8db7a9..90d01fc 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -36,10 +36,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.0.0-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.0.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.1.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.0.0' - implementation 'eu.chainfire:librootjavadaemon:1.0.0' + implementation 'eu.chainfire:librootjava:1.1.0' + implementation 'eu.chainfire:librootjavadaemon:1.1.0' } From 89360b46b0171d6fdae2f7a2cf61bd6782c5c41d Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 10:44:51 +0100 Subject: [PATCH 06/29] libdaemonize: retry launch several times On some devices in the early boot stages, execv will fail with EACCESS, cause unknown. Retrying a couple of times seems to work. Most-seen required attempts is three. That retrying works implies some sort of race-condition, possibly SELinux related. --- librootjavadaemon/CMakeLists.txt | 5 ++ librootjavadaemon/src/main/jni/daemonize.cpp | 62 +++++++++++++++++--- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/librootjavadaemon/CMakeLists.txt b/librootjavadaemon/CMakeLists.txt index 7b48e96..b872e6c 100644 --- a/librootjavadaemon/CMakeLists.txt +++ b/librootjavadaemon/CMakeLists.txt @@ -1,3 +1,8 @@ cmake_minimum_required(VERSION 3.4.1) + add_executable( libdaemonize.so src/main/jni/daemonize.cpp ) + +target_link_libraries( libdaemonize.so log ) + +#target_compile_definitions( libdaemonize.so PRIVATE DEBUG ) diff --git a/librootjavadaemon/src/main/jni/daemonize.cpp b/librootjavadaemon/src/main/jni/daemonize.cpp index cf56118..eca7655 100644 --- a/librootjavadaemon/src/main/jni/daemonize.cpp +++ b/librootjavadaemon/src/main/jni/daemonize.cpp @@ -21,12 +21,33 @@ #include #include #include +#include +#include + +#ifdef DEBUG +#include +#define LOG(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libdaemonize", __VA_ARGS__)) +#else +#define LOG(...) ((void)0) +#endif + +int sleep_ms(int ms) { + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + if ((nanosleep(&ts,&ts) == -1) && (errno == EINTR)) { + int ret = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); + if (ret < 1) ret = 1; + return ret; + } + return 0; +} /* Proper daemonization includes forking, closing the current STDIN/STDOUT/STDERR, creating a new * session, and forking again, making sure the twice-forked process becomes a child of init (1) */ static int fork_daemon(int returnParent) { pid_t child = fork(); - if (child == 0) { + if (child == 0) { // 1st child close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); @@ -39,12 +60,28 @@ static int fork_daemon(int returnParent) { setsid(); pid_t child2 = fork(); - if (child2 <= 0) return 0; // success child or error on 2nd fork - exit(EXIT_SUCCESS); + if (child2 == 0) { // 2nd child + return 0; // return execution to caller + } else if (child2 > 0) { // 1st child, fork ok + exit(EXIT_SUCCESS); + } else if (child2 < 0) { // 1st child, fork fail + LOG("2nd fork failed (%d)", errno); + exit(EXIT_FAILURE); + } + } + + // parent + if (child < 0) { + LOG("1st fork failed (%d)", errno); + return -1; // error on 1st fork + } + while (true) { + int status; + pid_t waited = waitpid(child, &status, 0); + if ((waited == child) && WIFEXITED(status)) { + break; + } } - if (child < 0) return -1; // error on 1st fork - int status; - waitpid(child, &status, 0); if (!returnParent) exit(EXIT_SUCCESS); return 1; // success parent } @@ -52,8 +89,17 @@ static int fork_daemon(int returnParent) { extern "C" { int main(int argc, char *argv[], char** envp) { - if (fork_daemon(0) == 0) { - execv(argv[1], &argv[1]); + if (fork_daemon(0) == 0) { // daemonized child + // On some devices in the early boot stages, execv will fail with EACCESS, cause unknown. + // Retrying a couple of times seems to work. Most-seen required attempts is three. + // That retrying works implies some sort of race-condition, possibly SELinux related. + for (int i = 0; i < 16; i++) { + execv(argv[1], &argv[1]); // never returns if successful + LOG("[%d] execv(%s, ...)-->%d", i, argv[1], errno); + sleep_ms(16); + } + LOG("too many failures, aborting"); + exit(EXIT_FAILURE); } } From 88ada1f80e210ccef926346f7c58b6c80c39c2db Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 10:48:10 +0100 Subject: [PATCH 07/29] Adjust chmod and add restorecon to app_process/libdaemonize This may prevent some EACCESS issues and allow the non-root app to clear left-over files. --- .../src/main/java/eu/chainfire/librootjava/AppProcess.java | 5 ++++- .../main/java/eu/chainfire/librootjavadaemon/RootDaemon.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java index 6062b8f..354c1da 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java @@ -321,6 +321,8 @@ public static String getAppProcessRelocate(Context context, String appProcessBas } } + boolean onData = path.startsWith("/data/"); + String appProcessCopy; if (guessIfAppProcessIs64Bits(appProcessBase)) { appProcessCopy = path + "/.app_process64_" + uuid; @@ -328,7 +330,8 @@ public static String getAppProcessRelocate(Context context, String appProcessBas appProcessCopy = path + "/.app_process32_" + uuid; } preLaunch.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", box, appProcessBase, appProcessCopy)); - preLaunch.add(String.format(Locale.ENGLISH, "%s chmod 0700 %s >/dev/null 2>/dev/null", box, appProcessCopy)); + preLaunch.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", box, onData ? "0766" : "0700", appProcessCopy)); + if (onData) preLaunch.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", appProcessCopy)); postExecution.add(String.format(Locale.ENGLISH, "%s rm %s >/dev/null 2>/dev/null", box, appProcessCopy)); return appProcessCopy; } diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index b804faf..f8b8a46 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -76,8 +76,11 @@ public static List patchLaunchScript(Context context, List scrip // copy our executable String libSrc = RootJava.getLibraryPath(context, "daemonize"); String libDest = app_process_path + "/.daemonize_" + AppProcess.uuid; + boolean onData = libDest.startsWith("/data/"); + ret.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", AppProcess.box, libSrc, libDest)); - ret.add(String.format(Locale.ENGLISH, "%s chmod 0700 %s >/dev/null 2>/dev/null", AppProcess.box, libDest)); + ret.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", AppProcess.box, onData ? "0766" : "0700", libDest)); + if (onData) ret.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", libDest)); // inject executable into command int idx = line.indexOf(app_process); From c6ed65129aeade8c5835f77a61cb6fc296b6920a Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 11:37:55 +0100 Subject: [PATCH 08/29] Improve IPC broadcast performance The broadcast was initially sent in serialized (ordered) mode, which could cause delays when Android is very busy (such as at early boot). The broadcast was changed to use normal broadcast mode. Additionally, FLAG_RECEIVER_FOREGROUND was added to the intent flags, to make sure the receiver gets a foreground priority boost. --- .../src/main/java/eu/chainfire/librootjava/Reflection.java | 4 ++-- .../src/main/java/eu/chainfire/librootjava/RootIPC.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java index e89f81b..077d35b 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java @@ -194,12 +194,12 @@ static void sendBroadcast(Intent intent) { Method mBroadcastIntent = getBroadcastIntent(oActivityManager.getClass()); if (mBroadcastIntent.getParameterTypes().length == 13) { // API 24+ - mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, null, true, false, 0); + mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, null, false, false, 0); return; } if (mBroadcastIntent.getParameterTypes().length == 12) { // API 21+ - mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, true, false, 0); + mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, false, false, 0); return; } } catch (Exception e) { diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/RootIPC.java b/librootjava/src/main/java/eu/chainfire/librootjava/RootIPC.java index 3bbe753..3d6fab6 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/RootIPC.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/RootIPC.java @@ -149,6 +149,7 @@ public void broadcastIPC() { Intent intent = new Intent(); intent.setPackage(packageName); intent.setAction(RootIPCReceiver.BROADCAST_ACTION); + intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); Bundle bundle = new Bundle(); bundle.putBinder(RootIPCReceiver.BROADCAST_BINDER, binder); From e4d897418752d7f819682ee861317ad40d77bef7 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 12:00:09 +0100 Subject: [PATCH 09/29] Daemonize: additional constructor options: surviveFrameworkRestart, exitListener surviveFrameworkRestart (recommended: false) controls whether the daemon automatically terminates if the Android framework dies. This is commonly done during testing and by some root apps by calling stop and start in a root shell. Setting this to true doesn't guarantee your daemon will continue to function properly through a framework restart, that depends on the rest of your code - it just won't automatically terminate. exitListener is a callback for when the daemon is terminated through the RootDaemon.exit() call. It IS NOT fired when the daemon is force killed, when you call System.exit(), or an unhandled exception occurs. It IS fired when you manually call RootDaemon.exit(), when the daemon is being replaced by a newer version, and when the daemon terminates due to framework death. --- librootjavadaemon/README.md | 9 +-- .../librootjavadaemon/RootDaemon.java | 70 +++++++++++++++---- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index 28d23bc..d182010 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -61,7 +61,7 @@ statements (generally after setting up logging): // If a daemon of the same version is already running, this // call will trigger process termination, and will thus // never return. - RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0); + RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0, false, null); // ... @@ -113,14 +113,15 @@ them with your own handling. #### Termination This daemon process will only terminate when explicitly told to do so, -either through IPC or a Linux kill signal (or if an unhandled -exception occurs). This is why in the example above we add a +either through IPC, a Linux kill signal, if an unhandled +exception occurs, or (if so configured) when the Android framework +dies. This is why in the example above we add a ```terminate()``` method to our IPC interface which calls ```RootDaemon.exit()```. This way you can tell the daemon to stop running from your non-root app through IPC. Note that this method will always trigger a ```RemoteException``` on the -non-root end when called. +non-root end when called through IPC. See the [example project](../librootjavadaemon_example) for further details. diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index f8b8a46..8e9dfd4 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -36,7 +36,7 @@ * Class with utility function sto launch Java code running as a root as a daemon * * @see #getLaunchScript(Context, Class, String, String, String[], String) - * @see #daemonize(String, int) + * @see #daemonize(String, int, boolean, OnExitListener) * @see #run() * @see #exit() */ @@ -122,6 +122,14 @@ public static List getLaunchScript(Context context, Class clazz, Stri /** Registered interfaces */ private static final List ipcs = new ArrayList(); + /** Called before termination */ + public interface OnExitListener { + void onExit(); + } + + /** Stored by daemonize(), called by exit() */ + private static volatile OnExitListener onExitListener = null; + /** * Makes sure there is only a single daemon running with this code parameter. This should * be one of the first calls in your process to be run as daemon, just after setting up logging @@ -145,9 +153,11 @@ public static List getLaunchScript(Context context, Class clazz, Stri * * @param packageName Package name of the app. BuildConfig.APPLICATION_ID can generally be used. * @param code User-value, should be unique per daemon process + * @param surviveFrameworkRestart If false (recommended), automatically terminate if the Android framework restarts + * @param exitListener Callback called before the daemon exists either due to a newer daemon version being started or {@link #exit()} being called, or null */ @SuppressLint("PrivateApi") - public static void daemonize(String packageName, int code) { + public static void daemonize(String packageName, int code, boolean surviveFrameworkRestart, OnExitListener exitListener) { String id = packageName + "#" + String.valueOf(code) + "#daemonize"; File apk = new File(System.getenv("CLASSPATH")); @@ -178,7 +188,34 @@ public static void daemonize(String packageName, int code) { } // If we reach this, there either was no previous daemon, or it was outdated - Logger.ep(LOG_PREFIX, "Installing service"); + Logger.dp(LOG_PREFIX, "Installing service"); + onExitListener = exitListener; + + if (!surviveFrameworkRestart) { + /* We link to Android's activity service, which lives in system_server. If the + framework is restarted, for example through stop/start in a root shell, this + service will die and we will be notified. + + Obviously when setting surviveFrameworkRestart to true, things you do in + your own code may still cause this process to terminate when the framework + dies, we're just not doing it automatically. */ + + IBinder activityService = (IBinder)mGetService.invoke(null, Context.ACTIVITY_SERVICE); + if (activityService != null) { + try { + activityService.linkToDeath(new IBinder.DeathRecipient() { + @Override + public void binderDied() { + exit(); + } + }, 0); + } catch (RemoteException e) { + // already dead + exit(); + } + } + } + mAddService.invoke(null, id, new IRootDaemonIPC.Stub() { @Override public String getVersion() { @@ -211,7 +248,7 @@ public void broadcast() { *
* Use this method instead of librootjava's 'new RootIPC()' constructor when running as daemon. * - * @param packageName Package name of the app. Use the same value as used when calling {@link #daemonize(String, int)}. + * @param packageName Package name of the app. Use the same value as used when calling {@link #daemonize(String, int, boolean, OnExitListener)}. * @param ipc Binder object to wrap and send out * @param code User-value, should be unique per Binder * @return RootIPC instance @@ -239,7 +276,8 @@ public static RootIPC register(String packageName, IBinder ipc, int code) { * the main() implementation. The initial Binder broadcasts and the connections themselves * are handled in background threads created by the RootIPC instances created when * {@link #register(String, IBinder, int)} is called, and re-broadcasting those interfaces - * is done by the internal Binder interface registered by {@link #daemonize(String, int)}.
+ * is done by the internal Binder interface registered by + * {@link #daemonize(String, int, boolean, OnExitListener)}.
*
* This method never returns! */ @@ -263,15 +301,23 @@ public static void run() { * RemoteException on the other end. */ public static void exit() { + /* We do not return from the run() call but immediately exit, so if this method is called + from inside a Binder interface method implementation, the process has died before the + IPC call to terminate completes on the other end. This triggers a RemoteException so the + other end can easily verify this process has terminated. It also prevents a + race-condition between the old service dying and new service registering. Additionally it + saves us from having to use another synchronizer to cope with a termination request + coming in from another daemon launch before run() is actually called. */ + Logger.dp(LOG_PREFIX, "Exiting"); - /* We do not return from the run() call but immediately exit, so the process has died - before the IPC call to terminate completes on the other end. This triggers a - RemoteException so the other end can easily verify this process has terminated. It - also prevents a race-condition between the old service dying and new service registering. - Additionally it saves us from having to use another synchronizer to cope with a - termination request coming in from another daemon launch before run() is actually - called. */ + try { + if (onExitListener != null) { + onExitListener.onExit(); + } + } catch (Exception e) { + Logger.ex(e); + } try { /* Unlike when using RootJava.getLaunchScript(), RootDaemon.getLaunchScript() does From 793878aec9ffaa65d2d70ee9d599941ffcf15dc6 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 14:08:53 +0100 Subject: [PATCH 10/29] Uppercase some constants --- .../eu/chainfire/librootjava/AppProcess.java | 17 ++++++++--------- .../chainfire/librootjavadaemon/RootDaemon.java | 6 +++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java index 354c1da..7649f54 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.util.List; import java.util.Locale; -import java.util.UUID; /** * Utility methods to determine the location and bits of the app_process executable to be used.
@@ -43,12 +42,12 @@ public class AppProcess { /** * Toolbox or toybox? */ - public static final String box = Build.VERSION.SDK_INT < 23 ? "toolbox" : "toybox"; + public static final String BOX = Build.VERSION.SDK_INT < 23 ? "toolbox" : "toybox"; /** * Used to create unique filenames in common locations */ - public static final String uuid = getUUID(); + public static final String UUID = getUUID(); /** * @return uuid that doesn't contain 32 or 64, as to not confuse bit-choosing code @@ -56,7 +55,7 @@ public class AppProcess { private static String getUUID() { String uuid = null; while ((uuid == null) || uuid.contains("32") || uuid.contains("64")) { - uuid = UUID.randomUUID().toString(); + uuid = java.util.UUID.randomUUID().toString(); } return uuid; } @@ -325,14 +324,14 @@ public static String getAppProcessRelocate(Context context, String appProcessBas String appProcessCopy; if (guessIfAppProcessIs64Bits(appProcessBase)) { - appProcessCopy = path + "/.app_process64_" + uuid; + appProcessCopy = path + "/.app_process64_" + UUID; } else { - appProcessCopy = path + "/.app_process32_" + uuid; + appProcessCopy = path + "/.app_process32_" + UUID; } - preLaunch.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", box, appProcessBase, appProcessCopy)); - preLaunch.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", box, onData ? "0766" : "0700", appProcessCopy)); + preLaunch.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", BOX, appProcessBase, appProcessCopy)); + preLaunch.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", BOX, onData ? "0766" : "0700", appProcessCopy)); if (onData) preLaunch.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", appProcessCopy)); - postExecution.add(String.format(Locale.ENGLISH, "%s rm %s >/dev/null 2>/dev/null", box, appProcessCopy)); + postExecution.add(String.format(Locale.ENGLISH, "%s rm %s >/dev/null 2>/dev/null", BOX, appProcessCopy)); return appProcessCopy; } } diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index 8e9dfd4..7712f75 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -75,11 +75,11 @@ public static List patchLaunchScript(Context context, List scrip // copy our executable String libSrc = RootJava.getLibraryPath(context, "daemonize"); - String libDest = app_process_path + "/.daemonize_" + AppProcess.uuid; + String libDest = app_process_path + "/.daemonize_" + AppProcess.UUID; boolean onData = libDest.startsWith("/data/"); - ret.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", AppProcess.box, libSrc, libDest)); - ret.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", AppProcess.box, onData ? "0766" : "0700", libDest)); + ret.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", AppProcess.BOX, libSrc, libDest)); + ret.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", AppProcess.BOX, onData ? "0766" : "0700", libDest)); if (onData) ret.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", libDest)); // inject executable into command From 2f0431964f0007890d6b17c9ff403ee723868864 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 15:19:23 +0100 Subject: [PATCH 11/29] Cache cleanup methods --- librootjava/README.md | 12 ++++ .../eu/chainfire/librootjava/RootJava.java | 65 +++++++++++++++++++ .../librootjava_example/MainActivity.java | 10 +++ librootjavadaemon/README.md | 10 +++ .../librootjavadaemon/RootDaemon.java | 29 ++++++++- .../MainActivity.java | 7 ++ 6 files changed, 132 insertions(+), 1 deletion(-) diff --git a/librootjava/README.md b/librootjava/README.md index 949e43a..411d27e 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -263,6 +263,18 @@ non-root code. (See the [example project](../librootjava_example) for a more elaborate example for this entire process) +#### Cleanup + +To execute our code as root, files may need to be created in our app's +cache directory. While the library does its best to clean up after +itself, there are situations (such as a reboot mid-process) where some +files may not automatically be removed. + +It is recommended to periodically call ```RootJava.cleanupCache()``` +to remove these leftover files. This method will cleanup all of our +files in the cache directory that predate the current boot. As I/O is +performed, this method should not be called from the main UI thread. + #### Debugging Debugging is supported since version 1.1.0, but disabled by default. diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java b/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java index 10c88e0..826d7c8 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/RootJava.java @@ -21,8 +21,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.SystemClock; import java.io.File; +import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -247,6 +249,69 @@ public static List getLaunchScript(Context context, Class clazz, Stri return script; } + /** Prefixes of filename to remove from the app's cache directory */ + public static final String[] CLEANUP_CACHE_PREFIXES = new String[] { ".app_process32_", ".app_process64_" }; + + /** + * Clean up leftover files from our cache directory.
+ *
+ * In ideal circumstances no files should be left dangling, but in practise it happens sooner + * or later anyway. Periodically (once per app launch or per boot) calling this method is + * advised.
+ *
+ * This method should be called from a background thread, as it performs disk i/o.
+ *
+ * It is difficult to determine which of these files may actually be in use, especially in + * daemon mode. We try to determine device boot time, and wipe everything from before that + * time. For safety we explicitly keep files using our current UUID. + * + * @param context Context to retrieve cache directory from + */ + public static void cleanupCache(Context context) { + cleanupCache(context, CLEANUP_CACHE_PREFIXES); + } + + /** + * Clean up leftover files from our cache directory.
+ *
+ * This version is for internal use, see {@link #cleanupCache(Context)} instead. + * + * @param context Context to retrieve cache directory from + * @param prefixes List of prefixes to scrub + */ + public static void cleanupCache(Context context, final String[] prefixes) { + try { + File cacheDir = context.getCacheDir(); + if (cacheDir.exists()) { + // determine time of last boot + long boot = System.currentTimeMillis() - SystemClock.elapsedRealtime(); + + // find our files + for (File file : cacheDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + boolean accept = false; + for (String prefix : prefixes) { + // just in case: don't return files that contain our current uuid + if (name.startsWith(prefix) && !name.endsWith(AppProcess.UUID)) { + accept = true; + break; + } + } + return accept; + } + })) { + if (file.lastModified() < boot) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + } + } + } catch (Exception e) { + Logger.ex(e); + } + } + // ------------------------ calls for root ------------------------ /** diff --git a/librootjava_example/src/main/java/eu/chainfire/librootjava_example/MainActivity.java b/librootjava_example/src/main/java/eu/chainfire/librootjava_example/MainActivity.java index 0cf003c..9a3a59d 100644 --- a/librootjava_example/src/main/java/eu/chainfire/librootjava_example/MainActivity.java +++ b/librootjava_example/src/main/java/eu/chainfire/librootjava_example/MainActivity.java @@ -34,6 +34,7 @@ import eu.chainfire.librootjava.Logger; import eu.chainfire.librootjava.RootIPCReceiver; +import eu.chainfire.librootjava.RootJava; import eu.chainfire.librootjava_example.root.IIPC; import eu.chainfire.librootjava_example.root.IPingCallback; import eu.chainfire.librootjava_example.root.PassedData; @@ -80,6 +81,15 @@ it inside a method (in this example), the context passed to its constructor is a wrapper. We need to update it to a proper context, so we may actually receive the Binder object from our root code. */ ipcReceiver.setContext(this); + + /* Cleanup leftover files from our cache directory. This is not exactly an elegant way to + do it, but it illustrates that this should be done off of the main UI thread. */ + (new Thread(new Runnable() { + @Override + public void run() { + RootJava.cleanupCache(MainActivity.this); + } + })).start(); } @Override diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index d182010..e4d6457 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -126,6 +126,16 @@ non-root end when called through IPC. See the [example project](../librootjavadaemon_example) for further details. +#### Cleanup + +As with running code as root in normal (non-daemon) mode, files may need +to be created in our app's cache directory. The chances of leftover +files are actually higher in daemon mode, and the number of files is +higher too. + +To clean up, call ```RootDaemon.cleanupCache()``` instead of +```RootJava.cleanupCache()```. It is *not* needed to call both. + ## abiFilters This library includes native code for all platforms the NDK supports. diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index 7712f75..fb63aad 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -117,6 +117,23 @@ public static List getLaunchScript(Context context, Class clazz, Stri return patchLaunchScript(context, RootJava.getLaunchScript(context, clazz, app_process, relocate_path, params, niceName)); } + /** Prefixes of filename to remove from the app's cache directory */ + public static final String[] CLEANUP_CACHE_PREFIXES = new String[] { ".daemonize_" }; + + /** + * Clean up leftover files from our cache directory.
+ *
+ * Call this method instead of (not in addition to) RootJava#cleanupCache(Context). + * + * @param context Context to retrieve cache directory from + */ + public static void cleanupCache(Context context) { + String[] prefixes = new String[RootJava.CLEANUP_CACHE_PREFIXES.length + CLEANUP_CACHE_PREFIXES.length]; + System.arraycopy(RootJava.CLEANUP_CACHE_PREFIXES, 0, prefixes, 0, RootJava.CLEANUP_CACHE_PREFIXES.length); + System.arraycopy(CLEANUP_CACHE_PREFIXES, 0, prefixes, RootJava.CLEANUP_CACHE_PREFIXES.length, CLEANUP_CACHE_PREFIXES.length); + RootJava.cleanupCache(context, prefixes); + } + // ------------------------ calls for root ------------------------ /** Registered interfaces */ @@ -183,7 +200,7 @@ public static void daemonize(String packageName, int code, boolean surviveFramew } else { Logger.dp(LOG_PREFIX, "Service already running, requesting re-broadcast and aborting"); ipc.broadcast(); - System.exit(0); + exit(); } } @@ -327,6 +344,16 @@ not clean up our relocated app_process binary after executing (which might preve if (app_process.exists() && !app_process.getAbsolutePath().startsWith("/system/bin/")) { //noinspection ResultOfMethodCallIgnored app_process.delete(); + + // See if we can also find a copy of the daemonize binary + String daemonize_path = app_process.getAbsolutePath(); + daemonize_path = daemonize_path.replace(".app_process32_", ".daemonize_"); + daemonize_path = daemonize_path.replace(".app_process64_", ".daemonize_"); + File daemonize = new File(daemonize_path); + if (daemonize.exists()) { + //noinspection ResultOfMethodCallIgnored + daemonize.delete(); + } } } catch (IOException e) { // should never actually happen diff --git a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java index 0085446..3c80d28 100644 --- a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java +++ b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java @@ -28,6 +28,7 @@ import eu.chainfire.librootjava.Logger; import eu.chainfire.librootjava.RootIPCReceiver; +import eu.chainfire.librootjavadaemon.RootDaemon; import eu.chainfire.librootjavadaemon_example.root.IIPC; import eu.chainfire.librootjavadaemon_example.root.RootMain; import eu.chainfire.libsuperuser.Debug; @@ -70,6 +71,12 @@ protected void onCreate(Bundle savedInstanceState) { textView.setHorizontallyScrolling(true); textView.setMovementMethod(new ScrollingMovementMethod()); ipcReceiver.setContext(this); + (new Thread(new Runnable() { + @Override + public void run() { + RootDaemon.cleanupCache(MainActivity.this); + } + })).start(); } @Override From 62879e65d66406b7ee5258ea2ad9a6b1b11c2cd9 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 15:20:26 +0100 Subject: [PATCH 12/29] Commentary --- .../eu/chainfire/librootjavadaemon_example/MainActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java index 3c80d28..85fa712 100644 --- a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java +++ b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java @@ -70,6 +70,8 @@ protected void onCreate(Bundle savedInstanceState) { textView = findViewById(R.id.textView); textView.setHorizontallyScrolling(true); textView.setMovementMethod(new ScrollingMovementMethod()); + + // See librootjava's example for further commentary on these two calls ipcReceiver.setContext(this); (new Thread(new Runnable() { @Override From 8c65e537422fa5dec1d5c2a50b465dc3dac8a81d Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 15:33:22 +0100 Subject: [PATCH 13/29] daemonize example: update daemonize call with new signature --- .../eu/chainfire/librootjavadaemon_example/root/RootMain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/root/RootMain.java b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/root/RootMain.java index e036fb9..d2db3fa 100644 --- a/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/root/RootMain.java +++ b/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/root/RootMain.java @@ -91,7 +91,7 @@ public void uncaughtException(Thread thread, Throwable throwable) { */ private void run(String[] args) { // Become the daemon - RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0); + RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0, false, null); // Restore original LD_LIBRARY_PATH RootJava.restoreOriginalLdLibraryPath(); From 7863f458b1701989dd6c0a125b2cd8174a13c900 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 19 Dec 2018 15:34:17 +0100 Subject: [PATCH 14/29] v1.2.0 --- librootjava/README.md | 2 +- librootjava/build.gradle | 2 +- librootjava_example/build.gradle | 4 ++-- librootjavadaemon/README.md | 2 +- librootjavadaemon/build.gradle | 6 +++--- librootjavadaemon_example/build.gradle | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/librootjava/README.md b/librootjava/README.md index 411d27e..dc817d3 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -431,7 +431,7 @@ an Android 10 preview comes out! ## Gradle ``` -implementation 'eu.chainfire:librootjava:1.1.0' +implementation 'eu.chainfire:librootjava:1.2.0' ``` ## Notes diff --git a/librootjava/build.gradle b/librootjava/build.gradle index 991c36e..ebd8b67 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -51,7 +51,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.1.0' + libraryVersion = '1.2.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index b6ecd15..d3e3078 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -35,8 +35,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.1.0' + implementation 'eu.chainfire:librootjava:1.2.0' } diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index e4d6457..a9f14f4 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -151,7 +151,7 @@ on the Android site. ## Gradle ``` -implementation 'eu.chainfire:librootjavadaemon:1.1.0' +implementation 'eu.chainfire:librootjavadaemon:1.2.0' ``` You should update to the latest libRootJava and libRootJavaDaemon at the diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 575680a..3bdb6ae 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -27,10 +27,10 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.1.0' + implementation 'eu.chainfire:librootjava:1.2.0' } task sourcesJar(type: Jar) { @@ -64,7 +64,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.1.0' + libraryVersion = '1.2.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 90d01fc..0f22add 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -36,10 +36,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.1.0-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.1.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.2.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.1.0' - implementation 'eu.chainfire:librootjavadaemon:1.1.0' + implementation 'eu.chainfire:librootjava:1.2.0' + implementation 'eu.chainfire:librootjavadaemon:1.2.0' } From e7ea8c2fe242deeec867240295ea077537c7b102 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Thu, 21 Mar 2019 17:51:04 +0100 Subject: [PATCH 15/29] Q Preview 1 compatiblity On several older Android versions we needed to relocate app_process to prevent it from being executed in a restricted SELinux context, but this causes the "Error finding namespace of apex: no namespace called runtime" linker error on Q Preview 1. Running app_process straight from /system/bin (and daemonizer from /data/.../lib) works fine however on QP1, so we just skip relocating altogher for now. Closes #4 --- .../eu/chainfire/librootjava/AppProcess.java | 28 +++++++++++++++++++ .../librootjavadaemon/RootDaemon.java | 25 +++++++++++------ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java index 7649f54..4698b13 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/AppProcess.java @@ -283,6 +283,29 @@ public static boolean guessIfAppProcessIs64Bits(String app_process) { return !isRunningAs32BitOn64BitArch(); } + /** + * Should app_process be relocated ?
+ *
+ * On older Android versions we must relocate the app_process binary to prevent it from + * running in a restricted SELinux context. On Q this presents us with the linker error: + * "Error finding namespace of apex: no namespace called runtime". However, at least + * on the first preview release of Q, running straight from /system/bin works and does + * not give us a restricted SELinux context, so we skip relocation. + * + * TODO: Revisit on new Q preview and production releases. Maybe spend some time figuring out what causes the namespace error and if we can fix it. + * + * @see #getAppProcessRelocate(Context, String, List, List, String) + * + * @return should app_process be relocated ? + */ + @TargetApi(Build.VERSION_CODES.M) + public static boolean shouldAppProcessBeRelocated() { + return !( + (Build.VERSION.SDK_INT >= 29) || + ((Build.VERSION.SDK_INT == 28) && (Build.VERSION.PREVIEW_SDK_INT != 0)) + ); + } + /** * Create script to relocate specified app_process binary to a different location.
*
@@ -290,6 +313,7 @@ public static boolean guessIfAppProcessIs64Bits(String app_process) { * SELinux context that we do not want. Relocating it bypasses that.
* * @see #getAppProcess() + * @see #shouldAppProcessBeRelocated() * * @param context Application or activity context * @param appProcessBase Path to original app_process or null for default @@ -301,6 +325,10 @@ public static boolean guessIfAppProcessIs64Bits(String app_process) { public static String getAppProcessRelocate(Context context, String appProcessBase, List preLaunch, List postExecution, String path) { if (appProcessBase == null) appProcessBase = getAppProcess(); if (path == null) { + if (!shouldAppProcessBeRelocated()) { + return appProcessBase; + } + path = "/dev"; if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) { File cacheDir = context.getCacheDir(); diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index fb63aad..ac625bb 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -73,18 +73,27 @@ public static List patchLaunchScript(Context context, List scrip // patch the main script line String app_process_path = app_process.substring(0, app_process.lastIndexOf('/')); - // copy our executable - String libSrc = RootJava.getLibraryPath(context, "daemonize"); - String libDest = app_process_path + "/.daemonize_" + AppProcess.UUID; - boolean onData = libDest.startsWith("/data/"); + // our executable + String libSource = RootJava.getLibraryPath(context, "daemonize"); + String libExec; + + if (app_process_path.startsWith("/system/bin")) { + // app_process was not relocated, assume caller knows what he's doing, and + // run our executable from its library location + libExec = libSource; + } else { + // copy our executable + libExec = app_process_path + "/.daemonize_" + AppProcess.UUID; + boolean onData = libExec.startsWith("/data/"); - ret.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", AppProcess.BOX, libSrc, libDest)); - ret.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", AppProcess.BOX, onData ? "0766" : "0700", libDest)); - if (onData) ret.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", libDest)); + ret.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", AppProcess.BOX, libSource, libExec)); + ret.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", AppProcess.BOX, onData ? "0766" : "0700", libExec)); + if (onData) ret.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", libExec)); + } // inject executable into command int idx = line.indexOf(app_process); - ret.add(line.substring(0, idx) + libDest + " " + line.substring(idx)); + ret.add(line.substring(0, idx) + libExec + " " + line.substring(idx)); in_post = true; } else if (in_post && line.contains("box rm")) { From 7c2da99845a0e782371385fc1072218be16ed167 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Thu, 21 Mar 2019 18:24:52 +0100 Subject: [PATCH 16/29] v1.3.0 --- librootjava/README.md | 2 +- librootjava/build.gradle | 2 +- librootjava_example/build.gradle | 4 ++-- librootjavadaemon/README.md | 2 +- librootjavadaemon/build.gradle | 6 +++--- librootjavadaemon_example/build.gradle | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/librootjava/README.md b/librootjava/README.md index dc817d3..7bca846 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -431,7 +431,7 @@ an Android 10 preview comes out! ## Gradle ``` -implementation 'eu.chainfire:librootjava:1.2.0' +implementation 'eu.chainfire:librootjava:1.3.0' ``` ## Notes diff --git a/librootjava/build.gradle b/librootjava/build.gradle index ebd8b67..250c360 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -51,7 +51,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.2.0' + libraryVersion = '1.3.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index d3e3078..6a58f6d 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -35,8 +35,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.2.0' + implementation 'eu.chainfire:librootjava:1.3.0' } diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index a9f14f4..175f953 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -151,7 +151,7 @@ on the Android site. ## Gradle ``` -implementation 'eu.chainfire:librootjavadaemon:1.2.0' +implementation 'eu.chainfire:librootjavadaemon:1.3.0' ``` You should update to the latest libRootJava and libRootJavaDaemon at the diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 3bdb6ae..2414543 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -27,10 +27,10 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.2.0' + implementation 'eu.chainfire:librootjava:1.3.0' } task sourcesJar(type: Jar) { @@ -64,7 +64,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.2.0' + libraryVersion = '1.3.0' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 0f22add..1019acc 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -36,10 +36,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.2.0-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.2.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.3.0-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.2.0' - implementation 'eu.chainfire:librootjavadaemon:1.2.0' + implementation 'eu.chainfire:librootjava:1.3.0' + implementation 'eu.chainfire:librootjavadaemon:1.3.0' } From aae3fa31af42c0a172b5656b467309aaa8449760 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Mon, 13 May 2019 16:52:37 +0200 Subject: [PATCH 17/29] Fix ProGuard issue Closes #1 This fix worked for the same issue in a different project, not sure why the issue shows up only for some projects, and even only for some people cloning the same git. --- librootjava_example/build.gradle | 1 + librootjavadaemon_example/build.gradle | 1 + 2 files changed, 2 insertions(+) diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index 6a58f6d..9a34f77 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -15,6 +15,7 @@ android { buildTypes { release { minifyEnabled true + useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 1019acc..017d4b8 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -15,6 +15,7 @@ android { buildTypes { release { minifyEnabled true + useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } From aeab2d73ec0867ae14f7a8d209f670937eac6899 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Mon, 21 Dec 2020 18:07:25 +0100 Subject: [PATCH 18/29] Android 11 doesn't like # character in service names --- .../main/java/eu/chainfire/librootjavadaemon/RootDaemon.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java index ac625bb..f122409 100644 --- a/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java +++ b/librootjavadaemon/src/main/java/eu/chainfire/librootjavadaemon/RootDaemon.java @@ -184,7 +184,7 @@ public interface OnExitListener { */ @SuppressLint("PrivateApi") public static void daemonize(String packageName, int code, boolean surviveFrameworkRestart, OnExitListener exitListener) { - String id = packageName + "#" + String.valueOf(code) + "#daemonize"; + String id = packageName + "_" + String.valueOf(code) + "_daemonize"; File apk = new File(System.getenv("CLASSPATH")); final String version = String.format(Locale.ENGLISH, "%s:%d:%d", apk.getAbsolutePath(), apk.lastModified(), apk.length()); From 2cc1e9b788f89c483a0fff7d471c13eaf45abdb9 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Mon, 21 Dec 2020 18:20:12 +0100 Subject: [PATCH 19/29] v1.3.1 --- librootjava/README.md | 2 +- librootjava/build.gradle | 2 +- librootjava_example/build.gradle | 4 ++-- librootjavadaemon/README.md | 2 +- librootjavadaemon/build.gradle | 6 +++--- librootjavadaemon_example/build.gradle | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/librootjava/README.md b/librootjava/README.md index 7bca846..3fa19ca 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -431,7 +431,7 @@ an Android 10 preview comes out! ## Gradle ``` -implementation 'eu.chainfire:librootjava:1.3.0' +implementation 'eu.chainfire:librootjava:1.3.1' ``` ## Notes diff --git a/librootjava/build.gradle b/librootjava/build.gradle index 250c360..e5ec9f5 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -51,7 +51,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.0' + libraryVersion = '1.3.1' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index 9a34f77..d4f33ce 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -36,8 +36,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.0' + implementation 'eu.chainfire:librootjava:1.3.1' } diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index 175f953..88f3a84 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -151,7 +151,7 @@ on the Android site. ## Gradle ``` -implementation 'eu.chainfire:librootjavadaemon:1.3.0' +implementation 'eu.chainfire:librootjavadaemon:1.3.1' ``` You should update to the latest libRootJava and libRootJavaDaemon at the diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 2414543..97dd435 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -27,10 +27,10 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.0' + implementation 'eu.chainfire:librootjava:1.3.1' } task sourcesJar(type: Jar) { @@ -64,7 +64,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.0' + libraryVersion = '1.3.1' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 017d4b8..9d81a7e 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -37,10 +37,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.0-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.3.0-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.3.1-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.0' - implementation 'eu.chainfire:librootjavadaemon:1.3.0' + implementation 'eu.chainfire:librootjava:1.3.1' + implementation 'eu.chainfire:librootjavadaemon:1.3.1' } From 8759ccbde9f978139c799d29434e6a69cc63a213 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 23 Dec 2020 10:28:11 +0100 Subject: [PATCH 20/29] Deprecation notice --- .github/workflows/ci.yml | 12 ++++++++++++ librootjava/README.md | 19 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..44f1e65 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,12 @@ +name: ci +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 1.8 + - uses: gradle/wrapper-validation-action@v1 + - run: ./gradlew build diff --git a/librootjava/README.md b/librootjava/README.md index 3fa19ca..b94740a 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -1,5 +1,7 @@ # libRootJava +[![ci][1]][2] + Run Java (and Kotlin) code as root! - Runs code directly from your APK @@ -20,6 +22,18 @@ crediting me is appreciated. If you modify the library itself when you use it in your projects, you are kindly requested to share the sources of those modifications. +## Deprecated + +This library is not under active development right now, as I've mostly +moved away from the Android world. While I believe it still works great, +if it breaks due to changes on new Android versions or root solutions, +fixes may be slow to appear. + +If you're writing a new app, you might consider using +[TopJohnWu's libsu](https://github.com/topjohnwu/libsu) instead. Barring +some edge-cases (that I personally seem to be the biggest user of) the +capabilities should be similar, but it's likely to be better maintained. + ## Spaghetti Sauce Project This library is part of the [Spaghetti Sauce Project](https://github.com/Chainfire/spaghetti_sauce_project). @@ -438,4 +452,7 @@ implementation 'eu.chainfire:librootjava:1.3.1' This library includes its own Logger class that is used throughout, which should probably have been refactored out. -It wasn't. \ No newline at end of file +It wasn't. + +[1]: https://github.com/Chainfire/libsuperuser/workflows/ci/badge.svg +[2]: https://github.com/Chainfire/libsuperuser/actions From 45e78151523fd4cae58ee5c75c31a8e782c57a87 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 23 Dec 2020 10:30:12 +0100 Subject: [PATCH 21/29] Update CI --- librootjava/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librootjava/README.md b/librootjava/README.md index b94740a..361ce2f 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -454,5 +454,5 @@ This library includes its own Logger class that is used throughout, which should probably have been refactored out. It wasn't. -[1]: https://github.com/Chainfire/libsuperuser/workflows/ci/badge.svg -[2]: https://github.com/Chainfire/libsuperuser/actions +[1]: https://github.com/Chainfire/librootjava/workflows/ci/badge.svg +[2]: https://github.com/Chainfire/librootjava/actions From 28bed81511a8f0a7f7d3f7e2f37aa6656d255b5f Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 23 Dec 2020 10:32:56 +0100 Subject: [PATCH 22/29] gradlew permission for CI --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From ed22c74cb63f6e5d74e94fa4dba29ad9b8779f2a Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Wed, 23 Dec 2020 10:36:36 +0100 Subject: [PATCH 23/29] Build without local.properties --- librootjava/build.gradle | 4 +++- librootjavadaemon/build.gradle | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/librootjava/build.gradle b/librootjava/build.gradle index e5ec9f5..f95c3a4 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -83,7 +83,9 @@ group = publishedGroupId bintray { Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) + if (project.rootProject.file('local.properties').exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } user = properties.getProperty('bintray.user') key = properties.getProperty('bintray.apikey') diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 97dd435..fa108e1 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -96,7 +96,9 @@ group = publishedGroupId bintray { Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) + if (project.rootProject.file('local.properties').exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } user = properties.getProperty('bintray.user') key = properties.getProperty('bintray.apikey') From 607a6d48aa486e7f829b0fef0f053f1e5f96a45e Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 11:35:03 +0100 Subject: [PATCH 24/29] Move from jCenter/binTray to JitPack --- build.gradle | 2 +- jitpack.yml | 2 ++ librootjava/build.gradle | 34 +----------------------- librootjava_example/build.gradle | 8 +++--- librootjavadaemon/build.gradle | 36 ++------------------------ librootjavadaemon_example/build.gradle | 12 ++++----- 6 files changed, 16 insertions(+), 78 deletions(-) create mode 100644 jitpack.yml diff --git a/build.gradle b/build.gradle index 9a78710..4ae2089 100644 --- a/build.gradle +++ b/build.gradle @@ -6,13 +6,13 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } } allprojects { repositories { google() + maven { url 'https://jitpack.io' } jcenter() mavenLocal() } diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..d1e6c1e --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +install: + - ./gradlew clean :librootjava:install :librootjavadaemon:install diff --git a/librootjava/build.gradle b/librootjava/build.gradle index f95c3a4..ec94150 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' android { compileSdkVersion 26 @@ -51,7 +50,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.1' + libraryVersion = '1.3.2' developerId = 'Chainfire' developerName = 'Jorrit Jongma' @@ -81,35 +80,6 @@ task installMavenLocal(type: Upload) { version = libraryVersion group = publishedGroupId -bintray { - Properties properties = new Properties() - if (project.rootProject.file('local.properties').exists()) { - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - } - user = properties.getProperty('bintray.user') - key = properties.getProperty('bintray.apikey') - - configurations = ['archives'] - dryRun = false - publish = true - pkg { - repo = bintrayRepo - name = libraryName - desc = libraryDescription - websiteUrl = siteUrl - issueTrackerUrl = issueTrackerUrl // doesn't actually work? - vcsUrl = gitUrl - //githubRepo = gitUrl // some more bintray weirdness here, breaks upload - //githubReleaseNotesFile = 'README.md' - licenses = allLicenses - publicDownloadNumbers = true - version { - name = libraryVersion - released = new Date() - } - } -} - install { repositories.mavenInstaller { pom.project { @@ -140,5 +110,3 @@ install { } } } - -bintrayUpload.dependsOn install diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index d4f33ce..1069792 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -28,7 +28,7 @@ dependencies { implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'eu.chainfire:libsuperuser:1.0.0.+' + implementation 'eu.chainfire:libsuperuser:1.1.1' // --- librootjava dependency --- @@ -36,8 +36,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.2-SNAPSHOT') { changing = true } - /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.1' + /* Use jitpack version */ + implementation 'eu.chainfire:librootjava:1.3.2' } diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index fa108e1..58d01a4 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' android { compileSdkVersion 26 @@ -30,7 +29,7 @@ dependencies { //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.1' + implementation 'eu.chainfire:librootjava:1.3.2' } task sourcesJar(type: Jar) { @@ -64,7 +63,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.1' + libraryVersion = '1.3.2' developerId = 'Chainfire' developerName = 'Jorrit Jongma' @@ -94,35 +93,6 @@ task installMavenLocal(type: Upload) { version = libraryVersion group = publishedGroupId -bintray { - Properties properties = new Properties() - if (project.rootProject.file('local.properties').exists()) { - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - } - user = properties.getProperty('bintray.user') - key = properties.getProperty('bintray.apikey') - - configurations = ['archives'] - dryRun = false - publish = true - pkg { - repo = bintrayRepo - name = libraryName - desc = libraryDescription - websiteUrl = siteUrl - issueTrackerUrl = issueTrackerUrl // doesn't actually work? - vcsUrl = gitUrl - //githubRepo = gitUrl // some more bintray weirdness here, breaks upload - //githubReleaseNotesFile = 'README.md' - licenses = allLicenses - publicDownloadNumbers = true - version { - name = libraryVersion - released = new Date() - } - } -} - install { repositories.mavenInstaller { pom.project { @@ -153,5 +123,3 @@ install { } } } - -bintrayUpload.dependsOn install diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 9d81a7e..ad46554 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -28,7 +28,7 @@ dependencies { implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'eu.chainfire:libsuperuser:1.0.0.+' + implementation 'eu.chainfire:libsuperuser:1.1.1' // --- librootjava and librootjavadaemon dependencies --- @@ -37,10 +37,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.3.1-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.2-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.3.2-SNAPSHOT') { changing = true } - /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.1' - implementation 'eu.chainfire:librootjavadaemon:1.3.1' + /* Use jitpack version */ + implementation 'eu.chainfire:librootjava:1.3.2' + implementation 'eu.chainfire:librootjavadaemon:1.3.2' } From acdaf6b7ca134e43ad8db40c907beca52a3a1d43 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 12:33:09 +0100 Subject: [PATCH 25/29] Build updates --- build.gradle | 2 +- gradle.properties | 19 ++++++++----------- gradle/wrapper/gradle-wrapper.properties | 4 ++-- librootjava/build.gradle | 4 ++-- .../java/eu/chainfire/librootjava/Logger.java | 2 +- librootjava_example/build.gradle | 3 +-- librootjavadaemon/build.gradle | 4 ++-- librootjavadaemon_example/build.gradle | 3 +-- 8 files changed, 18 insertions(+), 23 deletions(-) diff --git a/build.gradle b/build.gradle index 4ae2089..5e67a91 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:4.1.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } diff --git a/gradle.properties b/gradle.properties index 1d3591c..9c36dce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,15 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html - +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m +# Default value: -Xmx1024m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +#Sat Feb 05 12:08:52 CET 2022 +#android.enableJetifier=true +android.useAndroidX=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48effd2..44d10f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Nov 09 13:15:43 CET 2018 +#Sat Feb 05 12:08:34 CET 2022 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/librootjava/build.gradle b/librootjava/build.gradle index ec94150..7cec259 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -2,8 +2,8 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' android { - compileSdkVersion 26 - buildToolsVersion '28.0.3' + compileSdkVersion 30 + buildToolsVersion '30.0.3' defaultConfig { minSdkVersion 21 /* was 14 pre-Binder/AIDL */ targetSdkVersion 26 diff --git a/librootjava/src/main/java/eu/chainfire/librootjava/Logger.java b/librootjava/src/main/java/eu/chainfire/librootjava/Logger.java index 5ad1ee0..b2f81f9 100644 --- a/librootjava/src/main/java/eu/chainfire/librootjava/Logger.java +++ b/librootjava/src/main/java/eu/chainfire/librootjava/Logger.java @@ -23,7 +23,7 @@ @SuppressWarnings({"unused", "WeakerAccess"}) public class Logger { private static String getDefaultLogTag(){ - String tag = BuildConfig.APPLICATION_ID; + String tag = BuildConfig.LIBRARY_PACKAGE_NAME; int p; while ((p = tag.indexOf('.')) >= 0) { tag = tag.substring(p + 1); diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index 1069792..3de06c8 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.3' defaultConfig { applicationId "eu.chainfire.librootjava_example" @@ -15,7 +15,6 @@ android { buildTypes { release { minifyEnabled true - useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 58d01a4..bc594e9 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -2,8 +2,8 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' android { - compileSdkVersion 26 - buildToolsVersion '28.0.3' + compileSdkVersion 30 + buildToolsVersion '30.0.3' defaultConfig { minSdkVersion 21 targetSdkVersion 26 diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index ad46554..4278d2c 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.3' defaultConfig { applicationId "eu.chainfire.librootjavadaemon_example" @@ -15,7 +15,6 @@ android { buildTypes { release { minifyEnabled true - useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } From 4c0af8fcfbe20fc85db9f4995d9337784d262cb4 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 12:40:45 +0100 Subject: [PATCH 26/29] Fix CI --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44f1e65..baee9c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,4 +9,5 @@ jobs: with: java-version: 1.8 - uses: gradle/wrapper-validation-action@v1 - - run: ./gradlew build + - run: ./gradlew :librootjava:build + From c5aa981aa334ed4a50f43b90a1246e8f600ad413 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 12:47:58 +0100 Subject: [PATCH 27/29] Update README and version numbers --- librootjava/README.md | 19 +++++++++++++++++-- librootjava/build.gradle | 2 +- librootjava_example/build.gradle | 4 ++-- librootjavadaemon/README.md | 19 ++++++++++++++++++- librootjavadaemon/build.gradle | 6 +++--- librootjavadaemon_example/build.gradle | 8 ++++---- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/librootjava/README.md b/librootjava/README.md index 361ce2f..b866116 100644 --- a/librootjava/README.md +++ b/librootjava/README.md @@ -1,6 +1,6 @@ # libRootJava -[![ci][1]][2] +[![ci][1]][2] [![](https://jitpack.io/v/eu.chainfire/librootjava.svg)](https://jitpack.io/#eu.chainfire/librootjava) Run Java (and Kotlin) code as root! @@ -444,8 +444,23 @@ an Android 10 preview comes out! ## Gradle +Root `build.gradle`: + +``` +allprojects { + repositories { + ... + maven { url 'https://jitpack.io' } + } +} ``` -implementation 'eu.chainfire:librootjava:1.3.1' + +Module `build.gradle`: + +``` +dependencies { + implementation 'eu.chainfire:librootjava:1.3.3' +} ``` ## Notes diff --git a/librootjava/build.gradle b/librootjava/build.gradle index 7cec259..4ff774b 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -50,7 +50,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.2' + libraryVersion = '1.3.3' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjava_example/build.gradle b/librootjava_example/build.gradle index 3de06c8..fdf499e 100644 --- a/librootjava_example/build.gradle +++ b/librootjava_example/build.gradle @@ -35,8 +35,8 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.2-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true } /* Use jitpack version */ - implementation 'eu.chainfire:librootjava:1.3.2' + implementation 'eu.chainfire:librootjava:1.3.3' } diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index 88f3a84..339d4b7 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -1,5 +1,7 @@ # libRootJavaDaemon +[![](https://jitpack.io/v/eu.chainfire/librootjava.svg)](https://jitpack.io/#eu.chainfire/librootjava) + Add-on for [libRootJava](../librootjava) to run the root process as a daemon. @@ -150,8 +152,23 @@ on the Android site. ## Gradle +Root `build.gradle`: + +``` +allprojects { + repositories { + ... + maven { url 'https://jitpack.io' } + } +} +``` + +Module `build.gradle`: + ``` -implementation 'eu.chainfire:librootjavadaemon:1.3.1' +dependencies { + implementation 'eu.chainfire:librootjavadaemon:1.3.3' +} ``` You should update to the latest libRootJava and libRootJavaDaemon at the diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index bc594e9..6f84b84 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -26,10 +26,10 @@ dependencies { //implementation project(':librootjava') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.1-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true } /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.2' + implementation 'eu.chainfire:librootjava:1.3.3' } task sourcesJar(type: Jar) { @@ -63,7 +63,7 @@ ext { gitUrl = 'https://github.com/Chainfire/librootjava.git' issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues' - libraryVersion = '1.3.2' + libraryVersion = '1.3.3' developerId = 'Chainfire' developerName = 'Jorrit Jongma' diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index 4278d2c..eb1a075 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -36,10 +36,10 @@ dependencies { //implementation project(':librootjavadaemon') /* Use local Maven repository version, installed by installMavenLocal Gradle task */ - //implementation('eu.chainfire:librootjava:1.3.2-SNAPSHOT') { changing = true } - //implementation('eu.chainfire:librootjavadaemon:1.3.2-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true } + //implementation('eu.chainfire:librootjavadaemon:1.3.3-SNAPSHOT') { changing = true } /* Use jitpack version */ - implementation 'eu.chainfire:librootjava:1.3.2' - implementation 'eu.chainfire:librootjavadaemon:1.3.2' + implementation 'eu.chainfire:librootjava:1.3.3' + implementation 'eu.chainfire:librootjavadaemon:1.3.3' } From 1a08dff59238e6807a6ed06f46313138699e4543 Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 13:03:10 +0100 Subject: [PATCH 28/29] Update README again and clean bintray references --- librootjava/build.gradle | 4 ---- librootjavadaemon/README.md | 2 +- librootjavadaemon/build.gradle | 8 ++------ librootjavadaemon_example/build.gradle | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/librootjava/build.gradle b/librootjava/build.gradle index 4ff774b..5ff11ec 100644 --- a/librootjava/build.gradle +++ b/librootjava/build.gradle @@ -59,9 +59,6 @@ ext { licenseName = 'The Apache Software License, Version 2.0' licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' allLicenses = ["Apache-2.0"] - - bintrayRepo = 'maven' - bintrayName = artifact } task installMavenLocal(type: Upload) { @@ -76,7 +73,6 @@ task installMavenLocal(type: Upload) { } } -// Workaround bintray bug ignoring these from pom and bintray settings version = libraryVersion group = publishedGroupId diff --git a/librootjavadaemon/README.md b/librootjavadaemon/README.md index 339d4b7..c2f5496 100644 --- a/librootjavadaemon/README.md +++ b/librootjavadaemon/README.md @@ -167,7 +167,7 @@ Module `build.gradle`: ``` dependencies { - implementation 'eu.chainfire:librootjavadaemon:1.3.3' + implementation 'eu.chainfire.librootjava:librootjavadaemon:1.3.3' } ``` diff --git a/librootjavadaemon/build.gradle b/librootjavadaemon/build.gradle index 6f84b84..5e9a168 100644 --- a/librootjavadaemon/build.gradle +++ b/librootjavadaemon/build.gradle @@ -28,8 +28,8 @@ dependencies { /* Use local Maven repository version, installed by installMavenLocal Gradle task */ //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true } - /* Use bintray/jcenter version */ - implementation 'eu.chainfire:librootjava:1.3.3' + /* Use jitpack version */ + implementation 'eu.chainfire.librootjava:librootjava:1.3.3' } task sourcesJar(type: Jar) { @@ -72,9 +72,6 @@ ext { licenseName = 'The Apache Software License, Version 2.0' licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' allLicenses = ["Apache-2.0"] - - bintrayRepo = 'maven' - bintrayName = artifact } task installMavenLocal(type: Upload) { @@ -89,7 +86,6 @@ task installMavenLocal(type: Upload) { } } -// Workaround bintray bug ignoring these from pom and bintray settings version = libraryVersion group = publishedGroupId diff --git a/librootjavadaemon_example/build.gradle b/librootjavadaemon_example/build.gradle index eb1a075..af9981a 100644 --- a/librootjavadaemon_example/build.gradle +++ b/librootjavadaemon_example/build.gradle @@ -41,5 +41,5 @@ dependencies { /* Use jitpack version */ implementation 'eu.chainfire:librootjava:1.3.3' - implementation 'eu.chainfire:librootjavadaemon:1.3.3' + implementation 'eu.chainfire.librootjava:librootjavadaemon:1.3.3' } From baf99656d0a85b50bc626e1bfca963c2cb3ba87e Mon Sep 17 00:00:00 2001 From: Jorrit Jongma Date: Sat, 5 Feb 2022 13:15:08 +0100 Subject: [PATCH 29/29] Update jCenter dependency to mavenCentral --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5e67a91..d168446 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:4.1.3' @@ -13,7 +13,7 @@ allprojects { repositories { google() maven { url 'https://jitpack.io' } - jcenter() + mavenCentral() mavenLocal() } }