Welcome to XDA

Search to go directly to your device's forum

Register an account

Unlock full posting privileges

Ask a question

No registration required
Post Reply

[DEV GUIDE][2014.11.27] How-To SU

OP Chainfire

29th October 2012, 09:38 PM   |  #1  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 55,805
 
9,487 posts
Join Date:Joined: Oct 2007
Donate to Me
More
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 by Chainfire; 28th November 2014 at 12:15 PM.
The Following 50 Users Say Thank You to Chainfire For This Useful Post: [ View ]
7th November 2012, 03:40 PM   |  #2  
franciscofranco's Avatar
Recognized Developer
Flag Mountain View, CA
Thanks Meter: 91,069
 
16,079 posts
Join Date:Joined: Dec 2010
Donate to Me
More
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?
The Following 4 Users Say Thank You to franciscofranco For This Useful Post: [ View ]
7th November 2012, 04:01 PM   |  #3  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 55,805
 
9,487 posts
Join Date:Joined: Oct 2007
Donate to Me
More
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 ...
The Following 10 Users Say Thank You to Chainfire For This Useful Post: [ View ]
7th November 2012, 04:12 PM   |  #4  
meethere's Avatar
Senior Member
Thanks Meter: 103
 
717 posts
Join Date:Joined: Jan 2012
that's a nice read
thanks for the guide
7th November 2012, 04:22 PM   |  #5  
franciscofranco's Avatar
Recognized Developer
Flag Mountain View, CA
Thanks Meter: 91,069
 
16,079 posts
Join Date:Joined: Dec 2010
Donate to Me
More
Quote:
Originally Posted by Chainfire

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 by franciscofranco; 7th November 2012 at 04:26 PM.
The Following User Says Thank You to franciscofranco For This Useful Post: [ View ]
7th November 2012, 06:22 PM   |  #6  
pedja1's Avatar
Recognized Developer
Flag Zrenjanin
Thanks Meter: 1,675
 
1,286 posts
Join Date:Joined: Oct 2011
Donate to Me
More
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

Sent from my HTC EVO 3D X515m using Tapatalk 2
7th November 2012, 07:10 PM   |  #7  
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?
7th November 2012, 08:00 PM   |  #8  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 55,805
 
9,487 posts
Join Date:Joined: Oct 2007
Donate to Me
More
Quote:
Originally Posted by AdamOutler

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.
The Following 2 Users Say Thank You to Chainfire For This Useful Post: [ View ]
7th November 2012, 08:20 PM   |  #9  
Right. I generally launch in a Thread T=new Thread(..). I'be never used AsyncTask. I'm just wondering what its all about.
8th November 2012, 12:29 AM   |  #10  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 55,805
 
9,487 posts
Join Date:Joined: Oct 2007
Donate to Me
More
Quote:
Originally Posted by AdamOutler

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).

The Following 2 Users Say Thank You to Chainfire For This Useful Post: [ View ]
Post Reply Subscribe to Thread
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes