Wednesday, September 18, 2013

Limiting bandwidth on a rooted Android phone

While developing Android applications that use network communication it is often required to test the app in poor network conditions. Android emulator has an option -netdelay with which you can tell emulator to simulate gprs, edge, 3g or similar network conditions. The main problem with using emulator for simulating poor network conditions is that Android emulator is slow. Second problem is that you cannot use it to take pictures with camera. And what if you want to create an app that takes an picture and uploads it to a web server? You can only test such app on a real device. And how can you simulate poor network conditions on a real device?

Well, if you have SIM card in your phone, you can restrict it to use only gprs/edge. But if you have multiple phones for development and neither of them has SIM card in it? Or even better, what if you want to test against local server running on your computer? You need to limit Wi-Fi bandwidth somehow.

Fortunately, this is possible if you have a rooted phone. Android is essentially a Linux, and Linux has a tool called tc. Tc is short for traffic control, and this is exactly what it does. You can find numerous tutorials all over the internet how to use tc to optimise your server. I've found two such a tutorials on this site and this site. Solution from one site did the trick for me with upload limiting, and solution from other did the trick for download limiting. Therefore, I've made a hybrid script so it worked for me.

Here is it:
 #!/system/bin/bash  
 #  
 # tc uses the following units when passed as a parameter.  
 # kbps: Kilobytes per second  
 # mbps: Megabytes per second  
 # kbit: Kilobits per second  
 # mbit: Megabits per second  
 # bps: Bytes per second  
 #    Amounts of data can be specified in:  
 #    kb or k: Kilobytes  
 #    mb or m: Megabytes  
 #    mbit: Megabits  
 #    kbit: Kilobits  
 # To get the byte figure from bits, divide the number by 8 bit  
 #  
 #  
 # Name of the traffic control command.  
 TC=tc  
 # The network interface we're planning on limiting bandwidth.  
 IF=wlan0       # Interface  
 # Download limit (in mega bits)  
 DNLD=1mbit     # DOWNLOAD Limit  
 # Upload limit (in mega bits)  
 UPLD=1mbit     # UPLOAD Limit  
 # IP address of the machine we are controlling  
 IP=192.168.5.14   # Host IP  
 start() {  
 # Limit download  
 $TC qdisc add dev $IF handle ffff: ingress                  
 $TC filter add dev $IF parent ffff: protocol ip prio 50 u32 match ip src $IP/32 police rate $DNLD burst 100k drop flowid :1  
 # Limit upload  
 $TC qdisc add dev $IF root handle 1: htb default 30  
 $TC class add dev $IF parent 1: classid 1:1 htb rate $UPLD  
 $TC filter add dev $IF protocol ip parent 1:0 prio 1 u32 match ip dst $IP/32 flowid 1:1  
 }  
 stop() {  
 # Stop the bandwidth shaping.  
 $TC qdisc del dev $IF ingress  
 $TC qdisc del dev $IF root  
 }  
 restart() {  
 # Self-explanatory.  
 stop  
 sleep 1  
 start  
 }  
 show() {  
 # Display status of traffic control status.  
 $TC -s qdisc ls dev $IF  
 }  
 case "$1" in  
 start)  
 echo -n "Starting bandwidth shaping: "  
 start  
 echo "done"  
 ;;  
 stop)  
 echo -n "Stopping bandwidth shaping: "  
 stop  
 echo "done"  
 ;;  
 restart)  
 echo -n "Restarting bandwidth shaping: "  
 restart  
 echo "done"  
 ;;  
 show)  
 echo "Bandwidth shaping status for $IF:"  
 show  
 echo ""  
 ;;  
 *)  
 pwd=$(pwd)  
 echo "Usage: tc.bash {start|stop|restart|show}"  
 ;;  
 esac   
 exit 0  

Put this script in a file called limitBandwidth.sh, and transfer the file on your rooted phone (for example by using adb). I've placed it in folder /sdcard/.

So now, type adb shell to enter device's shell and execute following command:
su -c "sh /sdcard/limitBandwidth.sh start". After that your phone should have limited bandwidth towards the IP address you set in the script. If you want to limit bandwidth towards all addresses, just set IP to 0.0.0.0.

In my opinion it is a better option to limit bandwidth only towards your development machine that runs the test server. You can then use the phone normally for browsing and streaming videos without any limits in bandwidth whilst all requests towards your local machine will have limited bandwidth.

I've tested the script on Samsung Galaxy S2 with CyanogenMod 10.3. If you find it working on other rooted phone, please tell in comments that it works.

If it doesn't work, tell in comments that it doesn't work and attach an error. I cannot promise that I could make it work on that phone (most probably I would not), but if I get my hands on such phone, I will definitely investigate why it is not working.

Monday, March 11, 2013

Android native code debugging with Eclipse

OK, this has been covered in numerous blogs and forum posts, but I will post this tutorial for multiple reasons. The first reason is that this will be a reminder for me, and the second one is the fact that all tutorials cover the steps for debugging android application projects that have native part; but what about debugging android library project that has native part? What if you have a situation like this: you have an android library project that has native part and you have pure java-based android application project which uses the library project and you have bug in your native part. How to debug this? Well, you can always add native support to your application project and then make the debugging, but why doing that, if there is an easier way to solve this?

For this tutorial I will assume that you have the latest version of Eclipse, Android SDK and Android NDK installed. In my case, eclipse is at version Juno, Android SDK is at revision 21, and I'm using NDK r8d. Of course, this tutorial should work with earlier versions of NDK and SDK, but I haven't tested that.

OK, let's start:
First, you need to adapt the ndk-gdb script which is located in Android NDK dir. Create a copy of that script; let's call it ndk-gdb-eclipse. Open the copied file in your favorite editor and find the following snippet:


    PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    log "Found package name: $PACKAGE_NAME"
    if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
        echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
        echo "       Please check that the file is well-formed!"
        exit 1
    fi

Now, edit this so it will look like this:

if [[ -z "$PACKAGE_NAME" ]]; then
    PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    log "Found package name: $PACKAGE_NAME"
    if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
        echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
        echo "       Please check that the file is well-formed!"
        exit 1
    fi
else
    log "Using predefined package: $PACKAGE_NAME"
fi

OK, what does that do? This code actually checks if the variable PACKAGE_NAME is set, and if it is not set, it sets it to the name of the package for Android project in current directory. This works fine if you are trying to debug an application, but if you try to debug Android library project, the found package name will be wrong (the name of the package in the library does not need to be the same as the name of the package of application that uses the library).

OK, don't close the text editor yet, there is a still one little change that needs to be done. Scroll to the end of the script. The last line should be as this:
$GDBCLIENT -x `native_path $GDBSETUP`
Comment-out this line by prefixing it with #. The changed line should look like this:

#$GDBCLIENT -x `native_path $GDBSETUP` 
 OK, now save the file - the hard hacker-style work is done. Actually not yet, we will still write a little helper bash script which is actually not needed, but vastly eases the later usage. So, open your editor and put the following code inside it:

 #!/bin/bash
 # activate remote port to pipe forwarding
 PACKAGE_NAME=$1

 if [[ -z "$PACKAGE_NAME" ]]; then
  echo "No package name defined :-("
 else
  export PACKAGE_NAME
  ndk-gdb-eclipse --force --verbose
 fi

Save the file as "android-debug" and make it executable with chmod +x android-debug.

Next, you should build your native code with ndk-build NDK_DEBUG=1, which will enable native debugging. The easiest way to ensure that your native code is always built with debugging support is to make eclipse do that for you. Right click the project that contains native code, choose properties, click the C/C++ Build tab, uncheck "Use default build command" and add NDK_DEBUG=1 to build command (see the picture).

Example of C/C++ build settings for android library project with native code
OK, now connect your phone or start android emulator and start the application project which will use the library project you are about to debug. You can start the application by either selecting "Run" or "Debug". If you select "Run", you will be able to debug only the native part, and if you select "Debug", you will be able to debug both the native and the java part of your project.

Just to be sure that everything will work, add android:debuggable="true" to application tag in your AndroidManifest.xml. In my case, this is required only for debugging of java part of library project, but not for native debug.

OK, the application is now started on your phone, but before being able to debug the native part, you still have to perform some steps. First, open your terminal and navigate to root folder of library project that contains the native code. Run the following command:
android-debug test.application.packagename
So, "android-debug" is the script that you have created earlier, and test.application.packagename is the name of the package of application that uses the library project (not the package of library project). For example, if your library project is in package com.test.library and your application is in package net.cool.application, you should run "android-debug net.cool.application".
The script will then run the gdbserver on phone that will attach to the process of application you started. Now, all you have to do is connect your eclipse to that gdbserver and debugging can start. Warning, on some phones, deploying of gdbserver may not succeed. Unfortunately, on such phones, debugging the native code is not possible.

To connect your eclipse to the gdbserver on the phone do the following: open the Debug Configurations dialog in Eclipse (little arrow next to the button for starting the debug, and choosing "Debug Configurations..." option.

Create new C/C++ Remote Application launch configuration as seen on the following screenshot:


If you don't have C/C++ Remote Application launch configuration option, then you should install C/C++ hardware debug support from CDT update site.
In C/C++ application field enter the full path to app_process file that will be found in libraries ./obj/local/armeabi folder. If you can't find that file, that means that android-debug script has failed.

Next, click the debugger tab and enter the full path to arm-linux-androideabi-gdb file that is found in android-ndk-dir/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/ folder (I'm not sure where this is found in windows or mac, but it should be similar) to the GDB debugger field and into the GDB command file put the full path to the gdb.setup file that was created by ndk-build script (it should be found next to .so file in libs/armeabi folder). See the next screenshot for example:


Finally, open the Connection tab and set the port number to 5039.


And that is all. Click the Debug button and start putting breakpoints to your native code. When phone hits them, the execution will be suspended and you will be able to examine variables, do steps and everything else you are used to do when debugging C/C++ code with eclipse CDT.

I hope this wasn't too complicated...