Saturday, March 22, 2008

Automating port redirection in Android

If you are developing an application that uses port redirection you should open the emulator's console and issue the redir commands every time you restart the emulator. This is documented by Google in android emulator page.
To redirect port you need to
telnet localhost 5554

to open the emulator's console and then add the desired redirection commands, like
redir add tcp:5000:5000

But, is there another way than typing the commands every time ?
It's much likely that you make a mistake and wonder why your application stopped working until you realize your typing mistake.
I couldn't find any other documented way of automating port redirection, so I decided to go my way.

Python to the rescue
This is clearly a task for expect, but instead of trying to remember how to program TCL I decided to go with the python alternative: pexpect.

The script is named, pretty obviously, android-redir.py:

Download the script from here.

#!/usr/bin/env python
"""
$Id$
"""

__license__ = """

Copyright (C) 2008 Diego Torres Milano

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA
"""

__name__ = 'android port redirection'
__version__ = '0.3'
__rev__ = '$Revision$'

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# redirections can be added to ~/android/redir.conf, one per line,
# as an example, 'tcp:5000:6000' will route any packet sent to the host's
# TCP port 5000 to TCP port 6000 of the emulated device
#
# default emulated device:
host = 'localhost'
port = 5554
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import sys
import os
from optparse import OptionParser
import pexpect

usage = "usage: %prog [options] [{tcp|udp}:hostport:emuport] ..."

parser = OptionParser(usage=usage, version="%prog " + __version__)
parser.add_option("-v", "--verbose", dest="verbose", default=False,
action="store_true",
help="verbose output [default: %default]")
parser.add_option("", "--host", dest="host", default=host,
help="HOST where emulator is running [default: %default]")
parser.add_option("-p", "--port", dest="port", default=port,
help="emulator console PORT [default: %default]")
parser.add_option("-n", "--dry-run", dest="dry_run", default=False,
action="store_true",
help="Don't execute the commands")
(options, args) = parser.parse_args()

redirs = []
cmd = 'telnet %s %d' % (options.host, options.port)

try:
for redir in open(os.path.expanduser("~") + "/.android/redir.conf"):
redirs.append(redir[:-1])
except:
pass

for redir in args:
redirs.append(redir)

if options.verbose:
print cmd
if len(redirs) > 0:
print "adding redirections:"
for redir in redirs:
print "\t" + redir

if options.dry_run:
sys.exit(0)

if len(redirs) == 0:
print >>sys.stderr, "ERROR: redirection list is empty."
sys.exit(1)

child = pexpect.spawn(cmd)
child.expect("OK")

for redir in redirs:
child.sendline("redir add " + redir)
r = child.expect(["OK", "KO: host port already active", "KO:.*"])
if r == 2:
raise Exception(child.after)

if options.verbose:
child.sendline("redir list")
child.expect("OK")
print child.before

child.sendline("quit")


Usage
This is the script help showing its usage, accessible by
$ android-redir.py --help
Usage: android-redir.py [options] [{tcp|udp}:hostport:emuport] ...

Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose output [default: False]
--host=HOST HOST where emulator is running [default: localhost]
-p PORT, --port=PORT emulator console PORT [default: 5554]
-n, --dry-run Don't execute the command


You can also create ~/.android/redir.conf file and add the redirections you want, one per line
tcp:5000:5000
udp:50001:50001


and they will be read every time the script is run.


Next time we will be looking at how to integrate this with the emulator to run both commands at the same time.

1 comment:

Diego Torres Milano said...

I'm commenting myself.
There's another way of doing the redirections

adb forward <local> <remote>


but it seems that you can't list these redirections, they don't appear in


redir list


and adb has no option to list the forwarded ports.
Or is it hidden somewhere else ?