postExecution, String path) {
326 | if (appProcessBase == null) appProcessBase = getAppProcess();
327 | if (path == null) {
328 | if (!shouldAppProcessBeRelocated()) {
329 | return appProcessBase;
330 | }
331 |
332 | path = "/dev";
333 | if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
334 | File cacheDir = context.getCacheDir();
335 | try {
336 | //noinspection ResultOfMethodCallIgnored
337 | cacheDir.mkdirs();
338 | } catch (Exception e) {
339 | // just in case
340 | }
341 | if (cacheDir.exists()) {
342 | try {
343 | path = cacheDir.getCanonicalPath();
344 | } catch (IOException e) {
345 | // should never happen
346 | }
347 | }
348 | }
349 | }
350 |
351 | boolean onData = path.startsWith("/data/");
352 |
353 | String appProcessCopy;
354 | if (guessIfAppProcessIs64Bits(appProcessBase)) {
355 | appProcessCopy = path + "/.app_process64_" + UUID;
356 | } else {
357 | appProcessCopy = path + "/.app_process32_" + UUID;
358 | }
359 | preLaunch.add(String.format(Locale.ENGLISH, "%s cp %s %s >/dev/null 2>/dev/null", BOX, appProcessBase, appProcessCopy));
360 | preLaunch.add(String.format(Locale.ENGLISH, "%s chmod %s %s >/dev/null 2>/dev/null", BOX, onData ? "0766" : "0700", appProcessCopy));
361 | if (onData) preLaunch.add(String.format(Locale.ENGLISH, "restorecon %s >/dev/null 2>/dev/null", appProcessCopy));
362 | postExecution.add(String.format(Locale.ENGLISH, "%s rm %s >/dev/null 2>/dev/null", BOX, appProcessCopy));
363 | return appProcessCopy;
364 | }
365 | }
366 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/Debugger.java:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.File;
6 | import java.io.FileDescriptor;
7 | import java.io.FileOutputStream;
8 | import java.io.FileReader;
9 | import java.io.IOException;
10 | import java.io.PrintStream;
11 |
12 | /**
13 | * Utility methods to support debugging
14 | */
15 | @SuppressWarnings({"unused", "WeakerAccess"})
16 | public class Debugger {
17 | /**
18 | * Is debugging enabled ?
19 | */
20 | static volatile boolean enabled = false;
21 |
22 | /**
23 | * Is debugging enabled ?
24 | *
25 | * If called from non-root, this will return if we are launching new processes with debugging
26 | * enabled. If called from root, this will return if the current process was launched
27 | * with debugging enabled.
28 | *
29 | * @return Debugging enabled
30 | */
31 | public static boolean isEnabled() {
32 | if (android.os.Process.myUid() >= 10000) {
33 | return enabled;
34 | } else {
35 | return Reflection.isDebuggingEnabled();
36 | }
37 | }
38 |
39 | /**
40 | * Launch root processes with debugging enabled ?
41 | *
42 | * To prevent issues on release builds, BuildConfig.DEBUG should be respected. So instead
43 | * of passing true you would pass BuildConfig.DEBUG, while false
44 | * remains false.
45 | *
46 | * @param enabled Enable debugging (default: false)
47 | */
48 | public static void setEnabled(boolean enabled) {
49 | Debugger.enabled = enabled;
50 | }
51 |
52 | /**
53 | * Cache for name to present to debugger. Really only used to determine if we have manually
54 | * set a name already.
55 | */
56 | private static volatile String name = null;
57 |
58 | /**
59 | * Set name to present to debugger
60 | *
61 | * This method should only be called from the process running as root.
62 | *
63 | * Debugging will not work if this method has not been called, but the
64 | * {@link #waitFor(boolean)} method will call it for you, if used.
65 | *
66 | * {@link RootJava#restoreOriginalLdLibraryPath()} should have been called before calling
67 | * this method.
68 | *
69 | * To prevent issues with release builds, this call should be wrapped in a BuildConfig.DEBUG
70 | * check.
71 | *
72 | * @param name Name to present to debugger, or null to use process name
73 | *
74 | * @see #waitFor(boolean)
75 | */
76 | public static void setName(String name) {
77 | if (Debugger.name == null) {
78 | if (name == null) {
79 | final File cmdline = new File("/proc/" + android.os.Process.myPid() + "/cmdline");
80 | try (BufferedReader reader = new BufferedReader(new FileReader(cmdline))) {
81 | name = reader.readLine();
82 | if (name.indexOf(' ') > 0) name = name.substring(0, name.indexOf(' '));
83 | if (name.indexOf('\0') > 0) name = name.substring(0, name.indexOf('\0'));
84 | } catch (IOException e) {
85 | name = "librootjava:unknown";
86 | }
87 | }
88 | Debugger.name = name;
89 | Reflection.setAppName(name);
90 | }
91 | }
92 |
93 | /**
94 | * Wait for debugger to connect
95 | *
96 | * This method should only be called from the process running as root.
97 | *
98 | * If {@link #setName(String)} has not been called manually, the display name for the
99 | * debugger will be set to the current process name.
100 | *
101 | * After this method has been called, you can connect AndroidStudio's debugger to the root
102 | * process via Run->Attach Debugger to Android process.
103 | *
104 | * {@link RootJava#restoreOriginalLdLibraryPath()} should have been called before calling
105 | * this method.
106 | *
107 | * Android's internal debugger code will print to STDOUT during this call using System.println,
108 | * which may be annoying if your non-root process communicates with the root process through
109 | * STDIN/STDOUT/STDERR. If the swallowOutput parameter is set to true, System.println
110 | * will be temporarily redirected, and reset back to STDOUT afterwards.
111 | *
112 | * To prevent issues with release builds, this call should be wrapped in a BuildConfig.DEBUG
113 | * check:
114 | *
115 | *
116 | * {@code
117 | * if (BuildConfig.DEBUG) {
118 | * Debugger.waitFor(true);
119 | * }
120 | * }
121 | *
122 | *
123 | * @param swallowOutput Temporarily redirect STDOUT ?
124 | */
125 | public static void waitFor(boolean swallowOutput) {
126 | if (Reflection.isDebuggingEnabled()) {
127 | if (swallowOutput) {
128 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
129 | System.setOut(new PrintStream(buffer));
130 | }
131 | setName(null);
132 | android.os.Debug.waitForDebugger();
133 | if (swallowOutput) {
134 | System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/Linker.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava;
17 |
18 | import android.annotation.TargetApi;
19 | import android.os.Build;
20 | import android.system.Os;
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 |
25 | /**
26 | * Internal utility methods that deal with LD_LIBRARY_PATH
27 | */
28 | @SuppressWarnings({"WeakerAccess", "SameParameterValue"})
29 | class Linker {
30 | /**
31 | * Cross-API method to get environment variable
32 | *
33 | * @param name Name of variable
34 | * @return Value of variable
35 | */
36 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
37 | private static String getenv(String name) {
38 | if (haveLinkerNamespaces()) {
39 | // real OS call
40 | return Os.getenv(name);
41 | } else {
42 | // cached by JVM at startup
43 | return System.getenv(name);
44 | }
45 | }
46 |
47 | /**
48 | * Set environment variable on newer API levels.
49 | *
50 | * The updated value is set at OS level, but System.getenv() calls may still return
51 | * the old values, as those are cached at JVM startup.
52 | *
53 | * @param name Name of variable
54 | * @param value Value of variable
55 | */
56 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
57 | private static void setenv(String name, String value) {
58 | if (haveLinkerNamespaces()) {
59 | // note: restored path may not show up in methods depending on System.getenv
60 | try {
61 | if (value == null) {
62 | Os.unsetenv(name);
63 | } else {
64 | Os.setenv(name, value, true);
65 | }
66 | } catch (Exception e) {
67 | Logger.ex(e);
68 | }
69 | } // if we don't have linker namespaces this call isn't needed
70 | }
71 |
72 | /**
73 | * Are linker namespaces used?
74 | *
75 | * Android 7.0 (API level 24) and up use linker namespaces, which prevent apps from loading
76 | * native libraries outside of that namespace.
77 | *
78 | * @see #getPatchedLdLibraryPath(boolean, String[])
79 | *
80 | * @return If linker namespaces are used
81 | */
82 | @TargetApi(23)
83 | private static boolean haveLinkerNamespaces() {
84 | return (
85 | (Build.VERSION.SDK_INT >= 24) ||
86 |
87 | // 7.0 preview
88 | ((Build.VERSION.SDK_INT == 23) && (Build.VERSION.PREVIEW_SDK_INT != 0))
89 | );
90 | }
91 |
92 | /**
93 | * Returns a value for LD_LIBRARY_PATH fit to bypass linker namespace restrictions and load
94 | * system as well as our own native libraries.
95 | *
96 | * Android 7.0 (API level 24) and up use linker namespaces, which prevent apps from loading
97 | * native libraries outside of that namespace.
98 | *
99 | * These are also employed for Java code running as root through app_process. One way to
100 | * bypass linker namespace is to explicitly set the LD_LIBRARY_PATH variable. Getting that
101 | * to work properly is trickier than it sounds with several edge-cases, do not modify this
102 | * code without testing excessively on different Android devices versions!
103 | *
104 | * We also add a marker and include the original LD_LIBRARY_PATH, so it's value may be
105 | * restored after load. Otherwise, executing other binaries may fail.
106 | *
107 | * @see #restoreOriginalLdLibraryPath()
108 | *
109 | * @param use64bit Use 64-bit paths
110 | * @param extraPaths Additional paths to include
111 | * @return Patched value for LD_LIBRARY_PATH
112 | */
113 | static String getPatchedLdLibraryPath(boolean use64bit, String[] extraPaths) {
114 | String LD_LIBRARY_PATH = getenv("LD_LIBRARY_PATH");
115 | if (!haveLinkerNamespaces()) {
116 | if (LD_LIBRARY_PATH != null) {
117 | // some firmwares have this, some don't, launch at boot may fail without, or with,
118 | // so just copy what is the current situation
119 | return LD_LIBRARY_PATH;
120 | }
121 | return null;
122 | } else {
123 | StringBuilder paths = new StringBuilder();
124 |
125 | // these default paths are taken from linker code in AOSP, and are normally used
126 | // when LD_LIBRARY_PATH isn't set explicitly
127 | String[] scan;
128 | if (use64bit) {
129 | scan = new String[]{
130 | "/system/lib64",
131 | "/data/lib64",
132 | "/vendor/lib64",
133 | "/data/vendor/lib64"
134 | };
135 | } else {
136 | scan = new String[]{
137 | "/system/lib",
138 | "/data/lib",
139 | "/vendor/lib",
140 | "/data/vendor/lib"
141 | };
142 | }
143 |
144 | for (String path : scan) {
145 | File file = (new File(path));
146 | if (file.exists()) {
147 | try {
148 | paths.append(file.getCanonicalPath());
149 | paths.append(':');
150 |
151 | // This part can trigger quite a few SELinux policy violations, they
152 | // are harmless for our purpose, but if you're trying to trace SELinux
153 | // related issues in your Binder calls, you may want to comment this part
154 | // out. It is rarely (but still sometimes) actually required for your code
155 | // to run.
156 |
157 | File[] files = file.listFiles();
158 | if (files != null) {
159 | for (File dir : files) {
160 | if (dir.isDirectory()) {
161 | paths.append(dir.getCanonicalPath());
162 | paths.append(':');
163 | }
164 | }
165 | }
166 | } catch (IOException e) {
167 | // failed to resolve canonical path
168 | }
169 | }
170 | }
171 |
172 | if (extraPaths != null) {
173 | for (String path : extraPaths) {
174 | paths.append(path);
175 | paths.append(':');
176 | }
177 | }
178 |
179 | paths.append("/librootjava"); // for detection
180 |
181 | if (LD_LIBRARY_PATH != null) {
182 | paths.append(':');
183 | paths.append(LD_LIBRARY_PATH);
184 | }
185 |
186 | return paths.toString();
187 | }
188 | }
189 |
190 | /**
191 | * Retrieve the pre-patched value of LD_LIBRARY_PATH.
192 | *
193 | * @see #getPatchedLdLibraryPath(boolean, String[])
194 | *
195 | * @return Original value of LD_LIBRARY_PATH
196 | */
197 | private static String getOriginalLdLibraryPath() {
198 | String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH");
199 | if (LD_LIBRARY_PATH == null)
200 | return null;
201 |
202 | if (LD_LIBRARY_PATH.endsWith(":/librootjava"))
203 | return null;
204 |
205 | if (LD_LIBRARY_PATH.contains(":/librootjava:"))
206 | return LD_LIBRARY_PATH.substring(LD_LIBRARY_PATH.indexOf(":/librootjava:") + ":/librootjava:".length());
207 |
208 | return LD_LIBRARY_PATH;
209 | }
210 |
211 | /**
212 | * Restores correct LD_LIBRARY_PATH environment variable.
213 | *
214 | * @see Linker#getPatchedLdLibraryPath(boolean, String[])
215 | */
216 | static void restoreOriginalLdLibraryPath() {
217 | setenv("LD_LIBRARY_PATH", getOriginalLdLibraryPath());
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/Logger.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava;
17 |
18 | import java.util.Locale;
19 |
20 | /**
21 | * Provide logging functionality
22 | */
23 | @SuppressWarnings({"unused", "WeakerAccess"})
24 | public class Logger {
25 | private static String getDefaultLogTag(){
26 | String tag = BuildConfig.LIBRARY_PACKAGE_NAME;
27 | int p;
28 | while ((p = tag.indexOf('.')) >= 0) {
29 | tag = tag.substring(p + 1);
30 | }
31 | return tag;
32 | }
33 |
34 | private static String LOG_TAG = getDefaultLogTag();
35 |
36 | private static boolean log = false;
37 |
38 | /**
39 | * Set LOG_TAG
40 | * @param logTag LOG_TAG to use
41 | */
42 | public static void setLogTag(String logTag) {
43 | LOG_TAG = logTag;
44 | }
45 |
46 | /**
47 | * @return LOG_TAG
48 | */
49 | public static String getLogTag() {
50 | return LOG_TAG;
51 | }
52 |
53 | /**
54 | * @param enabled Enable debug logging
55 | */
56 | public static void setDebugLogging(boolean enabled) {
57 | log = enabled;
58 | }
59 |
60 | /**
61 | * Log on debug level
62 | *
63 | * @param message Message to format
64 | * @param args Format arguments
65 | */
66 | public static void d(String message, Object... args) {
67 | if (log) {
68 | if ((args != null) && (args.length > 0)) {
69 | message = String.format(Locale.ENGLISH, message, args);
70 | }
71 | android.util.Log.d(LOG_TAG, message);
72 | }
73 | }
74 |
75 | /**
76 | * Log on debug level with prefix
77 | *
78 | * @param prefix Prefix to prepend
79 | * @param message Message to format
80 | * @param args Format arguments
81 | */
82 | public static void dp(String prefix, String message, Object... args) {
83 | if (log) {
84 | if ((args != null) && (args.length > 0)) {
85 | message = String.format(Locale.ENGLISH, message, args);
86 | }
87 | android.util.Log.d(LOG_TAG, String.format(Locale.ENGLISH, "[%s]%s%s", prefix,
88 | (message.startsWith("[") || message.startsWith(" ")) ? "" : " ", message));
89 | }
90 | }
91 |
92 | /**
93 | * Log exception (debug level)
94 | *
95 | * @param e Exception to log
96 | */
97 | public static void ex(Exception e) {
98 | if (log) {
99 | dp("EXCEPTION", "%s: %s", e.getClass().getSimpleName(), e.getMessage());
100 | e.printStackTrace();
101 | }
102 | }
103 |
104 | /**
105 | * Log on verbose level
106 | *
107 | * @param message Message to format
108 | * @param args Format arguments
109 | */
110 | public static void v(String message, Object... args) {
111 | android.util.Log.v(LOG_TAG, String.format(Locale.ENGLISH, message, args));
112 | }
113 |
114 | /**
115 | * Log on verbose level with prefix
116 | *
117 | * @param prefix Prefix to prepend
118 | * @param message Message to format
119 | * @param args Format arguments
120 | */
121 | public static void vp(String prefix, String message, Object... args) {
122 | message = String.format(Locale.ENGLISH, message, args);
123 | android.util.Log.v(LOG_TAG, String.format(Locale.ENGLISH, "[%s]%s%s", prefix,
124 | (message.startsWith("[") || message.startsWith(" ")) ? "" : " ", message));
125 | }
126 |
127 | /**
128 | * Log on info level
129 | *
130 | * @param message Message to format
131 | * @param args Format arguments
132 | */
133 | public static void i(String message, Object... args) {
134 | android.util.Log.i(LOG_TAG, String.format(Locale.ENGLISH, message, args));
135 | }
136 |
137 | /**
138 | * Log on info level with prefix
139 | *
140 | * @param prefix Prefix to prepend
141 | * @param message Message to format
142 | * @param args Format arguments
143 | */
144 | public static void ip(String prefix, String message, Object... args) {
145 | message = String.format(Locale.ENGLISH, message, args);
146 | android.util.Log.i(LOG_TAG, String.format(Locale.ENGLISH, "[%s]%s%s", prefix,
147 | (message.startsWith("[") || message.startsWith(" ")) ? "" : " ", message));
148 | }
149 |
150 | /**
151 | * Log on warning level
152 | *
153 | * @param message Message to format
154 | * @param args Format arguments
155 | */
156 | public static void w(String message, Object... args) {
157 | android.util.Log.w(LOG_TAG, String.format(Locale.ENGLISH, message, args));
158 | }
159 |
160 | /**
161 | * Log on warning level with prefix
162 | *
163 | * @param prefix Prefix to prepend
164 | * @param message Message to format
165 | * @param args Format arguments
166 | */
167 | public static void wp(String prefix, String message, Object... args) {
168 | message = String.format(Locale.ENGLISH, message, args);
169 | android.util.Log.w(LOG_TAG, String.format(Locale.ENGLISH, "[%s]%s%s", prefix,
170 | (message.startsWith("[") || message.startsWith(" ")) ? "" : " ", message));
171 | }
172 |
173 | /**
174 | * Log on error level
175 | *
176 | * @param message Message to format
177 | * @param args Format arguments
178 | */
179 | public static void e(String message, Object... args) {
180 | android.util.Log.e(LOG_TAG, String.format(Locale.ENGLISH, message, args));
181 | }
182 |
183 | /**
184 | * Log on error level with prefix
185 | *
186 | * @param prefix Prefix to prepend
187 | * @param message Message to format
188 | * @param args Format arguments
189 | */
190 | public static void ep(String prefix, String message, Object... args) {
191 | message = String.format(Locale.ENGLISH, message, args);
192 | android.util.Log.e(LOG_TAG, String.format(Locale.ENGLISH, "[%s]%s%s", prefix,
193 | (message.startsWith("[") || message.startsWith(" ")) ? "" : " ", message));
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/Policies.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * Utility methods for handling SELinux policies
22 | */
23 | @SuppressWarnings({"unused", "WeakerAccess"})
24 | public class Policies {
25 | /**
26 | * SELinux policies that require patching for the Binder calls to work on newer Android
27 | * versions. Failing to do this may cause Binder transactions to fail.
28 | */
29 | private static String[] required = new String[] {
30 | /* We skip the init context used in older SuperSU versions, as that is potentially
31 | dangerous, and Android versions that actually require this policy modification
32 | are likely to run a SuperSU version that uses it's own SELinux context or Magisk */
33 | "allow appdomain supersu binder { call transfer }",
34 | "allow appdomain magisk binder { call transfer }"
35 | };
36 |
37 | /**
38 | * We only want to patch the SELinux policies once, keep track
39 | */
40 | private static Boolean patched = false;
41 |
42 | /**
43 | * Sets SELinux policies patched state.
44 | *
45 | * By default policies are only patched once. You can trigger the script to include the
46 | * policy patches again once by passing false, or every time by passing null. If you pass
47 | * true, the policies will not be patched.
48 | *
49 | * If you are not using the Binder IPC calls, you may want to set it to true to prevent
50 | * the policies from being needlessly patched.
51 | *
52 | * @param value New policy patched state
53 | */
54 | public static void setPatched(Boolean value) {
55 | patched = value;
56 | }
57 |
58 | /**
59 | * Create script to patch SELinux policies.
60 | *
61 | * @param preLaunch List that retrieves commands to execute to perform the policy patch
62 | */
63 | public static void getPatch(List preLaunch) {
64 | if ((patched == null) || !patched) {
65 | StringBuilder command = new StringBuilder("supolicy --live");
66 | for (String policy : required) {
67 | command.append(" \"").append(policy).append("\"");
68 | }
69 | command.append(" >/dev/null 2>/dev/null");
70 | preLaunch.add(command.toString());
71 | if (patched != null) {
72 | patched = true;
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/Reflection.java:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.IBinder;
7 | import android.os.IInterface;
8 | import android.os.Looper;
9 |
10 | import java.lang.reflect.Constructor;
11 | import java.lang.reflect.Field;
12 | import java.lang.reflect.Method;
13 |
14 | /**
15 | * Reflection-based methods are implemented here, so we have all the methods that are most
16 | * likely to break in one spot.
17 | */
18 | class Reflection {
19 | private static final Object lock = new Object();
20 |
21 | /** Cache for getSystemContext() */
22 | @SuppressLint("StaticFieldLeak")
23 | private static Context systemContext = null;
24 |
25 | /**
26 | * Retrieve system context
27 | *
28 | * Stability: unlikely to change, this implementation works from 1.6 through 9.0
29 | *
30 | * @see RootJava#getSystemContext()
31 | *
32 | * @return system context
33 | */
34 | @SuppressLint("PrivateApi")
35 | static Context getSystemContext() {
36 | synchronized (lock) {
37 | try {
38 | if (systemContext != null) {
39 | return systemContext;
40 | }
41 |
42 | // a prepared Looper is required for the calls below to succeed
43 | if (Looper.getMainLooper() == null) {
44 | try {
45 | Looper.prepareMainLooper();
46 | } catch (Exception e) {
47 | Logger.ex(e);
48 | }
49 | }
50 |
51 | Class> cActivityThread = Class.forName("android.app.ActivityThread");
52 | Method mSystemMain = cActivityThread.getMethod("systemMain");
53 | Method mGetSystemContext = cActivityThread.getMethod("getSystemContext");
54 |
55 | Object oActivityThread = mSystemMain.invoke(null);
56 | Object oContext = mGetSystemContext.invoke(oActivityThread);
57 |
58 | systemContext = (Context)oContext;
59 | return systemContext;
60 | } catch (Exception e) {
61 | Logger.ex(e);
62 | throw new RuntimeException("librootjava: unexpected exception in getSystemContext()");
63 | }
64 | }
65 | }
66 |
67 | /** Cache for getActivityManager() */
68 | private static Object oActivityManager = null;
69 |
70 | /**
71 | * Retrieve ActivityManager instance without needing a context
72 | *
73 | * Stability: has changed before, might change again, rare
74 | *
75 | * @return ActivityManager
76 | */
77 | @SuppressLint("PrivateApi")
78 | @SuppressWarnings({"JavaReflectionMemberAccess"})
79 | private static Object getActivityManager() {
80 | // Return object is AIDL interface IActivityManager, not an ActivityManager or
81 | // ActivityManagerService
82 |
83 | synchronized (lock) {
84 | if (oActivityManager != null) {
85 | return oActivityManager;
86 | }
87 |
88 | try { // marked deprecated in Android source
89 | Class> cActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
90 | Method mGetDefault = cActivityManagerNative.getMethod("getDefault");
91 | oActivityManager = mGetDefault.invoke(null);
92 | return oActivityManager;
93 | } catch (Exception e) {
94 | // possibly removed
95 | }
96 |
97 | try {
98 | // alternative
99 | Class> cActivityManager = Class.forName("android.app.ActivityManager");
100 | Method mGetService = cActivityManager.getMethod("getService");
101 | oActivityManager = mGetService.invoke(null);
102 | return oActivityManager;
103 | } catch (Exception e) {
104 | Logger.ex(e);
105 | }
106 |
107 | throw new RuntimeException("librootjava: unable to retrieve ActivityManager");
108 | }
109 | }
110 |
111 | /** Cache for getFlagReceiverFromShell() */
112 | private static Integer FLAG_RECEIVER_FROM_SHELL = null;
113 |
114 | /**
115 | * Retrieve value of Intent.FLAG_RECEIVER_FROM_SHELL, if it exists
116 | *
117 | * Stability: stable, even if the flag goes away again this is unlikely to affect things
118 | *
119 | * @return FLAG_RECEIVER_FROM_SHELL or 0
120 | */
121 | @SuppressWarnings({"JavaReflectionMemberAccess"})
122 | private static int getFlagReceiverFromShell() {
123 | synchronized (lock) {
124 | if (FLAG_RECEIVER_FROM_SHELL != null) {
125 | return FLAG_RECEIVER_FROM_SHELL;
126 | }
127 |
128 | try {
129 | Field fFlagReceiverFromShell = Intent.class.getDeclaredField("FLAG_RECEIVER_FROM_SHELL");
130 | FLAG_RECEIVER_FROM_SHELL = fFlagReceiverFromShell.getInt(null);
131 | return FLAG_RECEIVER_FROM_SHELL;
132 | } catch (NoSuchFieldException e) {
133 | // not present on all Android versions
134 | } catch (IllegalAccessException e) {
135 | Logger.ex(e);
136 | }
137 |
138 | FLAG_RECEIVER_FROM_SHELL = 0;
139 | return FLAG_RECEIVER_FROM_SHELL;
140 | }
141 | }
142 |
143 | /** Cache for getBroadcastIntent() */
144 | private static Method mBroadcastIntent = null;
145 |
146 | /**
147 | * Retrieve the ActivityManager.broadcastIntent() method
148 | *
149 | * @param cActivityManager ActivityManager class
150 | * @return broadcastIntent method
151 | */
152 | private static Method getBroadcastIntent(Class> cActivityManager) {
153 | synchronized (lock) {
154 | if (mBroadcastIntent != null) {
155 | return mBroadcastIntent;
156 | }
157 |
158 | for (Method m : cActivityManager.getMethods()) {
159 | if (m.getName().equals("broadcastIntent") && (m.getParameterTypes().length == 13)) {
160 | // API 24+
161 | mBroadcastIntent = m;
162 | return mBroadcastIntent;
163 | }
164 | if (m.getName().equals("broadcastIntent") && (m.getParameterTypes().length == 12)) {
165 | // API 21+
166 | mBroadcastIntent = m;
167 | return mBroadcastIntent;
168 | }
169 | }
170 |
171 | throw new RuntimeException("librootjava: unable to retrieve broadcastIntent method");
172 | }
173 | }
174 |
175 | /**
176 | * Broadcast intent
177 | *
178 | * Stability: the implementation for this will definitely change over time
179 | *
180 | * This implementation does not require us to have a context
181 | *
182 | * @see RootJava#sendBroadcast(Intent)
183 | * @see RootIPC#broadcastIPC()
184 | *
185 | * @param intent Intent to broadcast
186 | */
187 | @SuppressLint("PrivateApi")
188 | static void sendBroadcast(Intent intent) {
189 | try {
190 | // Prevent system from complaining about unprotected broadcast, if the field exists
191 | intent.setFlags(getFlagReceiverFromShell());
192 |
193 | Object oActivityManager = getActivityManager();
194 | Method mBroadcastIntent = getBroadcastIntent(oActivityManager.getClass());
195 | if (mBroadcastIntent.getParameterTypes().length == 13) {
196 | // API 24+
197 | mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, null, false, false, 0);
198 | return;
199 | }
200 | if (mBroadcastIntent.getParameterTypes().length == 12) {
201 | // API 21+
202 | mBroadcastIntent.invoke(oActivityManager, null, intent, null, null, 0, null, null, null, -1, false, false, 0);
203 | return;
204 | }
205 | } catch (Exception e) {
206 | Logger.ex(e);
207 | return;
208 | }
209 |
210 | // broadcast wasn't sent if we arrive here
211 | throw new RuntimeException("librootjava: unable to send broadcast");
212 | }
213 |
214 | /**
215 | * Determine if debugging is enabled on the VM level
216 | *
217 | * Stability: unlikely to change, this implementation works from 1.6 through 9.0
218 | *
219 | * @see Debugger#isEnabled()
220 | *
221 | * @return Debugging enabled
222 | */
223 | @SuppressLint("PrivateApi")
224 | static boolean isDebuggingEnabled() {
225 | try {
226 | Class> cVMDebug = Class.forName("dalvik.system.VMDebug");
227 | Method mIsDebuggingEnabled = cVMDebug.getDeclaredMethod("isDebuggingEnabled");
228 | return (Boolean)mIsDebuggingEnabled.invoke(null);
229 | } catch (Exception e) {
230 | Logger.ex(e);
231 | return false;
232 | }
233 | }
234 |
235 | /**
236 | * Set app name for debugger connection
237 | *
238 | * Stability: unlikely to change, this implementation works from 1.6 through 9.0
239 | *
240 | * @see Debugger#setName(String)
241 | */
242 | @SuppressLint("PrivateApi")
243 | static void setAppName(String name) {
244 | try {
245 | Class> cDdmHandleAppName = Class.forName("android.ddm.DdmHandleAppName");
246 | Method m = cDdmHandleAppName.getDeclaredMethod("setAppName", String.class, int.class);
247 | m.invoke(null, name, 0);
248 | } catch (Exception e) {
249 | Logger.ex(e);
250 | }
251 | }
252 |
253 | /**
254 | * Internal class to retrieve an interface from a Binder (Proxy)
255 | *
256 | * @param Interface
257 | */
258 | @SuppressWarnings("unchecked")
259 | static class InterfaceRetriever {
260 | /**
261 | * Stability: stable, as changes to this pattern in AOSP would probably require all
262 | * AIDL-using apps to be recompiled.
263 | *
264 | * @see RootIPCReceiver#getInterfaceFromBinder(IBinder)
265 | *
266 | * @param clazz Class of T
267 | * @param binder Binder proxy to retrieve interface from
268 | * @return T (proxy) instance or null
269 | */
270 | T getInterfaceFromBinder(Class clazz, IBinder binder) {
271 | // There does not appear to be a nice way to do this without reflection,
272 | // though of course you can use T.Stub.asInterface(binder) in final code, that doesn't
273 | // help for our callbacks
274 | try {
275 | Class> cStub = Class.forName(clazz.getName() + "$Stub");
276 | Field fDescriptor = cStub.getDeclaredField("DESCRIPTOR");
277 | fDescriptor.setAccessible(true);
278 |
279 | String descriptor = (String)fDescriptor.get(binder);
280 | IInterface intf = binder.queryLocalInterface(descriptor);
281 | if (clazz.isInstance(intf)) {
282 | // local
283 | return (T)intf;
284 | } else {
285 | // remote
286 | Class> cProxy = Class.forName(clazz.getName() + "$Stub$Proxy");
287 | Constructor> ctorProxy = cProxy.getDeclaredConstructor(IBinder.class);
288 | ctorProxy.setAccessible(true);
289 | return (T)ctorProxy.newInstance(binder);
290 | }
291 | } catch (Exception e) {
292 | Logger.ex(e);
293 | }
294 | return null;
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/librootjava/src/main/java/eu/chainfire/librootjava/RootIPC.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava;
17 |
18 | import android.content.Intent;
19 | import android.os.Bundle;
20 | import android.os.IBinder;
21 | import android.os.RemoteException;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 |
26 | /**
27 | * Binder-based IPC server for the root process
28 | *
29 | * This class wraps the supplied Binder interface in its own helper (primarily to keep track of
30 | * the non-root processes' state), and broadcasts the wrapper to the non-root process.
31 | *
32 | * @see RootIPCReceiver
33 | */
34 | @SuppressWarnings({"unused", "WeakerAccess", "BooleanMethodIsAlwaysInverted", "FieldCanBeLocal", "Convert2Diamond"})
35 | public class RootIPC {
36 | /**
37 | * The non-root process did not connect back to us in a timely fashion after the broadcast
38 | */
39 | public static class TimeoutException extends Exception {
40 | public TimeoutException(String message) {
41 | super(message);
42 | }
43 | }
44 |
45 | private final String packageName;
46 | private final IBinder userIPC;
47 | private final int code;
48 |
49 | private final Object helloWaiter = new Object();
50 | private final Object byeWaiter = new Object();
51 |
52 | private class Connection {
53 | private final IBinder binder;
54 | private final IBinder.DeathRecipient deathRecipient;
55 |
56 | public Connection(IBinder binder, IBinder.DeathRecipient deathRecipient) {
57 | this.binder = binder;
58 | this.deathRecipient = deathRecipient;
59 | }
60 |
61 | public IBinder getBinder() {
62 | return binder;
63 | }
64 |
65 | public IBinder.DeathRecipient getDeathRecipient() {
66 | return deathRecipient;
67 | }
68 | }
69 |
70 | private final List connections = new ArrayList();
71 | private volatile boolean connectionSeen = false;
72 |
73 | /**
74 | * Wrap the supplied Binder, send it to the target package, and optionally wait until a session is connected and completed
75 | *
76 | * @param packageName Package name of process to send Binder to. Use BuildConfig.APPLICATION_ID (double check you're importing the correct BuildConfig!) for convenience
77 | * @param ipc Binder object to wrap and send out
78 | * @param code User-value, should be unique per Binder
79 | * @param connection_timeout_ms How long to wait for the other process to initiate the connection, -1 for default, 0 to wait forever (if blocking)
80 | * @param blocking If a connection is made, do not return until the other process disconnects or dies,
81 | * @throws TimeoutException If the connection times out
82 | */
83 | public RootIPC(String packageName, IBinder ipc, int code, int connection_timeout_ms, boolean blocking) throws TimeoutException {
84 | this.packageName = packageName;
85 | userIPC = ipc;
86 | this.code = code;
87 | broadcastIPC();
88 |
89 | if (connection_timeout_ms < 0) connection_timeout_ms = 30 * 1000;
90 | if (connection_timeout_ms > 0) {
91 | synchronized (helloWaiter) {
92 | if (!haveClientsConnected()) {
93 | try {
94 | helloWaiter.wait(connection_timeout_ms);
95 | } catch (InterruptedException e) {
96 | // expected, do nothing
97 | }
98 | }
99 | if (!haveClientsConnected()) {
100 | throw new TimeoutException("librootjava: timeout waiting for IPC connection");
101 | }
102 | }
103 | }
104 |
105 | if (blocking) {
106 | // this will loop until all connections have said goodbye or their processes have died
107 | synchronized (byeWaiter) {
108 | while (!haveAllClientsDisconnected()) {
109 | try {
110 | byeWaiter.wait();
111 | } catch (InterruptedException e) {
112 | return;
113 | }
114 | }
115 | }
116 | }
117 | }
118 |
119 | /**
120 | * Do we or did we have a connection?
121 | *
122 | * @return if client has connected
123 | */
124 | public boolean haveClientsConnected() {
125 | synchronized (connections) {
126 | return connectionSeen;
127 | }
128 | }
129 |
130 | /**
131 | * Have we had connection and are they gone now?
132 | *
133 | * @return if client has exited
134 | */
135 | public boolean haveAllClientsDisconnected() {
136 | synchronized (connections) {
137 | return connectionSeen && (getConnectionCount() == 0);
138 | }
139 | }
140 |
141 | /**
142 | * Wrap the binder in an intent and broadcast it to packageName
143 | *
144 | * Uses the reflected sendBroadcast method that doesn't require us to have a context
145 | *
146 | * You may call this manually to re-broadcast the interface
147 | */
148 | public void broadcastIPC() {
149 | Intent intent = new Intent();
150 | intent.setPackage(packageName);
151 | intent.setAction(RootIPCReceiver.BROADCAST_ACTION);
152 | intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
153 |
154 | Bundle bundle = new Bundle();
155 | bundle.putBinder(RootIPCReceiver.BROADCAST_BINDER, binder);
156 | bundle.putInt(RootIPCReceiver.BROADCAST_CODE, code);
157 | intent.putExtra(RootIPCReceiver.BROADCAST_EXTRA, bundle);
158 |
159 | Reflection.sendBroadcast(intent);
160 | }
161 |
162 | /**
163 | * Get number of connected clients
164 | *
165 | * @return number of connected clients
166 | */
167 | public int getConnectionCount() {
168 | synchronized (connections) {
169 | pruneConnections();
170 | return connections.size();
171 | }
172 | }
173 |
174 | /**
175 | * Remove dead connections from our records. This should never actually have any effect due
176 | * to our DeathRecipients.
177 | */
178 | private void pruneConnections() {
179 | synchronized (connections) {
180 | if (connections.size() == 0) return;
181 |
182 | for (int i = connections.size() - 1; i >= 0; i--) {
183 | Connection conn = connections.get(i);
184 | if (!conn.getBinder().isBinderAlive()) {
185 | connections.remove(i);
186 | }
187 | }
188 |
189 | if (!connectionSeen && (connections.size() > 0)) {
190 | connectionSeen = true;
191 | synchronized (helloWaiter) {
192 | helloWaiter.notifyAll();
193 | }
194 | }
195 |
196 | if (connections.size() == 0) {
197 | synchronized (byeWaiter) {
198 | byeWaiter.notifyAll();
199 | }
200 | }
201 | }
202 | }
203 |
204 | /**
205 | * Get Connection based on IBinder
206 | * @param binder IBinder to find Connection for
207 | * @return Connection or null
208 | */
209 | private Connection getConnection(IBinder binder) {
210 | synchronized (connections) {
211 | pruneConnections();
212 | for (Connection conn : connections) {
213 | if (conn.getBinder() == binder) {
214 | return conn;
215 | }
216 | }
217 | return null;
218 | }
219 | }
220 |
221 | /**
222 | * Get Connection based on DeathRecipient
223 | * @param deathRecipient DeathRecipient to find Connection for
224 | * @return Connection or null
225 | */
226 | private Connection getConnection(IBinder.DeathRecipient deathRecipient) {
227 | synchronized (connections) {
228 | pruneConnections();
229 | for (Connection conn : connections) {
230 | if (conn.getDeathRecipient() == deathRecipient) {
231 | return conn;
232 | }
233 | }
234 | return null;
235 | }
236 | }
237 |
238 | /**
239 | * Our own wrapper around the supplied Binder interface, which allows us to keep track of
240 | * non-root process' state and connection state.
241 | */
242 | private final IBinder binder = new IRootIPC.Stub() {
243 | @Override
244 | public void hello(IBinder self) {
245 | // incoming connection from the non-root process
246 |
247 | // receive notifications when that process dies
248 | DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
249 | @Override
250 | public void binderDied() {
251 | Connection conn = getConnection(this);
252 | if (conn != null) {
253 | bye(conn.getBinder());
254 | }
255 | }
256 | };
257 | try {
258 | self.linkToDeath(deathRecipient, 0);
259 | } catch (RemoteException e) {
260 | // it's already dead!
261 | self = null;
262 | }
263 |
264 | // if still alive, record the connection
265 | if (self != null) {
266 | synchronized (connections) {
267 | connections.add(new Connection(self, deathRecipient));
268 | connectionSeen = true;
269 | }
270 | synchronized (helloWaiter) {
271 | helloWaiter.notifyAll();
272 | }
273 | }
274 | }
275 |
276 | @Override
277 | public IBinder getUserIPC() {
278 | // this is the originally supplied Binder interface
279 | return userIPC;
280 | }
281 |
282 | @Override
283 | public void bye(IBinder self) {
284 | // The non-root process is either informing us it is going away, or it already died
285 | synchronized (connections) {
286 | Connection conn = getConnection(self);
287 | if (conn != null) {
288 | try {
289 | conn.getBinder().unlinkToDeath(conn.getDeathRecipient(), 0);
290 | } catch (Exception e) {
291 | // no action required
292 | }
293 | connections.remove(conn);
294 | }
295 | }
296 | synchronized (byeWaiter) {
297 | byeWaiter.notifyAll();
298 | }
299 | }
300 | };
301 | }
302 |
--------------------------------------------------------------------------------
/librootjava_example/.gitignore:
--------------------------------------------------------------------------------
1 | #built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Windows thumbnail db
19 | Thumbs.db
20 |
21 | # OSX files
22 | .DS_Store
23 |
24 | # Android Studio
25 | *.iml
26 | .idea
27 | .gradle
28 | build/
29 | .navigation
30 | captures/
31 | output.json
32 |
33 | #NDK
34 | obj/
35 | .externalNativeBuild
36 |
--------------------------------------------------------------------------------
/librootjava_example/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2018 Jorrit Jongma
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/librootjava_example/README.md:
--------------------------------------------------------------------------------
1 | # libRootJava Example Project
2 |
3 | Please see [libRootJava](../librootjava) for license and documentation
4 |
--------------------------------------------------------------------------------
/librootjava_example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion '30.0.3'
6 |
7 | defaultConfig {
8 | applicationId "eu.chainfire.librootjava_example"
9 | minSdkVersion 21
10 | targetSdkVersion 26
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled true
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 |
27 | implementation 'com.android.support:appcompat-v7:26.1.0'
28 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
29 |
30 | implementation 'eu.chainfire:libsuperuser:1.1.1'
31 |
32 | // --- librootjava dependency ---
33 |
34 | /* Use module sources directly */
35 | //implementation project(':librootjava')
36 |
37 | /* Use local Maven repository version, installed by installMavenLocal Gradle task */
38 | //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true }
39 |
40 | /* Use jitpack version */
41 | implementation 'eu.chainfire:librootjava:1.3.3'
42 | }
43 |
--------------------------------------------------------------------------------
/librootjava_example/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/proguard-rules.pro
--------------------------------------------------------------------------------
/librootjava_example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/aidl/eu/chainfire/librootjava_example/root/IIPC.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava_example.root;
2 |
3 | import eu.chainfire.librootjava_example.root.IRemoteInputStream;
4 | import eu.chainfire.librootjava_example.root.PassedData;
5 | import eu.chainfire.librootjava_example.root.IPingCallback;
6 |
7 | interface IIPC {
8 | int getPid();
9 | List run(String command);
10 | IRemoteInputStream openFileForReading(String filename);
11 | PassedData getSomeData();
12 | Bitmap getIcon();
13 | void ping(IPingCallback pong);
14 | }
15 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/aidl/eu/chainfire/librootjava_example/root/IPingCallback.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava_example.root;
2 |
3 | interface IPingCallback {
4 | void pong(long rootMainThreadId, long rootCallThreadId);
5 | }
6 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/aidl/eu/chainfire/librootjava_example/root/IRemoteInputStream.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava_example.root;
2 |
3 | interface IRemoteInputStream {
4 | int available();
5 | int read();
6 | int readBuf(out byte[] b, int off, int len);
7 | void close();
8 | }
9 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/aidl/eu/chainfire/librootjava_example/root/PassedData.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjava_example.root;
2 |
3 | // Depending on your version of the Android SDK Build Tools, aidl compiler will complain that
4 | // it is refusing to generate code from aidl file defining parcelable. It is safe to ignore
5 | // that error.
6 |
7 | parcelable PassedData;
--------------------------------------------------------------------------------
/librootjava_example/src/main/java/eu/chainfire/librootjava_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava_example;
17 |
18 | import android.graphics.Bitmap;
19 | import android.os.RemoteException;
20 | import android.support.v7.app.AppCompatActivity;
21 | import android.os.Bundle;
22 | import android.text.method.ScrollingMovementMethod;
23 | import android.view.View;
24 | import android.widget.Button;
25 | import android.widget.ImageView;
26 | import android.widget.TextView;
27 |
28 | import java.io.BufferedReader;
29 | import java.io.IOException;
30 | import java.io.InputStream;
31 | import java.io.InputStreamReader;
32 | import java.util.List;
33 | import java.util.Locale;
34 |
35 | import eu.chainfire.librootjava.Logger;
36 | import eu.chainfire.librootjava.RootIPCReceiver;
37 | import eu.chainfire.librootjava.RootJava;
38 | import eu.chainfire.librootjava_example.root.IIPC;
39 | import eu.chainfire.librootjava_example.root.IPingCallback;
40 | import eu.chainfire.librootjava_example.root.PassedData;
41 | import eu.chainfire.librootjava_example.root.RemoteInputStream;
42 | import eu.chainfire.librootjava_example.root.RootMain;
43 | import eu.chainfire.libsuperuser.Debug;
44 | import eu.chainfire.libsuperuser.Shell;
45 |
46 | /*
47 | IMPORTANT: The code in this class is written to make it easier to understand how libRootJava
48 | works. It takes some shortcuts for the sake of brevity. Long running code (such as executing
49 | commands in a root shell) is usually better placed inside a Service.
50 |
51 | */
52 | public class MainActivity extends AppCompatActivity {
53 | static {
54 | // Application::onCreate would be a better place for this
55 |
56 | // librootjava's logger
57 | Logger.setLogTag("librootjava");
58 | Logger.setDebugLogging(BuildConfig.DEBUG);
59 |
60 | // libsuperuser's logger
61 | Debug.setDebug(BuildConfig.DEBUG);
62 | }
63 |
64 | private Button button = null;
65 | private TextView textView = null;
66 | private ImageView imageView = null;
67 | private Shell.Interactive shell = null;
68 |
69 | @Override
70 | protected void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | setContentView(R.layout.activity_main);
73 | button = findViewById(R.id.button);
74 | textView = findViewById(R.id.textView);
75 | textView.setHorizontallyScrolling(true);
76 | textView.setMovementMethod(new ScrollingMovementMethod());
77 | imageView = findViewById(R.id.imageView);
78 |
79 | /* As we declare and construct ipcReceiver in the class definition rather than constructing
80 | it inside a method (in this example), the context passed to its constructor is an empty
81 | wrapper. We need to update it to a proper context, so we may actually receive the Binder
82 | object from our root code. */
83 | ipcReceiver.setContext(this);
84 |
85 | /* Cleanup leftover files from our cache directory. This is not exactly an elegant way to
86 | do it, but it illustrates that this should be done off of the main UI thread. */
87 | (new Thread(new Runnable() {
88 | @Override
89 | public void run() {
90 | RootJava.cleanupCache(MainActivity.this);
91 | }
92 | })).start();
93 | }
94 |
95 | @Override
96 | protected void onDestroy() {
97 | /* Disconnect and release resources. If onConnect() is still running, disconnect will occur
98 | after its completion, but this call will return immediately. */
99 | ipcReceiver.release();
100 |
101 | super.onDestroy();
102 | }
103 |
104 | private void uiLog(final String msg) {
105 | // Log a string to our textView, making sure the addition runs on the UI thread
106 | runOnUiThread(new Runnable() {
107 | @Override
108 | public void run() {
109 | textView.append(msg + "\n");
110 | }
111 | });
112 | }
113 |
114 | private final RootIPCReceiver ipcReceiver = new RootIPCReceiver(this, 0) {
115 | @Override
116 | public void onConnect(IIPC ipc) {
117 | /* This is always called from a background thread, so you can do blocking operations
118 | here without issue. Keep in mind that due to this, the activity may actually be
119 | destroyed while this callback is still running.
120 |
121 | As always long-running code should be executed in a service rather than in an
122 | activity, but that is beyond the scope of this example.
123 |
124 | If release() is called from onDestroy, this will schedule a disconnect, and
125 | you can use the isDisconnectScheduled() call as a trigger to abort.
126 |
127 | If you're done with the IPC interface at the end of this method, call disconnect().
128 | You shouldn't store the interface itself, but if you don't disconnect() you can call
129 | RootIPCReceiver.getIPC() later.
130 | */
131 | Logger.dp("IPC", "onConnect");
132 | try {
133 | uiLog("");
134 | uiLog("Connected");
135 |
136 | // Get the other end's PID
137 | uiLog("");
138 | uiLog(String.format(Locale.ENGLISH, "Remote pid: %d", ipc.getPid()));
139 | uiLog("");
140 |
141 | // This file is actually readable directly from your app, but it's a nice short
142 | // text file that serves well as an example
143 | uiLog("Example InputStream:");
144 | InputStream is = RemoteInputStream.toInputStream(ipc.openFileForReading("/system/bin/am"));
145 | if (is != null) {
146 | try {
147 | BufferedReader br = new BufferedReader(new InputStreamReader(is));
148 | try {
149 | while (br.ready()) {
150 | uiLog(br.readLine());
151 | }
152 | } catch (IOException e) {
153 | uiLog(e.getMessage());
154 | Logger.ex(e);
155 | }
156 | } finally {
157 | try {
158 | is.close();
159 | } catch (IOException e) {
160 | // no action required
161 | }
162 | }
163 | }
164 | uiLog("");
165 |
166 | // Receive an automatically reconstructed PassedObject. This is a copy of the
167 | // object on the other end, so changing it here does not change it there.
168 | PassedData pd = ipc.getSomeData();
169 | uiLog(String.format(Locale.ENGLISH, "getSomeData(): %d %d %s", pd.getA(), pd.getB(), pd.getC()));
170 |
171 | // Run a command on the root end and get the output back
172 | List output = ipc.run("ls -l /init");
173 | if (output != null) {
174 | for (String line : output) {
175 | // should show the same output as exampleWork1 and exampleWork2
176 | uiLog("exampleWorkX: " + line);
177 | }
178 | }
179 |
180 | // Get our icon through root
181 | final Bitmap bitmap = ipc.getIcon();
182 | runOnUiThread(new Runnable() {
183 | @Override
184 | public void run() {
185 | imageView.setImageBitmap(bitmap);
186 | }
187 | });
188 |
189 | // Ping-pong, get some thread info through a callback
190 | final long ipcThreadId = Thread.currentThread().getId();
191 | runOnUiThread(new Runnable() {
192 | @Override
193 | public void run() {
194 | uiLog(String.format(Locale.ENGLISH, "Ping: thisUI[%d] thisIPC[%d]", Thread.currentThread().getId(), ipcThreadId));
195 | }
196 | });
197 | // Note that we implement IPingCallback.Stub rather than IPingCallback
198 | ipc.ping(new IPingCallback.Stub() {
199 | /* The pong callback may be executed on the same thread as the ping call is
200 | made, but only when the root end calls the callback while the ping call is
201 | still blocking. It is best to assume it will run on a different thread and
202 | guard variable and method access accordingly.
203 |
204 | In this example you are likely to see thisCallback[%d] returning the same
205 | value for as thisIPC[%d] the first pong, and a different value the second
206 | pong. */
207 | @Override
208 | public void pong(long rootMainThreadId, long rootCallThreadId) {
209 | uiLog(String.format(Locale.ENGLISH, "Pong: rootMain[%d] rootCall[%d] thisCallback[%d]", rootMainThreadId, rootCallThreadId, Thread.currentThread().getId()));
210 | }
211 | });
212 |
213 | try {
214 | // give the root end some time to send pong replies
215 | Thread.sleep(1000);
216 | } catch (InterruptedException e) {
217 | // no action required
218 | }
219 |
220 | // Our work here is done
221 | disconnect();
222 | } catch (RemoteException e) {
223 | uiLog("RemoteException during IPC. Connection lost?");
224 | Logger.ex(e);
225 | }
226 | }
227 |
228 | @Override
229 | public void onDisconnect(IIPC ipc) {
230 | // May or may not be called from a background thread
231 | uiLog("");
232 | uiLog("Disconnected");
233 | Logger.dp("IPC", "onDisconnect");
234 | }
235 | };
236 |
237 | public void onRunClick(View v) {
238 | button.setEnabled(false);
239 |
240 | uiLog("Executing script:");
241 | List script = RootMain.getLaunchScript(this, null, null);
242 | for (String line : script) {
243 | Logger.d("%s", line);
244 | uiLog(line);
245 | }
246 | uiLog("");
247 |
248 | // Open a root shell if we don't have one yet
249 | if ((shell == null) || !shell.isRunning()) {
250 | shell = (new Shell.Builder())
251 | .useSU()
252 | .open(new Shell.OnCommandResultListener() {
253 | @Override
254 | public void onCommandResult(int commandCode, int exitCode, List output) {
255 | if (exitCode != SHELL_RUNNING) {
256 | // we couldn't open the shell, enable the button
257 | runOnUiThread(new Runnable() {
258 | @Override
259 | public void run() {
260 | button.setEnabled(true);
261 | }
262 | });
263 | }
264 | }
265 | });
266 | }
267 |
268 | // Execute the script (asynchronously)
269 | shell.addCommand(script, 0, new Shell.OnCommandLineListener() {
270 | @Override
271 | public void onCommandResult(int commandCode, int exitCode) {
272 | // execution finished, enable the button
273 | runOnUiThread(new Runnable() {
274 | @Override
275 | public void run() {
276 | button.setEnabled(true);
277 | }
278 | });
279 | }
280 |
281 | @Override
282 | public void onLine(String line) {
283 | // we receive the output of exampleWork1/2 here
284 | uiLog(line);
285 | }
286 | });
287 |
288 | /*
289 | If this method was not running on the main thread, and you wanted to use the IPC class
290 | serially rather than using the onConnect callback, you could do it like this:
291 |
292 | IIPC ipc = ipcReceiver.getIPC(30 * 1000);
293 | if (ipc != null) {
294 | int remotePid = ipc.getPid();
295 | // ...
296 | ipc.disconnect();
297 | }
298 |
299 | */
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/java/eu/chainfire/librootjava_example/root/PassedData.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava_example.root;
17 |
18 | import android.os.Parcel;
19 | import android.os.Parcelable;
20 |
21 | public class PassedData implements Parcelable {
22 | // These are called by the framework to deserialize a Binder transaction into objects
23 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
24 | public PassedData createFromParcel(Parcel in) {
25 | return new PassedData(in);
26 | }
27 |
28 | public PassedData[] newArray(int size) {
29 | return new PassedData[size];
30 | }
31 | };
32 |
33 | // Constructor called by CREATOR, our custom deserializer
34 | private PassedData(Parcel p) {
35 | this.a = p.readInt();
36 | this.b = p.readLong();
37 | this.c = p.readString();
38 | }
39 |
40 | // Our custom serializer
41 | @Override
42 | public void writeToParcel(Parcel dest, int flags) {
43 | dest.writeInt(a);
44 | dest.writeLong(b);
45 | dest.writeString(c);
46 | }
47 |
48 | @Override
49 | public int describeContents() {
50 | // return Parcelable.CONTENTS_FILE_DESCRIPTOR here if you have FileDescriptor fields
51 | return 0;
52 | }
53 |
54 | // Data class members as usual
55 |
56 | private final int a;
57 | private final long b;
58 | private final String c;
59 |
60 | public PassedData(int a, long b, String c) {
61 | this.a = a;
62 | this.b = b;
63 | this.c = c;
64 | }
65 |
66 | public int getA() {
67 | return a;
68 | }
69 |
70 | public long getB() {
71 | return b;
72 | }
73 |
74 | public String getC() {
75 | return c;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/java/eu/chainfire/librootjava_example/root/RemoteInputStream.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjava_example.root;
17 |
18 | import android.os.RemoteException;
19 | import android.support.annotation.NonNull;
20 |
21 | import java.io.BufferedInputStream;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 |
25 | import eu.chainfire.librootjava.Logger;
26 |
27 | /**
28 | * Example helper class to pass InputStreams through Binder
29 | *
30 | * InputStream is a pretty basic class that is easily wrapped, because it really only requires
31 | * a handful of base methods.
32 | *
33 | * We cannot make an InputStream into a Binder-passable interface directly, because its definitions
34 | * includes throwing IOExceptions. It also defines multiple methods with the same name and a
35 | * different parameter list, which is not supported in aidl.
36 | *
37 | * You should never throw an exception in your Binder interface. We catch the exceptions and
38 | * return -2 instead, because conveniently all the methods we override should return values >= -1.
39 | * More complex classes would require more complex solutions.
40 | */
41 | @SuppressWarnings("WeakerAccess")
42 | public class RemoteInputStream {
43 | /**
44 | * Wraps an InputStream in an IRemoteInputStream
45 | *
46 | * Use this on the root side
47 | *
48 | * @param is InputStream
49 | * @return IRemoteInputStream
50 | */
51 | public static IRemoteInputStream.Stub fromInputStream(final InputStream is) {
52 | return new IRemoteInputStream.Stub() {
53 | // these methods are declared in IRemoteInputStream.aidl
54 |
55 | @Override
56 | public int available() {
57 | try {
58 | return is.available();
59 | } catch (IOException e) {
60 | Logger.ex(e);
61 | return -2;
62 | }
63 | }
64 |
65 | @Override
66 | public int read() {
67 | try {
68 | return is.read();
69 | } catch (IOException e) {
70 | Logger.ex(e);
71 | return -2;
72 | }
73 | }
74 |
75 | @Override
76 | public int readBuf(byte[] b, int off, int len) {
77 | try {
78 | return is.read(b, off, len);
79 | } catch (IOException e) {
80 | Logger.ex(e);
81 | return -2;
82 | }
83 | }
84 |
85 | @Override
86 | public void close() {
87 | try {
88 | is.close();
89 | } catch (IOException e) {
90 | // no action required
91 | }
92 | }
93 | };
94 | }
95 |
96 | /**
97 | * Wraps an IRemoteInputStream in an InputStream
98 | *
99 | * We throw an IOException if we receive a -2 result, because we know we caught one on the
100 | * other end in that case. The logcat output will obviously not show the real reason for the
101 | * exception.
102 | *
103 | * We also wrap the InputStream we create inside a BufferedInputStream, to potentially reduce
104 | * the number of calls made. We increase the buffer size to 64kb in case this is ever used
105 | * to actually read large files, which is quite a bit faster than the default 8kb.
106 | *
107 | * Use this on the non-root side.
108 | *
109 | * @param ris IRemoteInputStream received through Binder
110 | * @return InputStream
111 | */
112 | public static InputStream toInputStream(final IRemoteInputStream ris) {
113 | if (ris == null) return null;
114 | return new BufferedInputStream(new InputStream() {
115 | private int throwIO(int r) throws IOException {
116 | if (r == -2) throw new IOException("Remote Exception");
117 | return r;
118 | }
119 |
120 | @Override
121 | public int available() throws IOException {
122 | // Basic InputStream works without overriding this method, but many methods that
123 | // take InputStreams depend on this method returning non-0 (the base method
124 | // always returns 0)
125 | try {
126 | return throwIO(ris.available());
127 | } catch (RemoteException e) {
128 | throw new IOException("Remote Exception");
129 | }
130 | }
131 |
132 | @Override
133 | public int read() throws IOException {
134 | // This is the only method you *really* need to override
135 | try {
136 | return throwIO(ris.read());
137 | } catch (RemoteException e) {
138 | throw new IOException("Remote Exception");
139 | }
140 | }
141 |
142 | @Override
143 | public int read(@NonNull byte[] b) throws IOException {
144 | // Overriding this one too will make reading much faster than just having read()
145 | return read(b, 0, b.length);
146 | }
147 |
148 | @Override
149 | public int read(@NonNull byte[] b, int off, int len) throws IOException {
150 | // Overriding this one too will make reading much faster than just having read()
151 | try {
152 | return throwIO(ris.readBuf(b, off, len));
153 | } catch (RemoteException e) {
154 | throw new IOException("Remote Exception");
155 | }
156 | }
157 |
158 | @Override
159 | public void close() throws IOException {
160 | // This method too is an optional override, but we wouldn't want to leave our
161 | // files open, would we?
162 | try {
163 | ris.close();
164 | } catch (RemoteException e) {
165 | throw new IOException("Remote Exception");
166 | }
167 | }
168 | }, 64 * 1024);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
37 |
38 |
46 |
47 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjava_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | libRootJava Example
3 |
4 |
--------------------------------------------------------------------------------
/librootjava_example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/librootjavadaemon/.gitignore:
--------------------------------------------------------------------------------
1 | #built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Windows thumbnail db
19 | Thumbs.db
20 |
21 | # OSX files
22 | .DS_Store
23 |
24 | # Android Studio
25 | *.iml
26 | .idea
27 | .gradle
28 | build/
29 | .navigation
30 | captures/
31 | output.json
32 |
33 | #NDK
34 | obj/
35 | .externalNativeBuild
36 |
--------------------------------------------------------------------------------
/librootjavadaemon/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 |
4 | add_executable( libdaemonize.so src/main/jni/daemonize.cpp )
5 |
6 | target_link_libraries( libdaemonize.so log )
7 |
8 | #target_compile_definitions( libdaemonize.so PRIVATE DEBUG )
9 |
--------------------------------------------------------------------------------
/librootjavadaemon/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2018 Jorrit Jongma
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/librootjavadaemon/README.md:
--------------------------------------------------------------------------------
1 | # libRootJavaDaemon
2 |
3 | [](https://jitpack.io/#eu.chainfire/librootjava)
4 |
5 | Add-on for [libRootJava](../librootjava) to run the root process as a
6 | daemon.
7 |
8 | ## License
9 |
10 | Copyright © 2018 Jorrit *Chainfire* Jongma
11 |
12 | This code is released under the [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
13 |
14 | If you use this library (or code inspired by it) in your projects,
15 | crediting me is appreciated.
16 |
17 | If you modify the library itself when you use it in your projects,
18 | you are kindly requested to share the sources of those modifications.
19 |
20 | ## Spaghetti Sauce Project
21 |
22 | This library is part of the [Spaghetti Sauce Project](https://github.com/Chainfire/spaghetti_sauce_project).
23 |
24 | ## About
25 |
26 | For some use-cases you may want to run your root process as a daemon,
27 | so it can keep running independently of the state of the non-root
28 | process.
29 |
30 | This library adds that functionality to [libRootJava](../librootjava).
31 |
32 | By adding a couple of lines to your [libRootJava](../librootjava) code,
33 | this library will take care of starting the root process in daemon mode,
34 | replacing older versions of the daemon already running, or triggering
35 | an already running instance of the daemon of the same version to
36 | re-broadcast its IPC interfaces (if any).
37 |
38 | Please consider if your project really needs this functionality before
39 | using this library, as it will be using additional device resources.
40 |
41 | ## Recommended reading
42 |
43 | You should already be familiar with the workings of
44 | [libRootJava](../librootjava). If not, this is the time to read up.
45 |
46 | I strongly recommend you read the library's source code in its entirety
47 | before using it extensively, as you should understand the basics of
48 | what is going on behind the scenes. It is liberally commented.
49 |
50 | I also strongly recommend not just running, but actually reading the
51 | [example project](../librootjavadaemon_example)'s code.
52 |
53 | ## Getting started
54 |
55 | In the class serving as your entry-point for the code running as root,
56 | add a call to ```RootDaemon.daemonize()``` as one of the first
57 | statements (generally after setting up logging):
58 |
59 | ```
60 | public static void main(String[] args) {
61 | // setup Logger
62 |
63 | // If a daemon of the same version is already running, this
64 | // call will trigger process termination, and will thus
65 | // never return.
66 | RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0, false, null);
67 |
68 | // ...
69 |
70 | RootJava.restoreOriginalLdLibraryPath();
71 |
72 | // ...
73 | ```
74 |
75 | Then, instead of calling ```new RootIPC()``` for each of your
76 | implemented IPC classes, call ```RootDaemon.register()```, closing
77 | with ```RootDaemon.run()```:
78 |
79 | ```
80 | // ...
81 |
82 | IBinder ipc = new IMyIPC.Stub() {
83 | // ...
84 |
85 | @Override
86 | public void terminate() {
87 | RootDaemon.exit();
88 | }
89 | }
90 |
91 | RootDaemon.register(BuildConfig.APPLICATION_ID, ipc, 0);
92 |
93 | // ...
94 |
95 | RootDaemon.run();
96 | }
97 | ```
98 |
99 | On the non-root side, simply replace your calls to
100 | ```RootJava.getLaunchScript()``` with calls to
101 | ```RootDaemon.getLaunchScript()```. Everything will work the same
102 | as with normal use of [libRootJava](../librootjava), aside from that
103 | the root process isn't tied to the lifecycle of the non-root end,
104 | and you might want to explicitly tell the daemon to terminate at
105 | some point (or not).
106 |
107 | See the [example project](../librootjavadaemon_example) for further
108 | details.
109 |
110 | You can of course use this library without Binder-based IPC, in which
111 | case you would skip the ```RootDaemon.register()``` and
112 | ```RootDaemon.run()``` calls in the code running as root, and replace
113 | them with your own handling.
114 |
115 | #### Termination
116 |
117 | This daemon process will only terminate when explicitly told to do so,
118 | either through IPC, a Linux kill signal, if an unhandled
119 | exception occurs, or (if so configured) when the Android framework
120 | dies. This is why in the example above we add a
121 | ```terminate()``` method to our IPC interface which calls
122 | ```RootDaemon.exit()```. This way you can tell the daemon to
123 | stop running from your non-root app through IPC.
124 |
125 | Note that this method will always trigger a ```RemoteException``` on the
126 | non-root end when called through IPC.
127 |
128 | See the [example project](../librootjavadaemon_example) for further
129 | details.
130 |
131 | #### Cleanup
132 |
133 | As with running code as root in normal (non-daemon) mode, files may need
134 | to be created in our app's cache directory. The chances of leftover
135 | files are actually higher in daemon mode, and the number of files is
136 | higher too.
137 |
138 | To clean up, call ```RootDaemon.cleanupCache()``` instead of
139 | ```RootJava.cleanupCache()```. It is *not* needed to call both.
140 |
141 | ## abiFilters
142 |
143 | This library includes native code for all platforms the NDK supports.
144 | If your APK does not support all of these platforms, you need to use
145 | *abiFilters* in your Gradle script to filter the unwanted libraries
146 | out. Failure to do this may lead the Play Store to think your APK is
147 | compatible with all the platforms this library supports, rather than
148 | the ones you intended to support.
149 |
150 | See [Specify ABIs](https://developer.android.com/studio/projects/gradle-external-native-builds#specify-abi)
151 | on the Android site.
152 |
153 | ## Gradle
154 |
155 | Root `build.gradle`:
156 |
157 | ```
158 | allprojects {
159 | repositories {
160 | ...
161 | maven { url 'https://jitpack.io' }
162 | }
163 | }
164 | ```
165 |
166 | Module `build.gradle`:
167 |
168 | ```
169 | dependencies {
170 | implementation 'eu.chainfire.librootjava:librootjavadaemon:1.3.3'
171 | }
172 | ```
173 |
174 | You should update to the latest libRootJava and libRootJavaDaemon at the
175 | same time.
176 |
--------------------------------------------------------------------------------
/librootjavadaemon/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 | android {
5 | compileSdkVersion 30
6 | buildToolsVersion '30.0.3'
7 | defaultConfig {
8 | minSdkVersion 21
9 | targetSdkVersion 26
10 | consumerProguardFiles 'proguard.txt'
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | }
16 | }
17 | externalNativeBuild {
18 | cmake {
19 | path "CMakeLists.txt"
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | /* Use module sources directly */
26 | //implementation project(':librootjava')
27 |
28 | /* Use local Maven repository version, installed by installMavenLocal Gradle task */
29 | //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true }
30 |
31 | /* Use jitpack version */
32 | implementation 'eu.chainfire.librootjava:librootjava:1.3.3'
33 | }
34 |
35 | task sourcesJar(type: Jar) {
36 | classifier = 'sources'
37 | from android.sourceSets.main.java.srcDirs
38 | }
39 |
40 | task javadoc(type: Javadoc) {
41 | source = android.sourceSets.main.java.srcDirs
42 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
43 | }
44 |
45 | task javadocJar(type: Jar, dependsOn: javadoc) {
46 | classifier = 'javadoc'
47 | from javadoc.destinationDir
48 | }
49 |
50 | artifacts {
51 | archives javadocJar
52 | archives sourcesJar
53 | }
54 |
55 | ext {
56 | libraryName = 'libRootJavaDaemon'
57 | libraryDescription = 'Add-on for libRootJava to run the root process as a daemon'
58 |
59 | publishedGroupId = 'eu.chainfire'
60 | artifact = 'librootjavadaemon'
61 |
62 | siteUrl = 'https://github.com/Chainfire/librootjava'
63 | gitUrl = 'https://github.com/Chainfire/librootjava.git'
64 | issueTrackerUrl = 'https://github.com/Chainfire/librootjava/issues'
65 |
66 | libraryVersion = '1.3.3'
67 |
68 | developerId = 'Chainfire'
69 | developerName = 'Jorrit Jongma'
70 | developerEmail = 'chainfire@chainfire.eu'
71 |
72 | licenseName = 'The Apache Software License, Version 2.0'
73 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
74 | allLicenses = ["Apache-2.0"]
75 | }
76 |
77 | task installMavenLocal(type: Upload) {
78 | repositories.mavenInstaller {
79 | configuration = configurations['archives']
80 | pom.project {
81 | packaging 'aar'
82 | groupId = publishedGroupId
83 | artifactId = artifact
84 | version = libraryVersion + '-SNAPSHOT'
85 | }
86 | }
87 | }
88 |
89 | version = libraryVersion
90 | group = publishedGroupId
91 |
92 | install {
93 | repositories.mavenInstaller {
94 | pom.project {
95 | packaging 'aar'
96 | groupId = publishedGroupId
97 | artifactId = artifact
98 | name libraryName
99 | version = libraryVersion
100 | url siteUrl
101 | licenses {
102 | license {
103 | name licenseName
104 | url licenseUrl
105 | }
106 | }
107 | developers {
108 | developer {
109 | id developerId
110 | name developerName
111 | email developerEmail
112 | }
113 | }
114 | scm {
115 | connection gitUrl
116 | developerConnection gitUrl
117 | url gitUrl
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/librootjavadaemon/proguard.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon/proguard.txt
--------------------------------------------------------------------------------
/librootjavadaemon/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/librootjavadaemon/src/main/aidl/eu/chainfire/librootjavadaemon/IRootDaemonIPC.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjavadaemon;
2 |
3 | // Used internally by RootDaemon
4 |
5 | interface IRootDaemonIPC {
6 | String getVersion();
7 | void terminate();
8 | void broadcast();
9 | }
10 |
--------------------------------------------------------------------------------
/librootjavadaemon/src/main/jni/daemonize.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #ifdef DEBUG
28 | #include
29 | #define LOG(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libdaemonize", __VA_ARGS__))
30 | #else
31 | #define LOG(...) ((void)0)
32 | #endif
33 |
34 | int sleep_ms(int ms) {
35 | struct timespec ts;
36 | ts.tv_sec = ms / 1000;
37 | ts.tv_nsec = (ms % 1000) * 1000000;
38 | if ((nanosleep(&ts,&ts) == -1) && (errno == EINTR)) {
39 | int ret = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
40 | if (ret < 1) ret = 1;
41 | return ret;
42 | }
43 | return 0;
44 | }
45 |
46 | /* Proper daemonization includes forking, closing the current STDIN/STDOUT/STDERR, creating a new
47 | * session, and forking again, making sure the twice-forked process becomes a child of init (1) */
48 | static int fork_daemon(int returnParent) {
49 | pid_t child = fork();
50 | if (child == 0) { // 1st child
51 | close(STDIN_FILENO);
52 | close(STDOUT_FILENO);
53 | close(STDERR_FILENO);
54 |
55 | int devNull = open("/dev/null", O_RDWR);
56 | dup2(devNull, STDIN_FILENO);
57 | dup2(devNull, STDOUT_FILENO);
58 | dup2(devNull, STDERR_FILENO);
59 | close(devNull);
60 |
61 | setsid();
62 | pid_t child2 = fork();
63 | if (child2 == 0) { // 2nd child
64 | return 0; // return execution to caller
65 | } else if (child2 > 0) { // 1st child, fork ok
66 | exit(EXIT_SUCCESS);
67 | } else if (child2 < 0) { // 1st child, fork fail
68 | LOG("2nd fork failed (%d)", errno);
69 | exit(EXIT_FAILURE);
70 | }
71 | }
72 |
73 | // parent
74 | if (child < 0) {
75 | LOG("1st fork failed (%d)", errno);
76 | return -1; // error on 1st fork
77 | }
78 | while (true) {
79 | int status;
80 | pid_t waited = waitpid(child, &status, 0);
81 | if ((waited == child) && WIFEXITED(status)) {
82 | break;
83 | }
84 | }
85 | if (!returnParent) exit(EXIT_SUCCESS);
86 | return 1; // success parent
87 | }
88 |
89 | extern "C" {
90 |
91 | int main(int argc, char *argv[], char** envp) {
92 | if (fork_daemon(0) == 0) { // daemonized child
93 | // On some devices in the early boot stages, execv will fail with EACCESS, cause unknown.
94 | // Retrying a couple of times seems to work. Most-seen required attempts is three.
95 | // That retrying works implies some sort of race-condition, possibly SELinux related.
96 | for (int i = 0; i < 16; i++) {
97 | execv(argv[1], &argv[1]); // never returns if successful
98 | LOG("[%d] execv(%s, ...)-->%d", i, argv[1], errno);
99 | sleep_ms(16);
100 | }
101 | LOG("too many failures, aborting");
102 | exit(EXIT_FAILURE);
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/.gitignore:
--------------------------------------------------------------------------------
1 | #built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Windows thumbnail db
19 | Thumbs.db
20 |
21 | # OSX files
22 | .DS_Store
23 |
24 | # Android Studio
25 | *.iml
26 | .idea
27 | .gradle
28 | build/
29 | .navigation
30 | captures/
31 | output.json
32 |
33 | #NDK
34 | obj/
35 | .externalNativeBuild
36 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2018 Jorrit Jongma
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/librootjavadaemon_example/README.md:
--------------------------------------------------------------------------------
1 | # libRootJavaDaemon Example Project
2 |
3 | Please see [libRootJavaDaemon](../librootjavadaemon) for license and documentation
4 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion '30.0.3'
6 |
7 | defaultConfig {
8 | applicationId "eu.chainfire.librootjavadaemon_example"
9 | minSdkVersion 21
10 | targetSdkVersion 26
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled true
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 |
27 | implementation 'com.android.support:appcompat-v7:26.1.0'
28 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
29 |
30 | implementation 'eu.chainfire:libsuperuser:1.1.1'
31 |
32 | // --- librootjava and librootjavadaemon dependencies ---
33 |
34 | /* Use module sources directly */
35 | //implementation project(':librootjava')
36 | //implementation project(':librootjavadaemon')
37 |
38 | /* Use local Maven repository version, installed by installMavenLocal Gradle task */
39 | //implementation('eu.chainfire:librootjava:1.3.3-SNAPSHOT') { changing = true }
40 | //implementation('eu.chainfire:librootjavadaemon:1.3.3-SNAPSHOT') { changing = true }
41 |
42 | /* Use jitpack version */
43 | implementation 'eu.chainfire:librootjava:1.3.3'
44 | implementation 'eu.chainfire.librootjava:librootjavadaemon:1.3.3'
45 | }
46 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/proguard-rules.pro
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/aidl/eu/chainfire/librootjavadaemon_example/root/IIPC.aidl:
--------------------------------------------------------------------------------
1 | package eu.chainfire.librootjavadaemon_example.root;
2 |
3 | interface IIPC {
4 | int getPid();
5 | int getLaunchedByPid();
6 | void terminate();
7 | }
8 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjavadaemon_example;
17 |
18 | import android.os.RemoteException;
19 | import android.support.v7.app.AppCompatActivity;
20 | import android.os.Bundle;
21 | import android.text.method.ScrollingMovementMethod;
22 | import android.view.View;
23 | import android.widget.Button;
24 | import android.widget.TextView;
25 |
26 | import java.util.List;
27 | import java.util.Locale;
28 |
29 | import eu.chainfire.librootjava.Logger;
30 | import eu.chainfire.librootjava.RootIPCReceiver;
31 | import eu.chainfire.librootjavadaemon.RootDaemon;
32 | import eu.chainfire.librootjavadaemon_example.root.IIPC;
33 | import eu.chainfire.librootjavadaemon_example.root.RootMain;
34 | import eu.chainfire.libsuperuser.Debug;
35 | import eu.chainfire.libsuperuser.Shell;
36 |
37 | /*
38 | IMPORTANT: The code in this class is written to make it easier to understand how
39 | libRootJavaDaemon works. It takes some shortcuts for the sake of brevity. Long running code
40 | (such as executing commands in a root shell) is usually better placed inside a Service.
41 |
42 | You should read librootjava_example first, as it's comments provide basic information
43 | this example builds on.
44 |
45 | */
46 | public class MainActivity extends AppCompatActivity {
47 | static {
48 | // librootjava's logger
49 | Logger.setLogTag("librootjavadaemon");
50 | Logger.setDebugLogging(BuildConfig.DEBUG);
51 |
52 | // libsuperuser's logger
53 | Debug.setDebug(BuildConfig.DEBUG);
54 | }
55 |
56 | private Button buttonLaunchDaemon = null;
57 | private Button buttonKillDaemon = null;
58 | private Button buttonKillUI = null;
59 | private TextView textView = null;
60 | private Shell.Interactive shell = null;
61 |
62 | @Override
63 | protected void onCreate(Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | setContentView(R.layout.activity_main);
66 | buttonLaunchDaemon = findViewById(R.id.buttonLaunchDaemon);
67 | buttonKillDaemon = findViewById(R.id.buttonKillDaemon);
68 | buttonKillDaemon.setEnabled(false);
69 | buttonKillUI = findViewById(R.id.buttonKillUI);
70 | textView = findViewById(R.id.textView);
71 | textView.setHorizontallyScrolling(true);
72 | textView.setMovementMethod(new ScrollingMovementMethod());
73 |
74 | // See librootjava's example for further commentary on these two calls
75 | ipcReceiver.setContext(this);
76 | (new Thread(new Runnable() {
77 | @Override
78 | public void run() {
79 | RootDaemon.cleanupCache(MainActivity.this);
80 | }
81 | })).start();
82 | }
83 |
84 | @Override
85 | protected void onDestroy() {
86 | ipcReceiver.release();
87 | super.onDestroy();
88 | }
89 |
90 | private void uiLog(final String msg) {
91 | runOnUiThread(new Runnable() {
92 | @Override
93 | public void run() {
94 | textView.append(msg + "\n");
95 | }
96 | });
97 | }
98 |
99 | private final RootIPCReceiver ipcReceiver = new RootIPCReceiver(this, 0) {
100 | @Override
101 | public void onConnect(IIPC ipc) {
102 | Logger.dp("IPC", "onConnect");
103 | uiLog("Connected to daemon");
104 |
105 | runOnUiThread(new Runnable() {
106 | @Override
107 | public void run() {
108 | buttonKillDaemon.setEnabled(true);
109 | }
110 | });
111 |
112 | uiLog(String.format(Locale.ENGLISH, "Our pid: %d", android.os.Process.myPid()));
113 | try {
114 | uiLog(String.format(Locale.ENGLISH, "Daemon pid: %d", ipc.getPid()));
115 | uiLog(String.format(Locale.ENGLISH, "Daemon launched by pid: %d", ipc.getLaunchedByPid()));
116 |
117 | if (ipc.getLaunchedByPid() == android.os.Process.myPid()) {
118 | uiLog("That is this process!");
119 | } else {
120 | uiLog("That was another process!");
121 | }
122 |
123 | // we don't call disconnect() here because then the Kill Daemon button wouldn't work
124 | } catch (RemoteException e) {
125 | uiLog("RemoteException during IPC. Connection lost?");
126 | Logger.ex(e);
127 | }
128 | }
129 |
130 | @Override
131 | public void onDisconnect(IIPC ipc) {
132 | uiLog("Disconnected from daemon");
133 | uiLog("");
134 | Logger.dp("IPC", "onDisconnect");
135 | }
136 | };
137 |
138 | public void onLaunchDaemonClick(View v) {
139 | buttonLaunchDaemon.setEnabled(false);
140 |
141 | // Get daemon launch script
142 | List script = RootMain.getLaunchScript(this);
143 |
144 | // Open a root shell if we don't have one yet
145 | if ((shell == null) || !shell.isRunning()) {
146 | shell = (new Shell.Builder())
147 | .useSU()
148 | .open(new Shell.OnCommandResultListener() {
149 | @Override
150 | public void onCommandResult(int commandCode, int exitCode, List output) {
151 | if (exitCode != SHELL_RUNNING) {
152 | // we couldn't open the shell, enable the button
153 | runOnUiThread(new Runnable() {
154 | @Override
155 | public void run() {
156 | buttonLaunchDaemon.setEnabled(true);
157 | }
158 | });
159 | }
160 | }
161 | });
162 | }
163 |
164 | // Execute the script (asynchronously)
165 | shell.addCommand(script, 0, new Shell.OnCommandLineListener() {
166 | @Override
167 | public void onCommandResult(int commandCode, int exitCode) {
168 | // execution finished, enable the button
169 | runOnUiThread(new Runnable() {
170 | @Override
171 | public void run() {
172 | buttonLaunchDaemon.setEnabled(true);
173 | }
174 | });
175 | }
176 |
177 | @Override
178 | public void onLine(String line) {
179 | }
180 | });
181 | }
182 |
183 | public void onKillDaemonClick(View v) {
184 | uiLog("Terminating daemon...");
185 | IIPC ipc = ipcReceiver.getIPC();
186 | if (ipc != null) {
187 | try {
188 | // Tell the other end to terminate
189 | ipc.terminate();
190 |
191 | // If no RemoteException was thrown, the daemon wasn't killed
192 | uiLog("Terminating daemon failed");
193 | } catch (RemoteException e) {
194 | // As the daemon process dies, the Binder link dies, and a RemoteException thrown
195 | uiLog("Daemon terminated");
196 |
197 | buttonKillDaemon.setEnabled(false);
198 | }
199 | } else {
200 | uiLog("Not connected to daemon");
201 | }
202 | }
203 |
204 | public void onKillUIClick(View v) {
205 | // Make sure our process dies and a new process is created when you launch this app
206 | // again. This is to demonstrate the daemon stays alive regardless of the state of our
207 | // UI process.
208 | System.exit(0);
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/java/eu/chainfire/librootjavadaemon_example/root/RootMain.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2018 Jorrit 'Chainfire' Jongma
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package eu.chainfire.librootjavadaemon_example.root;
17 |
18 | import android.content.Context;
19 | import android.os.IBinder;
20 |
21 | import java.util.List;
22 |
23 | import eu.chainfire.librootjava.Logger;
24 | import eu.chainfire.librootjava.RootJava;
25 | import eu.chainfire.librootjavadaemon.RootDaemon;
26 | import eu.chainfire.librootjavadaemon_example.BuildConfig;
27 | import eu.chainfire.libsuperuser.Debug;
28 |
29 | /**
30 | * This class' main method will be launched as root. You can access any other class from your
31 | * package, but not instances - this is a separate process from the UI.
32 | */
33 | public class RootMain {
34 | /**
35 | * Call this from non-root code to generate the script to launch the root code as a daemon
36 | *
37 | * @param context Application or activity context
38 | * @return Script
39 | */
40 | public static List getLaunchScript(Context context) {
41 | // We pass the daemon our PID so we know which process started it
42 | String[] params = new String[] {
43 | String.valueOf(android.os.Process.myPid())
44 | };
45 |
46 | // We call RootDaemon's getLaunchScript() rather than RootJava's
47 | return RootDaemon.getLaunchScript(context, RootMain.class, null, null, params, context.getPackageName() + ":root");
48 | }
49 |
50 | /**
51 | * Entry point into code running as root
52 | *
53 | * @param args Passed arguments
54 | */
55 | public static void main(String[] args) {
56 | // Setup logging - note that these logs do show up in (adb) logcat, but they do not show up
57 | // in AndroidStudio where the logs from the non-root part of your app are displayed!
58 | Logger.setLogTag("librootjava:root");
59 | Logger.setDebugLogging(BuildConfig.DEBUG);
60 |
61 | // Setup libsuperuser (required for this example code, but not required to use librootjava in general)
62 | Debug.setDebug(BuildConfig.DEBUG);
63 | Debug.setLogTypeEnabled(Debug.LOG_GENERAL | Debug.LOG_COMMAND, true);
64 | Debug.setLogTypeEnabled(Debug.LOG_OUTPUT, true);
65 | Debug.setSanityChecksEnabled(false); // don't complain about calls on the main thread
66 |
67 | // Log uncaught exceptions rather than just sending them to stderr
68 | final Thread.UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();
69 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
70 | @Override
71 | public void uncaughtException(Thread thread, Throwable throwable) {
72 | Logger.dp("EXCEPTION", "%s", throwable.getClass().getName());
73 | if (oldHandler != null) {
74 | oldHandler.uncaughtException(thread, throwable);
75 | } else {
76 | System.exit(1);
77 | }
78 | }
79 | });
80 |
81 | // Create instance and pass control over to run(), so we don't have to static everything
82 | new RootMain().run(args);
83 | }
84 |
85 | private volatile int launchedBy;
86 |
87 | /**
88 | * All your code here. Execution ends when this method returns.
89 | *
90 | * @param args Passed arguments
91 | */
92 | private void run(String[] args) {
93 | // Become the daemon
94 | RootDaemon.daemonize(BuildConfig.APPLICATION_ID, 0, false, null);
95 |
96 | // Restore original LD_LIBRARY_PATH
97 | RootJava.restoreOriginalLdLibraryPath();
98 |
99 | // Interpret prepended arguments
100 | if ((args == null) || (args.length == 0)) {
101 | // We expect at least one argument, PID of who started the daemon
102 | return;
103 | }
104 |
105 | try {
106 | launchedBy = Integer.parseInt(args[0]);
107 | } catch (NumberFormatException e) {
108 | // we expected an int
109 | return;
110 | }
111 |
112 | Logger.d("START");
113 |
114 | // Implement our IPC class
115 | IBinder ipc = new IIPC.Stub() {
116 | @Override
117 | public int getPid() {
118 | // Our Pid
119 | return android.os.Process.myPid();
120 | }
121 |
122 | @Override
123 | public int getLaunchedByPid() {
124 | // Pid of the process that launched the daemon
125 | return launchedBy;
126 | }
127 |
128 | @Override
129 | public void terminate() {
130 | // In daemon mode, this root process keeps running until you manually stop it,
131 | // an unhandled exception occurs, or Linux kills it for some reason.
132 | // Note that this will always throw a RemoteException on the other end!
133 | RootDaemon.exit();
134 | }
135 | };
136 |
137 | // We use this instead of 'new RootIPC(...)' in daemon mode
138 | RootDaemon.register(BuildConfig.APPLICATION_ID, ipc, 0);
139 |
140 | // Keep serving our IPC interface until terminated
141 | RootDaemon.run();
142 |
143 | // RootDaemon.run() never actually returns so this will never be executed
144 | Logger.d("END");
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
31 |
32 |
42 |
43 |
59 |
60 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chainfire/librootjava/baf99656d0a85b50bc626e1bfca963c2cb3ba87e/librootjavadaemon_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | libRootJavaDaemon Example
3 |
4 |
--------------------------------------------------------------------------------
/librootjavadaemon_example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':librootjava', ':librootjava_example', ':librootjavadaemon', ':librootjavadaemon_example'
2 |
--------------------------------------------------------------------------------