[DEV GUIDE][2016.12.22] How-To SU

Search This thread

AnkitChowdhury

Senior Member
Jul 18, 2012
520
360
Nexus 7 (2013)
Xiaomi Mi A1
Try "attradd { uhid_device input_device } { mlstrustedsubject mlstrustedobject }". Not quite sure about this one.

Sorry for taking long to test. While the policies are patched successfully. It unfortunately doesn't make a difference. As the uinput module still remains inaccessible.

I actually tried it with another device on Lollipop just to make sure there isn't any problem with my code. On Lollipop the Virtual Touch Screen creation was successful and everything worked the way it should've. Thanks for trying to help. Is there anything else we can do? The lack of audit messages aren't helping either.

---------- Post added at 04:52 AM ---------- Previous post was at 04:48 AM ----------

Note of course that a better solution would be running whatever handles this input as root, and have it communicate with your main app over sockets and pipes, rather than adjusting security settings for all appdomain/untrusted_app.

Yes I understand that. It was a bad design decision. Choosing to use JNI libraries.

It'll be great if you could provide any solution to my current issue. If nothing works out unfortunately I will probably have to rewrite most parts of the app. Thanks again though. :)

---------- Post added at 05:01 AM ---------- Previous post was at 04:52 AM ----------

I also noticed something new in the audit logs in Marshmallow. Something like this " untrusted_app:s0:c512,c768 " . Does this signify anything new?
 

AnkitChowdhury

Senior Member
Jul 18, 2012
520
360
Nexus 7 (2013)
Xiaomi Mi A1
Hello @Chainfire, I've tried to do a bit of research regarding my issue. The problem as it seems is that I'm using JNI Library instead of JNI Executable.

Had I used JNI Executable I could have spawned it through my app from SU shell and could've changed it's context. But I'm using JNI Library and use an interface to call C/C++ functions via Java.

This way the library is loaded by the app and uses the app's context. Which is untrusted:s0. Even with Root access my Application's context doesn't change to init:s0. Only the supersu shell has init:s0 context.

Is there anyway we could change the context of my App to init:s0 or maybe system_app:s0? This might solve my issue I believe. I would really appreciate if you could help me with this, the one last time.

Thanks again. :)
 

Chainfire

Moderator Emeritus / Senior Recognized Developer
Oct 2, 2007
11,451
87,853
www.chainfire.eu
I also noticed something new in the audit logs in Marshmallow. Something like this " untrusted_app:s0:c512,c768 " . Does this signify anything new?

Yes, it's called MLS/MCS. This is why I asked you try mlstrustedobject / mlstrustedsubject. It's a tricky subject and annoying to work with.

http://selinuxproject.org/page/NB_MLS

This way the library is loaded by the app and uses the app's context. Which is untrusted:s0. Even with Root access my Application's context doesn't change to init:s0. Only the supersu shell has init:s0 context.

Is there anyway we could change the context of my App to init:s0 or maybe system_app:s0? This might solve my issue I believe. I would really appreciate if you could help me with this, the one last time.

Unfortunately that is not currently possible.
 

ssrij

Senior Member
Sep 7, 2013
1,842
1,869
London
I am in a weird situation. I have an app that requires root to function. I am using libsuperuser and calling the available() function in a background task, but I have some of users report me that (1) they didn't see the SU authorisation prompt AND the app received root permission or (2) The app didn't make SU show the authorisation prompt, so they couldn't approve the app. On my phone (using latest SuperSU beta), and on many other people's phones, it works fine, but on some, it does that.

Any idea why this could happen and what can be done to fix this? This is how I call available():

Code:
Tasks.executeInBackground(MainActivity.this, new BackgroundWork<Boolean>() {
                @Override
                public Boolean doInBackground() throws Exception {
                    return Shell.SU.available();
                }
            }, new Completion<Boolean>() {
                @Override
                public void onSuccess(Context context, Boolean result) {
                    isSuAvailable = result;
                    Log.i(TAG, "SU available: " + Boolean.toString(result));
                    if (isSuAvailable) {
                        // Perform stuff
                        }
                    } else {
                        Log.i(TAG, "SU permission denied or not available");
                        // Show error dialog
                    }
                }
 
Last edited:

Chainfire

Moderator Emeritus / Senior Recognized Developer
Oct 2, 2007
11,451
87,853
www.chainfire.eu
I have some of users report me that (1) they didn't see the SU authorisation prompt AND the app received root permission or (2) The app didn't make SU show the authorisation prompt, so they couldn't approve the app

Which su are they running?

On various CM-based firmwares you do not get a confirmation dialog, you need to manually enable root access before starting the app.
 

ssrij

Senior Member
Sep 7, 2013
1,842
1,869
London
Which su are they running?

On various CM-based firmwares you do not get a confirmation dialog, you need to manually enable root access before starting the app.

I'm not sure, I haven't asked them, although some of them said they're on the latest SuperSU stable or KingRoot. I'm on SuperSU beta (May 10) on a Galaxy S7 edge, and I see a prompt.

Is there a way to handle external authorisation? A way to detect if the SU authorisation dialog will show up or not, so I can tell the user to manually grant access via their SU app.

Sent from my Samsung Galaxy S7 Edge using XDA Labs
 

Chainfire

Moderator Emeritus / Senior Recognized Developer
Oct 2, 2007
11,451
87,853
www.chainfire.eu
I'm not sure, I haven't asked them, although some of them said they're on the latest SuperSU stable or KingRoot. I'm on SuperSU beta (May 10) on a Galaxy S7 edge, and I see a prompt.

Well, then it seems you have altogether too little info...

Is there a way to handle external authorisation? A way to detect if the SU authorisation dialog will show up or not, so I can tell the user to manually grant access via their SU app.

Nope.

The best you can do is check the su version and compare it with known strings like supersu or cm-su.
 

ssrij

Senior Member
Sep 7, 2013
1,842
1,869
London
Well, then it seems you have altogether too little info...



Nope.

The best you can do is check the su version and compare it with known strings like supersu or cm-su.

So how does one handle other SU apps or SU binaries located in a different path? It seems like bruteforcing is the only way. I wish there was a consistent way (maybe a method in libsuperuser) to handle different scenarios.
 

Chainfire

Moderator Emeritus / Senior Recognized Developer
Oct 2, 2007
11,451
87,853
www.chainfire.eu
So how does one handle other SU apps or SU binaries located in a different path? It seems like bruteforcing is the only way. I wish there was a consistent way (maybe a method in libsuperuser) to handle different scenarios.

Those things are not related. libsuperuser will call su regardless of its path, its not hardcoded. Whether that su call pops up a dialog for the user to click on, or automatically denies, or grants, or whatever, is up to the implementation. You do not control that, and you should not want to.

The su call is the interface. This is the same across all root solutions. What happens next is implementation specific.
 

vishal007

Senior Member
Sep 7, 2011
1,461
395
Bengaluru
teamamaze.xyz
@Chainfire I'm trying to start an interactive shell at the startup of app but am having some problems with the callbacks.
So, in my onCreate I start an interactive shell using Shell.Builder.open method. Then in one of my Asynctasks I need to run the su command.
I read the Shell.Interactive documentation and you clearly stated that callbacks will be executed on the thread in which the instance was created (unless I call #setAutoHandler with false). But I'm already in my asynctask and I need the callback to be executed on that same thread. Is there any way I can do that? The inline documentation in java docs is a bit conflicting in this regard, as in one line you say not to pass commands in main thread, but then later on you say that only main thread have a Looper and only it can handle the callback, and that while the callback (onLineListener for eg.) is being executed, we need to take into consideration the multithreading point stand, and make calls thread safe.
I think I'm missing some really foolish. Please point me in the right path.
 

Chainfire

Moderator Emeritus / Senior Recognized Developer
Oct 2, 2007
11,451
87,853
www.chainfire.eu
@Chainfire I'm trying to start an interactive shell at the startup of app but am having some problems with the callbacks.
So, in my onCreate I start an interactive shell using Shell.Builder.open method. Then in one of my Asynctasks I need to run the su command.
I read the Shell.Interactive documentation and you clearly stated that callbacks will be executed on the thread in which the instance was created (unless I call #setAutoHandler with false). But I'm already in my asynctask and I need the callback to be executed on that same thread. Is there any way I can do that? The inline documentation in java docs is a bit conflicting in this regard, as in one line you say not to pass commands in main thread, but then later on you say that only main thread have a Looper and only it can handle the callback, and that while the callback (onLineListener for eg.) is being executed, we need to take into consideration the multithreading point stand, and make calls thread safe.
I think I'm missing some really foolish. Please point me in the right path.

You have not fully understood the docs, and you are lacking knowledge of some basic Android principles (blocking operations, handlers, loopers).

You should not perform blocking operations on the main thread. Adding a command to a Shell.Interactive instance is generally not a blocking operation. #run(), #waitForIdle(), and #close() are blocking operations. Disk/file operations are blocking operations. Network operations are blocking operations. Any operation that runs in unknown time is a blocking operation.

As with many other APIs, callbacks are posted to a Handler. A Handler requires a Looper. You may provide your own Handler, or if you do not, libsuperuser will create one for you (unless you call #setAutoHandler(false)). Each Looper can have multiple Handlers, this is not an issue. A Handler will be associated with the thread on which new Handler() is called, and callbacks posted to that Handler will be executed on that thread - this call is made by libsuperuser on the thread on which you call #open().

setHandler(yourHandler)
- callbacks executed on thread from yourHandler

setHandler(null) && setAutoHandler(true)
- callbacks executed on thread that called #open()
- if you call #open() from the main thread, your callbacks may block the main thread, which is bad

setHandler(null) && setAutoHandler(false)
- callbacks executed on one of the Gobbler (internal libsuperuser) threads
- Gobbler threads are dangerous to block, could lead to deadlock. Very simple code like setting a synchronized variable based on parsed lines is usually not a problem.

Nowhere do I write that only the main thread has a Looper, but it indeed has one. It's entire operation is built around it. Background threads like AsyncTask#doInBackground() do not generally have a Looper, but it is certainly possible to create a background thread that has one. In fact, there is a special class for this: HandlerThread:

Code:
HandlerThread ht = new HandlerThread("threads must have a name");
ht.start();
Handler h = new Handler(ht.getLooper());

.... Shell.Builder#setHandler(h) ...

All callbacks will then be called on the ht thread. You can run your own Runnable's on that thread as well, by using h.post(Runnable).

The exact reasons why are lengthy and complicated, but you should not create many different threads with Loopers, and you should keep them around and re-use them. For example, in most of my own apps, there is the Looper on the main thread, and I have one (two in rare cases) additional HandlerThread to perform all sorts of background operations, such as callbacks I do not want to run on the main thread. While it is technically possible to create a Looper and a Handler in an AsyncTask's thread and use them, this is strongly recommended against.

Due to the asynchronous nature of libsuperuser, it technically unfeasable to call callbacks on a thread directly, this is why Loopers and Handlers are used, which exist exactly for this purpose. It is important to understand that these are basically just a message loop/pump, and messages are processed serially. On the main thread, all your code is already run inside this message loop, this is just hidden from you. Imagine doing this:

Code:
public void onStart() {
    (new Shell.Builder())
        .addCommand("echo test",  0, new Shell.OnCommandResultListener() {
            public void onCommandResult(int commandCode, int exitCode, List<String> output) {
                // point A
            }
        })
        .open()
    // point B
        .waitForIdle();
    // point C
}

If you want to use results from a command in a procedural fashion, rather than event-based, you will need the #waitForIdle() call.

In this case, a Handler is auto-created on the main thread, and thus the callback (point A) becomes a message in the main thread's pump, once the command is done executing. But, as I mentioned before, all code in your activity is itself run inside a message in the same pump. #waitForIdle() waits for point A to be reached, but the message containing point A will not be run until point C is reached. This is a deadlock situation (A waits for C to happen, while C waits for A to happen).

If your AsyncTask had a Looper and a Handler, and you would run the callbacks on the same thread as the #doInBackground() call, the same thing would happen.

In other words: if you want to use results from a shell call in procedural fashion, you want the callbacks to run on a different thread than the calling code!

Something like this works:

Code:
private HandlerThread backgroundHandlerThread;
private Handler backgroundHandler;
private Shell.Interactive shell;

private void ensureShell() {
    if (shell != null) return;
    backgroundHandlerThread = new HandlerThread("BackgroundHandlerThread");
    backgroundHandlerThread.start();
    backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
    shell = (new Shell.Builder()).useSU().useHandler(backgroundHandler).open();
}

@Override
public void onStart() {
    ensureShell();
    (new MyTask()).execute(); 
}

private class MyTask extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected Boolean doInBackground(Void... params) {
        final Boolean[] result = new Boolean[] { false };
        shell.addCommand("echo foobar", 0, new Shell.OnCommandResultListener() {
            public void onCommandResult(int commandCode, int exitCode, List<String> output) {
                for (String line : output) {
                    if (output.contains("foobar")) {
                        result[0] = true;
                        break;
                    }
                }
            }
        });
        shell.waitForIdle();
        return result[0];
    }
}

This is the most elaborate that I have ever explained this, and I do not think I will comment on it further. Read it (and d.android.com) until you understand it.

Disclaimer: all code from memory, may not actually compile.
 
Last edited:

vishal007

Senior Member
Sep 7, 2011
1,461
395
Bengaluru
teamamaze.xyz
In other words: if you want to use results from a shell call, you want the callbacks to run on a different thread than the calling code!

Something like this works:

Code:
private HandlerThread backgroundHandlerThread;
private Handler backgroundHandler;
private Shell.Interactive shell;

private void ensureShell() {
    if (shell != null) return;
    backgroundHandlerThread = new HandlerThread("BackgroundHandlerThread");
    backgroundHandlerThread.start();
    backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
    shell = (new Shell.Builder()).useSU().useHandler(backgroundHandler).open();
}

@Override
public void onStart() {
    ensureShell();
    (new MyTask()).execute(); 
}

private class MyTask extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected Boolean doInBackground(Void... params) {
        final Boolean[] result = new Boolean[] { false };
        shell.addCommand("echo foobar", 0, new Shell.OnCommandResultListener() {
            public void onCommandResult(int commandCode, int exitCode, List<String> output) {
                for (String line : output) {
                    if (output.contains("foobar")) {
                        result[0] = true;
                        break;
                    }
                }
            }
        });
        shell.waitForIdle();
        return result[0];
    }
}

Thank you very much for your insight. It certainly helped me understand the looper and handler concepts better.
Although I had already implemented them, the exact reason I was not getting any results is that I was using Shell.Builder object instead of Shell.Interactive returned from Shell.Builder#open method to add a command, and was waiting for Shell.Interactive#waitForIdle returned from that #open call. Such a little foolish mistake. This last piece of code from you saved my day :)
I overlooked this part from your example, my fault.
 
Last edited:

kbeaumont

Member
Jul 1, 2016
5
0
I have a Teclast X10 3g phablet with MT8392 chip. There is NO byName for the mounts. Instead it has /proc/dumchar_info.

adb shell ls -l /proc/dum*
-r--r--r-- root root 0 2017-03-09 07:50 dumchar_info
kb@kb-VPCYB15AB:~$ adb shell cat /proc/dum*
Part_Name Size StartAddr Type MapTo Region
preloader 0x0000000000040000 0x0000000000000000 2 /dev/misc-sd BOOT_1
mbr 0x0000000000080000 0x0000000000000000 2 /dev/block/mmcblk0 USER
ebr1 0x0000000000080000 0x0000000000080000 2 /dev/block/mmcblk0p1 USER
pro_info 0x0000000000300000 0x0000000000100000 2 /dev/block/mmcblk0 USER
nvram 0x0000000000500000 0x0000000000400000 2 /dev/block/mmcblk0 USER
protect_f 0x0000000000a00000 0x0000000000900000 2 /dev/block/mmcblk0p2 USER
protect_s 0x0000000000a00000 0x0000000001300000 2 /dev/block/mmcblk0p3 USER
seccfg 0x0000000000040000 0x0000000001d00000 2 /dev/block/mmcblk0 USER
uboot 0x0000000000060000 0x0000000001d40000 2 /dev/block/mmcblk0 USER
bootimg 0x0000000000a00000 0x0000000001da0000 2 /dev/block/mmcblk0 USER
recovery 0x0000000000a00000 0x00000000027a0000 2 /dev/block/mmcblk0 USER
sec_ro 0x0000000000600000 0x00000000031a0000 2 /dev/block/mmcblk0p4 USER
misc 0x0000000000080000 0x00000000037a0000 2 /dev/block/mmcblk0 USER
logo 0x0000000000800000 0x0000000003820000 2 /dev/block/mmcblk0 USER
ebr2 0x0000000000080000 0x0000000004020000 2 /dev/block/mmcblk0 USER
frp 0x0000000000100000 0x00000000040a0000 2 /dev/block/mmcblk0p5 USER
expdb 0x0000000000e60000 0x00000000041a0000 2 /dev/block/mmcblk0 USER
android 0x0000000060000000 0x0000000005000000 2 /dev/block/mmcblk0p6 USER
cache 0x000000001a800000 0x0000000065000000 2 /dev/block/mmcblk0p7 USER
usrdata 0x0000000323100000 0x000000007f800000 2 /dev/block/mmcblk0p8 USER
bmtpool 0x0000000001500000 0x00000000ffff00a8 2 /dev/block/mmcblk0 USER
Part_Name:partition name you should open;
Size:size of partition
StartAddr:Start Address of partition;
Type:Type of partition(MTD=1,EMMC=2)
MapTo:actual device you operate

Can you fix the SU install script to work with this?

Also, do you know if there are drivers available for mt8392 in order to root the phone?
Regards,
KB
 

voider1

Member
Apr 5, 2015
8
1
I want to put SuperSU on the Wiko Freddy. I managed to patch the recovery image so that it would accept OTA updates signed by my own keys. I've also installed Xposed with success, just had to make some minor modifications to the OTA update (remounting /system as rw, the Wiko Freddy handles that in a different way). I can't figure out what goes wrong with SuperSU and I want to patch it so I can make it work with the Wiko Freddy. Busybox is installed, and the error I'm getting is:

Code:
E: Can't run /tmp/update_binary (No such file or directory)
E: Error in sideload/package.zip
(status 255)
 

Top Liked Posts

  • There are no posts matching your filters.
  • 62
    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.
    15
    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 ... :)
    5
    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?
    3
    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.
    3
    Lots of 5.0 info added the document.