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:
 # 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.  
 # 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=   # 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.  
 sleep 1  
 show() {  
 # Display status of traffic control status.  
 $TC -s qdisc ls dev $IF  
 case "$1" in  
 echo -n "Starting bandwidth shaping: "  
 echo "done"  
 echo -n "Stopping bandwidth shaping: "  
 echo "done"  
 echo -n "Restarting bandwidth shaping: "  
 echo "done"  
 echo "Bandwidth shaping status for $IF:"  
 echo ""  
 echo "Usage: tc.bash {start|stop|restart|show}"  
 exit 0  

Put this script in a file called, 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/ 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

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.