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.

Sunday, October 16, 2011

Eclipse: Launch failed. Binary not found![Ubuntu Only]

To solve this problem, double check if you have installed gcc/g++ on your machine or not. If not, sudo apt-get install gcc. If so, remember clicking the hammer icon (build project) before you try to run your program.

Tuesday, October 11, 2011

How to add a new entry into grub2 of ubuntu

Latest versions of ubuntu use grub2 instead of menu.lst to manage boot options. In grub2, all the boot items are stored in the directory "/etc/grub.d/". Open this directory, we can find some default boot items like: 00_header        10_linux       30_os-prober, etc. Following the instructions in the README file, we can add our own boot item just use the prefix "40_" or larger number. The number of this prefix determines in which order our boot item will be loaded. For eg. we can create a new file titled "40_linux-2.6.34" which means a boot item using the kernel of version 2.6.34.

In the new created file, we can add the boot information following the instruction of the default "40_custom". It is like this,

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.
echo "Adding Custom Kernel & SystemRescue" >&2
cat << EOF
menuentry "linux 2.6.34" {
set root=(hd0,1)
linux /boot/vmlinuz-2.6.34 root=/dev/sda1
initrd /boot/initrd.img-2.6.34
}
There are three highlighted lines of this file.
  • In the first line, we specify the location of the kernel image. "hd0" here is the drive name and "1" indicates the image is stored in the first (NOT SECOND) partition.
  • In the second line, we specify the directory in which the image file are located and again, specify the root location. Note here we use /dev/sda1 instead of hd0,1 again. If you are compiling a new kernel, use the command "make bzImage" to create a image for your compiled kernel.
  • In the third line, we specify the location of the initrd file, which includes information of all drivers the new kernel uses. Upon finishing compiling your kernel, use the command "mkintrd -o /boot/initrd.img-kernelname" to make an initrd file. In some new version of ubuntu, this command does not work and then you might have use "mkiniramfs" instead of "mkinitrd". These two commands work in most cases but sometimes while trying boot from your own item, you might get error like this "modprobe: FATAL: Could not load /lib/modules/2.6.8.custem/modules.deb, no such file or directory.". Then you could use another wonderful command "update-initramfs -c -k 2.6.20" by which you could update your owned created initrd file.
  • -c says: create (a inintramfs)
    -k XYZ says, for whitch Kernel the initramfs should be created.
After fixing all annoying booting problems, you could boot your ubuntu from your own boot item.