Android- performing su commands programatically does not work

17,264

Solution 1

I can get stuck on a problem for days, and the moment I gather up the courage to ask about it on StackOverflow, it is solved within minutes.

The fix is:

    p=Runtime.getRuntime().exec("su");
    DataOutputStream dos = new DataOutputStream(p.getOutputStream());
    dos.writeBytes("mkdir /sdcard/testdir\n");
    dos.writeBytes("exit\n");
    dos.flush();
    dos.close();
    p.waitFor();

Don't forget \n at the end of each command you write to the DataOutputStream, as it will not work without it.

Solution 2

You wrote that you "need varied commands to be performed under su". Note that the use of "Runtime.exec()" is discouraged by Chainfire, the developer of the most famous SuperSU root app.

It is tempting to use Runtime.getRuntime().exec("su -c [command]");, but you should be aware that [command] should be a single parameter, and thus may require quoting. Unfortunately both quoting the [command] parameter as well as passing the paramaters as separate variables to either Runtime.exec() or ProcessBuilder does not work consistently across all Android versions, and thus this construct should be avoided entirely. It is not impossible to do this right - but there's a high risk of problems.

See the How to SU Document. So you might want to follow his recommendation here:

3.2. Making the call

A common method to call su that avoids the known issues listed above is by creating an interactive shell and piping commands to it. This is done by calling Runtime.getRuntime().exec("su");, and retrieving input and output streams from the returned Process object. Doing this is a fairly straight-forward piece of code, but including the debug logs and checks it's a bit long to reproduce here.

The core code is located here: [libsuperuser :: Shell.java @ GitHub]. Shell.run() is a generic call to run shell code, the following more specific (static) utility functions are the ones you will probably end up using:

List<String> Shell.SH.run(String command)
List<String> Shell.SH.run(List<String> commands)
List<String> Shell.SH.run(String[] commands)

List<String> Shell.SU.run(String command)
List<String> Shell.SU.run(List<String> commands)
List<String> Shell.SU.run(String[] commands)

The SH variants are used for a non-root shell, where the SU variants are used for a root shell. These calls return a List containing the output of the shell commands. If there was no output, the list is empty, but not null. The result is only null in case an error occured - including the user not granting your app su access. These are blocking calls.

Note that in debug compiles, all shell STDIN/STDOUT/STDERR will be logged to logcat, and these calls will (intentionally) crash your app if called from the main thread. The reason for this will be discussed in section 4. When to call su.

Solution 3

If you use double quotes, it will work:

su -c ""command with args""
Share:
17,264

Related videos on Youtube

LoneDuck
Author by

LoneDuck

Updated on September 20, 2022

Comments

  • LoneDuck
    LoneDuck over 1 year

    I need my app to perform some su commands programatically (phone is rooted).

    When done using adb, the commands work.

    For instance: su -c "mkdir /sdcard/testdir" creates a directory called "testdir" in /sdcard.

    When I call:

        p = Runtime.getRuntime().exec("su -c \"mkdir /sdcard/testdir\"");
        p.waitFor();
    

    It just moves on and no change happens.

    I tried reading the input:

    DataInputStream dis = new DataInputStream(p.getInputStream());
        while((temp = dis.readLine())!=null)
            Log.d(ctx.TAG,"shell:"+temp);
    

    But it reports nothing (loop does 0 iterations).

    Has anyone ever faced this issue before? How can it be solved? Needless to day, non-su commands do work programatically with this method.

    Note: I gave mkdir as an example (I know it doesn't necessarily require su). I need a lot of varied commands to be performed under su

    Thank you!

    EDIT: when I call su -c "id" programatically, there's output that uid=0.

  • LoneDuck
    LoneDuck almost 9 years
    Thanks Aswin, but my program doesn't get stuck- p.waitFor() finishes and it goes on along. The problem is that no impact is made by the command, and no testdir is created in /sdcard.
  • Aswin
    Aswin almost 9 years
    Is your sdcard mounted? Check the path "/sdcard".
  • LoneDuck
    LoneDuck almost 9 years
    Yes, and su -c "mkdir /sdcard/testdir" does work if performed in adb shell.
  • Elliptica
    Elliptica over 6 years
    If I launch a superuser dialogue this way, I can't click it
  • Mobile World
    Mobile World over 6 years
    it is getting error java.io.IOException: Cannot run program "su": error=13, Permission denied
  • sadat
    sadat about 4 years
    I am getting "Caused by: java.io.IOException: Cannot run program "su": error=13, Permission denied". How can I configure my emulator to get this permission?