FORUMS
Remove All Ads from XDA

[DEV GUIDE][2016.12.22] How-To SU

11,010 posts
Thanks Meter: 83,899
 
By Chainfire, Senior Moderator / Senior Recognized Developer - Where is my shirt? on 29th October 2012, 08:38 PM
Post Reply Email Thread
17th May 2016, 08:40 AM |#111  
ssrij's Avatar
Senior Member
Flag Oxford
Thanks Meter: 1,842
 
Donate to Me
More
Quote:
Originally Posted by Chainfire

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
 
 
17th May 2016, 11:56 AM |#112  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 83,899
 
Donate to Me
More
Quote:
Originally Posted by ssrij

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

Quote:

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.
18th May 2016, 02:09 AM |#113  
ssrij's Avatar
Senior Member
Flag Oxford
Thanks Meter: 1,842
 
Donate to Me
More
Quote:
Originally Posted by Chainfire

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.
18th May 2016, 07:07 AM |#114  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 83,899
 
Donate to Me
More
Quote:
Originally Posted by ssrij

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.
28th December 2016, 06:42 PM |#115  
vishal007's Avatar
Senior Member
Flag Rohtak
Thanks Meter: 336
 
Donate to Me
More
@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.
28th December 2016, 11:52 PM |#116  
Chainfire's Avatar
OP Senior Moderator / Senior Recognized Developer - Where is my shirt?
Thanks Meter: 83,899
 
Donate to Me
More
Quote:
Originally Posted by vishal007

@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.
The Following 3 Users Say Thank You to Chainfire For This Useful Post: [ View ]
29th December 2016, 05:57 PM |#117  
vishal007's Avatar
Senior Member
Flag Rohtak
Thanks Meter: 336
 
Donate to Me
More
Quote:
Originally Posted by Chainfire


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.
5th March 2017, 05:08 AM |#118  
Junior Member
Ha noi
Thanks Meter: 0
 
More
Thanks for the helpful article.
31st March 2017, 01:06 PM |#119  
Junior Member
Thanks Meter: 0
 
More
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
24th April 2017, 08:05 PM |#120  
Junior Member
Thanks Meter: 1
 
More
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)
Post Reply Subscribe to Thread

Guest Quick Reply (no urls or BBcode)
Message:
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes