[DEV GUIDE][2016.12.22] How-To SU

Chainfire

Moderator Emeritus / Senior Recognized Developer -
Oct 2, 2007
11,440
87,674
263
www.chainfire.eu
Guidelines for problem-free su usage

How-To SU is my guide on using "su" in your own programs (to execute commands as root). The guide covers the major common pitfalls and also provides example code, as I have been asked by several developers to provide.

I also did a presentation on this stuff at the Big Android BBQ 2012, so if you're looking for the article and code promised there, here it is!

The text is too long to copy here, but you can find it here:

http://su.chainfire.eu/

Example code is located on GitHub:

https://github.com/Chainfire/libsuperuser

If you are not an app developer, you have no business posting in this thread. This thread is for developer discussion on the article/example code only. In fact, I do not expect many posts in this thread at all - it's all rather straightforward, and this is not a helpdesk.

Moderators: Do not move this thread to a development subsection, 0-posters need to be able to reply to it.
 
Last edited:

franciscofranco

Recognized Developer
Dec 9, 2010
24,725
136,401
0
Carcavelos
Awesome job man, I definitely learned some new stuff. I've done a little bit differently than your implementation: instead of doing AsyncTask -> run su code inside I did this in my Root class:

Code:
class BackgroundThread extends AsyncTask<String, Void, Void> {
		private String s;
		public BackgroundThread(String command) {
			s = command;
		}
		@Override
		protected Void doInBackground(String... command) {
			Process process = null;
			DataOutputStream os = null;
			try {
				process = Runtime.getRuntime().exec("su");
				os = new DataOutputStream(process.getOutputStream());
				os.writeBytes(s+"\n");
				os.writeBytes("exit\n");
				os.flush();
				process.waitFor();
			} catch (Exception e) {
			}
			finally {
				try {
					if (os != null)
						os.close();
					else
						process.destroy();
				} catch (Exception e) {

				}
			}
			return null;
		}
	}
And then receive the command in the parameter:

Code:
public void run(String command) {
		new BackgroundThread(command).doInBackground();
	}
Is this acceptable from your point of view?
 

Chainfire

Moderator Emeritus / Senior Recognized Developer -
Oct 2, 2007
11,440
87,674
263
www.chainfire.eu
No it isn't acceptable, but the reason is not what you might think.

Using a generic AsyncTask derived class to run su calls can be a solution. As long as your code ends up running in the background, all is good. However, in my experience what usually happens is that you have to do a lot of things in the background.

For example, imagine that during your app's startup, you need to check some files, perform some su calls, check some more files, do some computations, etc. Those should all go in a single AsyncTask's doInBackground method. That way you can use onPreExecute and onPostExecute to show and dismiss a dialog that tells the user you are busy.

Furthermore, if you had 10 su commands to run, would you just call new BackgroundThread ... 10 times? As the call should return before the su command is finished executing, the order of the executions of those commands becomes semi-random, you can't depend on them being run in any specific order, it even differs between Android versions (some versions run AsyncTasks serially, others run X of them in parallel). Not to mention that you shouldn't create 10 separate su processes to run 10 commands unless you have a good reason. Batching commands together == much higher performance.

The above does depend on which commands you need to execute, the needs of your application, and how you use it. If you only have to execute a single su command, it can be done that way, but I think in general this is not an ideal solution.

Now, the reason why your solution is truly unacceptable is because you are calling doInBackground. This does not make the code run in the background. You override doInBackground and insert your code, but you call the execute method. You should call your code like this:

Code:
new BackgroundThread(command).execute();
Else you're still calling on the main thread !

If you really want to keep using this method, keep in mind that you can actually pass parameters to the execute() function that in turn will be passed to doInBackground. Look in the AsyncTask documentation ... :)
 

franciscofranco

Recognized Developer
Dec 9, 2010
24,725
136,401
0
Carcavelos
No it isn't acceptable, but the reason is not what you might think.

Using a generic AsyncTask derived class to run su calls can be a solution. As long as your code ends up running in the background, all is good. However, in my experience what usually happens is that you have to do a lot of things in the background.

For example, imagine that during your app's startup, you need to check some files, perform some su calls, check some more files, do some computations, etc. Those should all go in a single AsyncTask's doInBackground method. That way you can use onPreExecute and onPostExecute to show and dismiss a dialog that tells the user you are busy.

Furthermore, if you had 10 su commands to run, would you just call new BackgroundThread ... 10 times? As the call should return before the su command is finished executing, the order of the executions of those commands becomes semi-random, you can't depend on them being run in any specific order, it even differs between Android versions (some versions run AsyncTasks serially, others run X of them in parallel). Not to mention that you shouldn't create 10 separate su processes to run 10 commands unless you have a good reason. Batching commands together == much higher performance.

The above does depend on which commands you need to execute, the needs of your application, and how you use it. If you only have to execute a single su command, it can be done that way, but I think in general this is not an ideal solution.

Now, the reason why your solution is truly unacceptable is because you are calling doInBackground. This does not make the code run in the background. You override doInBackground and insert your code, but you call the execute method. You should call your code like this:

Code:
new BackgroundThread(command).execute();
Else you're still calling on the main thread !

If you really want to keep using this method, keep in mind that you can actually pass parameters to the execute() function that in turn will be passed to doInBackground. Look in the AsyncTask documentation ... :)
Hmm... I thought calling new BackgroundThread(command).doInBackground(); was doing it in the background thread... if I call new BackgroundThread(command).execute; the commands won't be called at all until I close the application/activity.

I don't do X new calls, for example in the BootService I just batch commands together and execute it once.

I'll use your code and see how it works.
 
Last edited:
P

pedja1

Guest
Very nice reading. I always use AsyncTask for su commands, although I learned it the hard way:)
I remember few months back I couldn't figure out why supersu pops up when timeout is at 2-3 seconds:D

Sent from my HTC EVO 3D X515m using Tapatalk 2
 

AdamOutler

Retired Senior Recognized Developer
Feb 18, 2011
5,224
9,808
0
Miami, Fl̨̞̲̟̦̀̈̃͛҃҅͟orida
I generally work with desktop apps. Is there a reason to not use Runnable?

Code:
public void liveBackgroundShellCommand() {


        Runnable r = new Runnable() {

            public void run() {
                boolean LinkLaunched = false;
                try {
                    String[] params = (String[]) Statics.LiveSendCommand.toArray(new String[0]);
                    Process process = new ProcessBuilder(params).start();
                    BufferedReader STDOUT = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    BufferedReader STDERR = new BufferedReader(new InputStreamReader(process.getErrorStream()));
This is how I set up a background shell. Then I just pass in the su -c command. Is there a disadvantage to this?
 

Chainfire

Moderator Emeritus / Senior Recognized Developer -
Oct 2, 2007
11,440
87,674
263
www.chainfire.eu
I generally work with desktop apps. Is there a reason to not use Runnable?

Code:
public void liveBackgroundShellCommand() {


        Runnable r = new Runnable() {

            public void run() {
                boolean LinkLaunched = false;
                try {
                    String[] params = (String[]) Statics.LiveSendCommand.toArray(new String[0]);
                    Process process = new ProcessBuilder(params).start();
                    BufferedReader STDOUT = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    BufferedReader STDERR = new BufferedReader(new InputStreamReader(process.getErrorStream()));
This is how I set up a background shell. Then I just pass in the su -c command. Is there a disadvantage to this?
If this is about desktop Java, then I don't know. I don't use desktop Java, but in most languages and frameworks it is generally frowning upon to block the UI thread, and the UI thread is usually the main/default/first thread.

If we're talking about Android, then this may be bad, depending on what you are doing. A Runnable by itself says nothing about threading or being in the background, a Runnable is just a generic way to define some code you want to run somewhere, sometime.

The code you pasted here does start the process, but it does not wait for the process to exit or read/write to the streams. As such, this piece of code does not block, and is fine. 9 out of 10 times however, a piece of code like this is going to be followed by something like process.waitFor() or reading from STDOUT/STDERR in which cases it does block, and is thus holding up your UI thread and thus bad.

Furthermore, as detailed in the article, calling "su -c" can be errorprone.
 

Chainfire

Moderator Emeritus / Senior Recognized Developer -
Oct 2, 2007
11,440
87,674
263
www.chainfire.eu
Right. I generally launch in a Thread T=new Thread(..). I'be never used AsyncTask. I'm just wondering what its all about.
Ok, yeah that's fine. AsyncTask is pretty much a Thread wrapper with handy utility functions (onPreExecute, onPostExecute, onProgressUpdate) that is specifically aimed at running a short-lived task away from the UI thread, often with UI feedback (it's brilliant for use with progress dialogs, for example).
 

AdamOutler

Retired Senior Recognized Developer
Feb 18, 2011
5,224
9,808
0
Miami, Fl̨̞̲̟̦̀̈̃͛҃҅͟orida
Ok, yeah that's fine. AsyncTask is pretty much a Thread wrapper with handy utility functions (onPreExecute, onPostExecute, onProgressUpdate) that is specifically aimed at running a short-lived task away from the UI thread, often with UI feedback (it's brilliant for use with progress dialogs, for example).
Ah ha! its like a SwingWorker() but with better functionality... Thanks Chainfire! I just looked and apparently it's not available on desktop or I would be switching immediately. That's a wonderful class.
 
  • Like
Reactions: gh0stslayer

mikereidis

Inactive Recognized Developer
Jan 28, 2011
7,826
4,142
0
Ottawa/Gatineau, Canada
Guidelines for problem-free su usage

How-To SU is my guide on using "su" in your own programs (to execute commands as root). The guide covers the major common pitfalls and also provides example code, as I have been asked by several developers to provide.
Great & sorely needed topic, thank you. I rarely check this forum, but stumbled in here. Hopefully busy devs of SU apps will be pointed here by someone.

I continue to have lots of problems with SuperUser/SuperSU apps that seem to be pre-configured or something to prevent SU access; and the user is never even prompted. But I don't think this is being discussed here.

At some point in the early development of ICS ROMs I noted that SU calls were taking much longer, perhaps about 0.1 seconds. So I optimized the 4-5 seconds and 40 or 50 calls (including blind chmod's for files that didn't exist on many devices) to about 2-4 calls for most devices.

I know I continue to be guilty of calling SU, and shell, and various other things from improper places, such as UI threads, onCreate etc. But I don't think these cause too much trouble in general, so putting these in threads or wherever is an exercise I save for an app re-design.

My app currently allows selection of "SU Type", but it doesn't seem to be needed anymore, now that I'm avoiding the quoting problems I've had previously.

I use the system() call from C/JNI in a lib. My best/default is like this, where CMD is the command, and noting that \" is " escaped in C:
system ("echo \"CMD\" | su -c sh");


This works, but writes to the filesystem every time:
system ("su -c \"sh /data/local/cmd\""); // Write CMD to cmd file first.


The simplest, but I was having more problems with this for whatever reason; perhaps quoting issues:
system ("su -c \"CMD\"");
 
  • Like
Reactions: gh0stslayer

andrewpmoore

Senior Member
Nov 21, 2003
653
684
0
Leeds
www.rageconsulting.com
thx

---------- Post added at 11:03 PM ---------- Previous post was at 10:58 PM ----------

it is great
I'm trying to convert my app to use your example classes for running root commands, as most of my app was implemented ok, but I needed to change a couple of bits based on your document. Once chaning I keep running into the following error:

11-20 22:16:39.770: I/System(31924): libcore.io.ErrnoException: kill failed: ESRCH (No such process)
11-20 22:16:39.770: I/System(31924): at libcore.io.Posix.kill(Native Method)
11-20 22:16:39.770: I/System(31924): at libcore.io.ForwardingOs.kill(ForwardingOs.java:77)
11-20 22:16:39.770: I/System(31924): at java.lang.ProcessManager$ProcessImpl.destroy(ProcessManager.java:257)
11-20 22:16:39.770: I/System(31924): at eu.chainfire.libsuperuser.Shell.run(Shell.java:100)
11-20 22:16:39.770: I/System(31924): at eu.chainfire.libsuperuser.Shell$SH.run(Shell.java:149)
11-20 22:16:39.770: I/System(31924): at com.rageconsulting.android.lightflow.util.RootUtil.runRootUnixCommand(RootUtil.java:49)

I'm calling my own helper method which is just a wrapper around your Shell class and it's getting called from an IntentService so I'm not quite sure why this happens.
 

Dark3n

Recognized Developer
Sep 14, 2007
2,566
5,156
203
twitter.com
You can ignore that.

By calling exit here, the process is usually gone.
When it tries to destroy the process in this line, the process is already gone, so it throws that error message as there is nothing to kill/destroy.
Before Android 4.0 this message was not even printed into the log.
 

maluus

Senior Member
Dec 26, 2010
463
68
58
Ankara
Hello dear developers of the supersu. first thanks for good work. I don't know if anyone informed but after htc one s jb update there seem to be some compatibility problems.