Android- performing su commands programatically does not work
Asked Answered
P

4

6

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.

Pleiades answered 20/4, 2015 at 14:4 Comment(1)
Have you solved this issue? facing the same issue here exactlyMare
P
17

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.

Pleiades answered 20/4, 2015 at 14:40 Comment(3)
If I launch a superuser dialogue this way, I can't click itUropygium
it is getting error java.io.IOException: Cannot run program "su": error=13, Permission deniedDart
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?Homy
F
8

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.

Fimbria answered 20/4, 2015 at 14:58 Comment(0)
M
1

If you use double quotes, it will work:

su -c ""command with args""
Maitland answered 11/12, 2017 at 1:11 Comment(0)
L
-1

You might be calling Runtime.getRuntime().exec() in main thread and p.waitFor() makes your main thread wait until it executes. Try calling in another thread, like the following snippet.

new Thread(){

     @override
     public void run(){
        p = Runtime.getRuntime().exec("su -c \"mkdir /sdcard/testdir\"");
        p.waitFor();

     }.start();

}
Libnah answered 20/4, 2015 at 14:12 Comment(3)
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.Pleiades
Is your sdcard mounted? Check the path "/sdcard".Libnah
Yes, and su -c "mkdir /sdcard/testdir" does work if performed in adb shell.Pleiades

© 2022 - 2024 — McMap. All rights reserved.