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.

6 comments:

  1. Tried running this script on a rooted Huawei U8652. I get a long list of not founds and then at the end it says /sdcard/limitBandwidth.sh: 67: Syntax error: expecting "in". Does this mean it's not possible to use tc on my phone? I have tc in my system/bin directory. Thanks.

    ReplyDelete
  2. Hello i have tried to run the script using android terminal emulator and i have encountered the following error messages:
    : notfoundper.sh[18]:
    ' unexpecteder.sh[35]: syntax error: '
    I am running this on the Hyundai Hold x machine with android 4.1.1
    P.S the device is rooted.
    Thanks

    ReplyDelete
  3. Worked on a MT6589 (Android 4.4) platform, with busybox installed

    ReplyDelete
  4. Worked for me. Not the 0.0.0.0 part but the rest was fine. I changed it to the address to which I really needed the bandwidth to be slow, and it worked. Thx!!
    (Samsung S2+, Cyanogenmod 12.1)

    ReplyDelete
  5. /sdcard/limitBandwidth.sh[1]: es: not found
    i see below error
    Starting bandwidth shaping: RTNETLINK answers: No such file or directory
    RTNETLINK answers: Invalid argument
    We have an error talking to the kernel
    done

    ReplyDelete