2

I am working on a program that needs to be able to determine what is on the android device at Xlocation. I am using "su ls Xlocation"

I want to get back and array list of files but only manage to get back the first Item. Am I missing a command that gets the next line? Or is there something else I need to do.

Below is my command I am sending

String[] commands = new String[]{"ls /system/app/"};
return doCommand(commands);

Below is my current method for doCommand

    private boolean doCommand(String[] commands)
    {
    ArrayList<String> output = new ArrayList<String>();

    boolean ran = false;
    try
    {
        Process process = Runtime.getRuntime().exec("su");
        DataOutputStream os = new DataOutputStream(process.getOutputStream());
        DataInputStream ins = new DataInputStream(process.getInputStream());

        // This part works I sends the command right!
        for (String single : commands) 
        {
            os.writeBytes(single + "\n");
            os.flush();
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
            ran = true;
        }

        int av = -1;
        while (av != 0)
        {

//////////////////////////// WORKING ON THIS TO GET ALL INFO /////////////////
            av = ins.available();
            if (av != 0) 
            {
                byte[] b = new byte[av];
                ins.read(b);
                output.add(new String(b));
                System.out.println(new String(b) + "Recieved form modem");
            }
        }
    }
    catch(Exception ex){}
    return ran;
}

As seen currently it only returns true or false. However run in debug I only get the first item in output. (output = "[first.apk")

Edited Newer Version;

    public ArrayList<String> doCommand(String[] commands)
{

    ArrayList<String> output = new ArrayList<String>();

    try
    {

        Process process = Runtime.getRuntime().exec("su");
        DataOutputStream os = new DataOutputStream(process.getOutputStream());  
        DataInputStream ins = new DataInputStream(process.getInputStream());
        for (String single : commands) 
        {
            os.writeBytes(single + "\n");

            //os.flush();
            output.add("true");
        }
        os.writeBytes("exit\n");


        byte[] bc = new byte[10000];
        String st = "";
        while(ins.read(bc) != -1)
        {
        st += new String(bc);
        os.flush();

        }
        output.add(st);


        os.flush();
        os.close();
        ins.close();
        process.waitFor();          
    }
    catch(Exception ex)
    {}

    return output;
}

Now getting a decent amount of output but still not all where the directory has large items inside size limit of byte[10000] I have checked.

If anyone wants to improve on this and get an exact answer that works do so I still check this post.

Thanks

SatanEnglish
  • 1,346
  • 1
  • 13
  • 22

2 Answers2

2

You can try adapting this method from my open source User Management (GitHub) app to do what you want. This will read each and every line of the output following a terminal command:

public static String[] getUserList()
{
    Process p;
    try {
        p = Runtime.getRuntime().exec("su");
        DataOutputStream os = new DataOutputStream(p.getOutputStream());  
        BufferedReader bf = new BufferedReader(new InputStreamReader(p.getInputStream()));

            os.writeBytes("pm list-users"+"\n");
            os.writeBytes("exit\n"); 
            ArrayList<String> users = new ArrayList<String>();
            String test;
            bf.readLine();
            while((test = bf.readLine()) != null)
            {
                users.add(test);
            }

            String[] userList = (String[]) users.toArray(new String[users.size()]);



        os.flush();
        return userList;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}
Raghav Sood
  • 81,899
  • 22
  • 187
  • 195
  • This is a good answer and have up it as good but it doesn't solve my problem this may be my ignorance. I doesn't work for case "ls /system/app/appname_*"+"\n" – SatanEnglish Jan 11 '13 at 00:28
  • but will work for "ls /system/app/" MY orginal gives me one return for the appname_* but that is all even if there are more than 1 – SatanEnglish Jan 11 '13 at 00:29
2

I'm a bit new to Java but I believe the issue is just a minor oversight.. ne in which I've run into a few times with Python ;)

Because you've added the below into your while loop:

        os.writeBytes("exit\n");
        os.flush();

After each iteration you are attempting to exit the terminal session. After the first iteration the terminal session would already be closed so this would explain why only the first result would be returned.

I would suggest adding the exit code after the while loop completes to ensure you are not sending the rest of the commands to a dead session. Alternatively you could recreate os during each iteration as well but that seems a bit redundant. ;)

Edit #1

I'm not familiar with the usage of bytes, but I have been able to parse the output of my commands with a similar while loop. I Also added stderr to the input to ensure the buffer is emptied when needed. Upon first glance at your new code, it appears you are defining a new String during each iteration of the while loop, instead of appending the line to a variable. This would cause your variable to be overwritten during each iteration. ;)

Below is some code I've used in the past to accomplish similar tasks. I quickly modified from my source but I am currently unable to give it a test run. Keep in mind that your os is my stdout, and your ins is my stdin.

Runtime          terminal = (Runtime) Runtime.getRuntime();
Process          process  = terminal.exec("su");
DataOutputStream stdout   = new DataOutputStream(process.getOutputStream());
InputStream      stderr   = process.getErrorStream();
BufferedReader   stdin    = new BufferedReader(new InputStreamReader(stderr));
String           line     = "";
String           output   = "";

// Execute command and flush 
stdout.writeBytes("your command here\n");
stdout.flush();

// Append stdin.readLine() to output variable until line is null
while ((line = stdin.readLine()) != null)
    output += line;

// I've had mixed luck getting waitFor() to work, but technically it should force
// the shell to wait until the command completes. Uncomment it to try it out.
//process.waitFor();
stdout.writeBytes("exit\n");
stdout.flush();

// Print output to logcat
System.out.println(output);
AWainb
  • 868
  • 2
  • 13
  • 27
  • I need to get on here and update my code I have got it a little better since this post. I now get most of the output. Will update and yes I have ended up putting most of this outside of the loop. Will update my code if you have anymore ideas let me know however not working on this project again for sometime. – SatanEnglish May 14 '14 at 04:55
  • "It appears you are defining a new String during each iteration" Where is this the case? There is concatenating on the string st "st **'+='** new String(bc);". Or are you meaning somewhere else? Either way I'll have a try at re-factoring what you have here and see if it will work for me when I next have time Thanks – SatanEnglish Jun 03 '14 at 21:09