Initial checkin of atest package of tools.

Change-Id: I2b0768f5a8b30fcbb4c8dc828e76d5809fb20155
diff --git a/android.py b/android.py
index ca7eaf4..1fcf06c 100644
--- a/android.py
+++ b/android.py
@@ -1,3 +1,4 @@
+# python3.4
 # Copyright (C) 2009 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -12,62 +13,133 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-__author__ = 'Damon Kohler <damonkohler@gmail.com>'
+"""
+JSON RPC interface to android scripting engine.
+"""
 
-import collections
-import json
+__author__ = 'Keith Dart <dart@google.com>'
+__oldauthor__ = 'Damon Kohler <damonkohler@gmail.com>'
+
 import os
+import json
 import socket
-import sys
 
-PORT = os.environ.get('AP_PORT')
-HOST = os.environ.get('AP_HOST')
-HANDSHAKE = os.environ.get('AP_HANDSHAKE')
-Result = collections.namedtuple('Result', 'id,result,error')
 
+HOST = os.environ.get('AP_HOST', None)
+PORT = os.environ.get('AP_PORT', 9999)
+
+
+class SL4AException(Exception):
+    pass
+
+class SL4AAPIError(SL4AException):
+    pass
+
+class SL4AProtocolError(SL4AException):
+    pass
+
+
+def IDCounter():
+    i = 0
+    while True:
+        yield i
+        i += 1
 
 class Android(object):
+    COUNTER = IDCounter()
 
-  def __init__(self, cmd='initiate', uid=-1, port=None, addr=None):
-    self.PORT = os.environ.get('AP_PORT')
-    if port is not None:
-      self.PORT = port
-    if addr is None:
-      addr = HOST, self.PORT
-    self.conn = socket.create_connection(addr)
-    self.client = self.conn.makefile()
-    self.id = 0
-    handshake = {'cmd':cmd,'uid':uid}
-    self.client.write(json.dumps(handshake)+'\n')
-    self.client.flush()
-    resp = self.client.readline()
-    #print(resp)
-    result = json.loads(resp)
-    self.uid = uid
-    if result['status']:
-      self.uid = result['uid']
-    #self._authenticate(HANDSHAKE)
+    def __init__(self, cmd='initiate', uid=-1, port=PORT, addr=HOST):
+        conn = socket.create_connection((addr, port))
+        self.uid = None
+        self.client = conn.makefile(mode="brw")
+        handshake = {'cmd':cmd, 'uid':uid}
+        self.client.write(json.dumps(handshake).encode("utf8")+b'\n')
+        self.client.flush()
+        resp = self.client.readline()
+        if not resp:
+            raise SL4AProtocolError("No response from handshake.")
+        result = json.loads(str(resp, encoding="utf8"))
+        if result['status']:
+          self.uid = result['uid']
+        else:
+          self.uid = -1
 
-  def _rpc(self, method, *args):
-    data = {'id': self.id,
-            'method': method,
-            'params': args}
-    request = json.dumps(data)
-    #print(request)
-    self.client.write(request+'\n')
-    self.client.flush()
-    response = self.client.readline()
-    self.id += 1
-    print("Response: "+str(response))
-    result = json.loads(response)
-    if result['error'] is not None:
-      print(result['error'])
-    # namedtuple doesn't work with unicode keys.
-    return Result(id=result['id'], result=result['result'],
-                  error=result['error'], )
+    def _rpc(self, method, *args):
+        apiid = next(Android.COUNTER)
+        data = {'id': apiid,
+                    'method': method,
+                    'params': args}
+        request = json.dumps(data)
+        self.client.write(request.encode("utf8")+b'\n')
+        self.client.flush()
+        response = self.client.readline()
+        result = json.loads(str(response, encoding="utf8"))
+        if result['error']:
+            raise SL4AAPIError(result['error'])
+        if result['id'] != apiid:
+            raise SL4AProtocolError("Mismatched API id")
+        return result['result']
 
-  def __getattr__(self, name):
-    def rpc_call(*args):
-      #print(args)
-      return self._rpc(name, *args)
-    return rpc_call
+    def __getattr__(self, name):
+        def rpc_call(*args):
+            return self._rpc(name, *args)
+        return rpc_call
+
+
+def start_forwarding(port, localport=PORT):
+    os.system("adb forward tcp:{} tcp:{}".format(localport, port))
+
+
+def android(argv):
+    import getopt
+
+    def _usage():
+        print("""Usage: android -p <port> [-l <localport>] [-u <uid>] <apicall> [<apiargs>...]
+            Example: android.py -p 56241 makeToast hello""")
+
+    localport = PORT
+    port = 0
+    uid = -1
+    cmd = "initiate"
+    try:
+        opts, args = getopt.getopt(argv[1:], "l:p:u:", ["localport=", "port=", "uid="])
+    except getopt.GetoptError:
+        _usage()
+        return
+    for opt, optarg in opts:
+        if opt in ("-p", "--port"):
+            try:
+                port = int(optarg)
+            except ValueError:
+                _usage()
+                return
+        if opt in ("-u", "--uid"):
+            try:
+                uid = int(optarg)
+            except ValueError:
+                _usage()
+                return
+            cmd = "continue"
+
+    if not args:
+        _usage()
+        return
+
+    if not port:
+        _usage()
+        return
+
+    if cmd == "initiate":
+        start_forwarding(port, localport)
+
+    a = Android(cmd=cmd, uid=uid, port=localport)
+    print ("UID:", a.uid)
+    rpc = getattr(a, args[0])
+    rpc(*args[1:])
+
+
+# Run as top-level script for handy test utility
+if __name__ == "__main__":
+    import sys
+    android(sys.argv)
+