0

I want to write a java program to find a device and list its contents. For that I first fire "adb devices" via Java's Process class and parse the output, which works just finde. Then I want to fire this command:

adb shell -s DEVICE-SERIAL /sdcard/

When I put that into my terminal I get the right output, a list of all folders and files in at the root of /sdcard/.

When I put that into Java code nothing is returned. Stderr however contains the standard help page you get when you misspell an adb command. Here's the funny thing: For debugging I output every command sent to ADB. If I run my programm and copy the exact same generated command and run it in my terminal it works just fine.

Why is that? For reference, here are my code excerpts for that:

ADB.java:

public static String runShellCommand(Device d, String command) {
    return runCommand(d, "shell " + command);
}

public static String runCommand(Device d, String command) {
    Runtime runtime = Runtime.getRuntime();
    String full = "";
    String error = "";

    try {
        if (d != null) {
            command = "-s " + d.getSerial() + " " + command;
        }

        System.out.println("Running command: adb " + command);
        Process process = runtime.exec(new String[] {ADB_LOCATION, command});

            process.waitFor();

        BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedReader errin = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        String line;

        while ((line = stdin.readLine()) != null) {
            full += line;
        }

        while ((line = errin.readLine()) != null) {
            error += line;
        }

        if (!error.isEmpty()) {
            System.err.println("\n=== ADB reports: ===");
            System.err.println(error);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        System.out.println("App was overenthuasiastic. Couldn't wait for thread to finish.");
    }

    return full;
}

Device.java:

public ArrayList<Folder> getFoldersInDirectory(String dir) {
    String[] list = ls(dir);

    for (String s: list) {
        System.out.println(s);
        System.out.println(isFolderOrFile(dir + s));
    }

    // Just for debugging, gets completed once this runs.
    return null;
}

protected String[] ls(String dir) {
    String result = ADB.runShellCommand(this, "ls " + dir);
    System.out.println(result);

    return result.split("\n");
}

As I said before, every other command runs just fine (I tested devices, get-state, get-serialno).

Outputs: Outputs

2
  • ADB.runShellCommand() is missing from your ADB.java snippet
    – Alex P.
    Commented Jan 8, 2014 at 0:02
  • runShellCommand basically just calls runCommand and puts "shell" before it but I'll post it. Commented Jan 8, 2014 at 0:11

3 Answers 3

2

The parameters for runtime.exec() are wrong. You are using {'adb', '-s xxxx shell "ls /sdcard/"'} when you should be using {'adb', '-s', 'xxxx', 'shell', 'ls /sdcard/'}

3
  • For debugging purposes I just wrote that manually but it didn't work either. Still no output. This time, stderr is empty as well. Commented Jan 8, 2014 at 0:16
  • empty /sdcard folder?
    – Alex P.
    Commented Jan 8, 2014 at 0:27
  • No, filled. I found a solution by merely accident and I'll post it, one moment. Commented Jan 8, 2014 at 0:29
2

Perhaps a bit too late for the OP, but could be useful for later viewers.

Recently I had to integrate one system to work closely with real devices via adb. Took a bit of time to figure out all the nasty bits of how to do it "properly". Then I had an idea to create a library that handles at least some basic adb commands (adb devices, shell, pull, push, install). It can do adb on local machine, or on remote machine over ssh (with key exchange setup) with the same API. Do not confuse with adb shell forwarding ;).

Its under apache 2.0 license: https://github.com/lesavsoftware/rem-adb-exec

1

Thanks to Alex P. I found a solution that's working (need to parse the output, though, but that's a different story):

I just modified runCommand() to put the arguments in an String-array and give it to exec:

public static String runCommand(Device d, String command) {
        Runtime runtime = Runtime.getRuntime();
        String full = "";
        String error = "";

        try {
            String[] cmdWithDevice = new String[5];
            String[] cmdWithoutDevice = new String[2];

            if (d != null) {
                cmdWithDevice[0] = ADB_LOCATION;
                cmdWithDevice[1] = "-s";
                cmdWithDevice[2] = d.getSerial();
                cmdWithDevice[3] = "shell";
                cmdWithDevice[4] = command;

                command = "-s " + d.getSerial() + " " + command;
            } else {
                cmdWithoutDevice[0] = ADB_LOCATION;
                cmdWithoutDevice[1] = command;
            }

            System.out.println("Running command: adb " + command);
            // Process process = runtime.exec(new String[] {ADB_LOCATION, command});
            Process process = runtime.exec(cmdWithDevice[0] == null ? cmdWithoutDevice : cmdWithDevice);

            BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
            BufferedReader errin = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            process.waitFor();

            String line;

            while ((line = stdin.readLine()) != null) {
                full += line;
            }

            while ((line = errin.readLine()) != null) {
                error += line;
            }

            if (!error.isEmpty()) {
                System.err.println("\n=== ADB reports: ===");
                System.err.println(error);
            }

            stdin.close();
            errin.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("App was overenthuasiastic. Couldn't wait for thread to finish.");
        }

        return full;
    }

For some reason it does not work if I run runtime.exec manually with the arguments passed in, only programmatically.

This solution is a bit hacky, though and needs proper implementation. Also this solution assumes that every device-specific command is for shell.

Not the answer you're looking for? Browse other questions tagged or ask your own question.