Monday, October 24, 2011

Android application runs shell command/script using Runtime.exec().


To run shell commands in android application code, we can use  the java function Runtime.exec();
e.g. Runtime.getRuntime().exec("cat ./example.txt");
Using Runtime.exec(), we can run most of the shell commands. However, if your commands include I/O redirection(like '<','>','|'(pipe) etc.), then you might fail with this function because it does not run in the shell of your android linux system. To enable I/O redirection in your commands, you should explicitly invoke the shell in which you want to run your commands.
e.g. String[] cmds={"/system/bin/sh","-c","echo 123 > /sdcard/example.txt"};
        Runtime.getRuntime().exec(cmds"cat ./example.txt");
       Note that here I use String[] instead of String because the Runtime.exec() function separates your command string into several parts by spaces and denotes those parts as parameters to the command. If you write your code like
        String cmd="/system/bin/sh -c echo 123 > /sdcard/example.txt";
        Runtime.getRuntime().exec(cmd);
Then the shell will get "-c","echo","123",">","/sdcard/example.txt" and treats "echo" as its parameter so the redirection will not work.
Some commands are only appropriate for super user, i.e. root which means you need root privilege to run them. If this is the case, you have to have a rooted phone at first, and run "su" before your commands.
e.g.
public static void doCmds(List<String> cmds) throws Exception {

    Process process = Runtime.getRuntime().exec("su");
    DataOutputStream os = new DataOutputStream(process.getOutputStream());

    for (String tmpCmd : cmds) {
            os.writeBytes(tmpCmd+"\n");
    }

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

    process.waitFor();
}    

Here after Runtime.getRuntime().exec("su") you get a new process called "su", then you get the input of this new process by process.getOutputStream().Yes! Get input by process.getOutputStream() instead of process.getInputStream() . Then you can write your commands into "su"'s input stream ending with "\n", which informs "su" that it should finish the command. After that, write the command "exit" into "su" and the newly created process will terminate. Note, use process.waitFor() to wait for the process terminates and you can get its exit value stored in the variable process.
This works fine for one command each time. What if you want run multiple commands that needs root privilege? Whenever you initiate a "su" process, your android phone will always pop up a toast window to ask if you would like to give super user privilege to this application and even if you clicked "Agree and Remember", you have to make another choice and click one more time at the next time you open a "su" process just inside this very application.... Well, you can just write your command into the input stream of "su" ending with "\n", after the previous command is done, write your next command into the input stream and continue this process for the rest of your commands. After all commands done, write "exit\n" to the input stream and the "su" process will terminate.

4 comments:

  1. Is possibile insert this code on a button? I mean when i click the button start the code (for example su or ls)..

    ReplyDelete
    Replies
    1. case OPTION_MENU_PANIC_ID:
      /**
      *
      * example PANIC button for immediately shutdown
      * Rooted needed.
      */
      AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

      // Setting Dialog Title
      alertDialog.setTitle("PANIC??");

      // Setting Dialog Message
      alertDialog.setMessage("Are you sure you want to shutdown the phone now?");

      // Setting Icon to Dialog
      alertDialog.setIcon(R.drawable.panic);

      // Setting Positive "Yes" Button
      alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog,int which) {

      // Write your code here to invoke YES event
      Toast.makeText(getApplicationContext(), "Shutdown", Toast.LENGTH_SHORT).show();
      try {
      Runtime.getRuntime().exec(new String[]{"su","-c","reboot -p"});
      } catch (IOException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
      }
      }
      });

      // Setting Negative "NO" Button
      alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
      // Write your code here to invoke NO event
      Toast.makeText(getApplicationContext(), "Cancelled", Toast.LENGTH_SHORT).show();
      dialog.cancel();
      }
      });

      // Showing Alert Message
      alertDialog.show();
      /**

      **/
      return true;

      Delete
  2. How can we get the results of commands ?

    ReplyDelete
  3. How to execute set command on phone home path?

    ReplyDelete