adding multi-session support

Change-Id: I9c41caff0c5845ba01e8d25dc793b4b956c0e02b
diff --git a/Common/src/com/googlecode/android_scripting/facade/EventServer.java b/Common/src/com/googlecode/android_scripting/facade/EventServer.java
index 432980a..3b67fa4 100644
--- a/Common/src/com/googlecode/android_scripting/facade/EventServer.java
+++ b/Common/src/com/googlecode/android_scripting/facade/EventServer.java
@@ -20,6 +20,7 @@
 import com.googlecode.android_scripting.event.Event;
 import com.googlecode.android_scripting.jsonrpc.JsonBuilder;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.InetSocketAddress;
@@ -64,6 +65,7 @@
 
   @Override
   protected void handleConnection(Socket socket) throws IOException {
+    Log.d("handle event connection.");
     Listener l = new Listener(socket);
     Log.v("Adding EventServer listener " + socket.getPort());
     mListeners.add(l);
@@ -116,4 +118,9 @@
       out = new PrintWriter(l.getOutputStream(), true);
     }
   }
+
+  @Override
+  protected void handleRPCConnection(Socket sock, Integer UID, BufferedReader reader, PrintWriter writer)
+      throws Exception {
+  }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/FacadeManagerFactory.java b/Common/src/com/googlecode/android_scripting/facade/FacadeManagerFactory.java
index 4bde600..2557cd8 100644
--- a/Common/src/com/googlecode/android_scripting/facade/FacadeManagerFactory.java
+++ b/Common/src/com/googlecode/android_scripting/facade/FacadeManagerFactory.java
@@ -7,9 +7,9 @@
 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManagerFactory;
 
-import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Collection;
-import java.util.List;
+import java.util.Map;
 
 public class FacadeManagerFactory implements RpcReceiverManagerFactory {
 
@@ -17,7 +17,7 @@
   private final Service mService;
   private final Intent mIntent;
   private final Collection<Class<? extends RpcReceiver>> mClassList;
-  private final List<RpcReceiverManager> mFacadeManagers;
+  private final Map<Integer, RpcReceiverManager> mFacadeManagers;
 
   public FacadeManagerFactory(int sdkLevel, Service service, Intent intent,
       Collection<Class<? extends RpcReceiver>> classList) {
@@ -25,17 +25,18 @@
     mService = service;
     mIntent = intent;
     mClassList = classList;
-    mFacadeManagers = new ArrayList<RpcReceiverManager>();
+    mFacadeManagers = new HashMap<Integer, RpcReceiverManager>();
   }
 
-  public FacadeManager create() {
+  @Override
+  public FacadeManager create(Integer UID) {
     FacadeManager facadeManager = new FacadeManager(mSdkLevel, mService, mIntent, mClassList);
-    mFacadeManagers.add(facadeManager);
+    mFacadeManagers.put(UID, facadeManager);
     return facadeManager;
   }
 
   @Override
-  public List<RpcReceiverManager> getRpcReceiverManagers() {
+  public Map<Integer, RpcReceiverManager> getRpcReceiverManagers() {
     return mFacadeManagers;
   }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/ScanFacade.java b/Common/src/com/googlecode/android_scripting/facade/ScanFacade.java
index 82975d1..84a4848 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ScanFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ScanFacade.java
@@ -11,6 +11,7 @@
 import com.googlecode.android_scripting.MainThread;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcOptional;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 import com.googlecode.android_scripting.rpc.RpcStartEvent;
 import com.googlecode.android_scripting.rpc.RpcStopEvent;
@@ -20,14 +21,13 @@
 
 /**
  * ScanManager functions.
- *
  */
 public class ScanFacade extends RpcReceiver {
   private final Service mService;
   private final EventFacade mEventFacade;
   private final WifiScanner mScan;
-  // These counters are just for indexing;
-  // they do not represent the total number of listeners
+  //These counters are just for indexing;
+  //they do not represent the total number of listeners
   private static int WifiScanListenerCnt;
   private static int WifiChangeListenerCnt;
   private static int WifiHotspotListenerCnt;
@@ -52,10 +52,10 @@
     protected String listenerType;
 
     public WifiActionListener(String type, int idx, Bundle statusBundle, Bundle resultBundle) {
-      index = idx;
-      listenerType = type;
-      mStatus = statusBundle;
-      mResults = resultBundle;
+      this.index = idx;
+      this.listenerType = type;
+      this.mStatus = statusBundle;
+      this.mResults = resultBundle;
     }
 
     @Override
@@ -81,28 +81,25 @@
     public void reportResult(ScanResult[] results, String eventType) {
       Log.d("android_scripting " + eventType + " " + listenerType + index);
       mResults.putString("ID", listenerType + index);
+      mResults.putLong("Timestamp", System.currentTimeMillis()/1000);
       mResults.putString("Type", eventType);
       mResults.putParcelableArray("Results", results);
-      mResults.putLong("Time", System.currentTimeMillis());
       mEventFacade.postEvent("ScanResults", mResults.clone());
       mResults.clear();
     }
-
   }
 
   /**
    * Constructs a wifiScanListener obj and returns it
-   *
    * @return WifiScanListener
    */
   private WifiScanListener genWifiScanListener() {
-    WifiScanListener mWifiScannerListener =
-        MainThread.run(mService, new Callable<WifiScanListener>() {
-          @Override
-          public WifiScanListener call() throws Exception {
-            return new WifiScanListener();
-          }
-        });
+    WifiScanListener mWifiScannerListener = MainThread.run(mService, new Callable<WifiScanListener>() {
+      @Override
+      public WifiScanListener call() throws Exception {
+        return new WifiScanListener();
+      }
+    });
     wifiScannerListenerList.put(mWifiScannerListener.index, mWifiScannerListener);
     return mWifiScannerListener;
   }
@@ -161,7 +158,6 @@
 
   /**
    * Constructs a ChangeListener obj and returns it
-   *
    * @return ChangeListener
    */
   public ChangeListener genWifiChangeListener() {
@@ -198,38 +194,29 @@
     public void onFailure(int reason, Object exception) {
       mWAL.onFailure(reason, exception);
     }
-
-    /**
-     * indicates that changes were detected in wifi environment
-     *
-     * @param results
-     *          indicate the access points that exhibited change
+    /** indicates that changes were detected in wifi environment
+     * @param results indicate the access points that exhibited change
      */
     @Override
-    public void onChanging(ScanResult[] results) { /* changes are found */
+    public void onChanging(ScanResult[] results) {           /* changes are found */
       mWAL.reportResult(results, "onChanging");
     }
-
-    /**
-     * indicates that no wifi changes are being detected for a while
-     *
-     * @param results
-     *          indicate the access points that are bing monitored for change
+    /** indicates that no wifi changes are being detected for a while
+     * @param results indicate the access points that are bing monitored for change
      */
     @Override
-    public void onQuiescence(ScanResult[] results) { /* changes settled down */
+    public void onQuiescence(ScanResult[] results) {         /* changes settled down */
       mWAL.reportResult(results, "onQuiescence");
     }
   }
 
   public WifiHotspotListener genWifiHotspotListener() {
-    WifiHotspotListener mWifiHotspotListener =
-        MainThread.run(mService, new Callable<WifiHotspotListener>() {
-          @Override
-          public WifiHotspotListener call() throws Exception {
-            return new WifiHotspotListener();
-          }
-        });
+    WifiHotspotListener mWifiHotspotListener = MainThread.run(mService, new Callable<WifiHotspotListener>() {
+      @Override
+      public WifiHotspotListener call() throws Exception {
+        return new WifiHotspotListener();
+      }
+    });
     wifiHotspotListenerList.put(mWifiHotspotListener.index, mWifiHotspotListener);
     return mWifiHotspotListener;
   }
@@ -268,26 +255,23 @@
 
   /**
    * Starts periodic wifi background scan
-   *
    * @param periodInMs
-   * @param channel_freqs
-   *          frequencies of channels to scan
+   * @param channel_freqs frequencies of channels to scan
    * @return the id of the scan listener associated with this scan
    */
   @Rpc(description = "Starts a periodic Wifi scan in background.")
   @RpcStartEvent("WifiScan")
   public Integer startWifiBackgroundScan(@RpcParameter(name = "periodInMs") Integer periodInMs,
-      @RpcParameter(name = "channel_freqs") Integer[] channel_freqs) {
+                                  @RpcParameter(name = "channel_freqs") Integer[] channel_freqs) {
     WifiScanner.ScanSettings ss = new WifiScanner.ScanSettings();
     ss.channels = new WifiScanner.ChannelSpec[channel_freqs.length];
-    for (int i = 0; i < channel_freqs.length; i++) {
+    for(int i=0; i<channel_freqs.length; i++) {
       ss.channels[i] = new WifiScanner.ChannelSpec(channel_freqs[i]);
     }
     ss.periodInMs = periodInMs;
     Log.d("android_scripting periodInMs " + ss.periodInMs);
-    for (int i = 0; i < ss.channels.length; i++) {
-      Log.d("android_scripting " + ss.channels[i].frequency + " " + ss.channels[i].passive + " "
-          + ss.channels[i].dwellTimeMS);
+    for(int i=0; i<ss.channels.length; i++) {
+      Log.d("android_scripting " + ss.channels[i].frequency + " " + ss.channels[i].passive + " " + ss.channels[i].dwellTimeMS);
     }
     WifiScanListener mListener = genWifiScanListener();
     mScan.startBackgroundScan(ss, mListener);
@@ -296,9 +280,7 @@
 
   /**
    * Stops a wifi background scan
-   *
-   * @param listener_index
-   *          the id of the scan listener whose scan to stop
+   * @param listener_index the id of the scan listener whose scan to stop
    */
   @Rpc(description = "Stops an ongoing periodic Wifi scan in background")
   @RpcStopEvent("WifiScan")
@@ -314,7 +296,7 @@
   public Integer[] showWifiScanListeners() {
     Integer[] result = new Integer[wifiScannerListenerList.size()];
     int j = 0;
-    for (int i : wifiScannerListenerList.keySet()) {
+    for(int i : wifiScannerListenerList.keySet()) {
       result[j] = wifiScannerListenerList.get(i).index;
       j += 1;
     }
@@ -323,7 +305,6 @@
 
   /**
    * Starts tracking wifi changes
-   *
    * @return the id of the change listener associated with this track
    */
   @Rpc(description = "Starts tracking wifi changes")
@@ -336,9 +317,7 @@
 
   /**
    * Stops tracking wifi changes
-   *
-   * @param listener_index
-   *          the id of the change listener whose track to stop
+   * @param listener_index the id of the change listener whose track to stop
    */
   @Rpc(description = "Stops tracking wifi changes")
   public void stopTrackingChange(@RpcParameter(name = "listener") Integer listener_index) {
@@ -349,31 +328,27 @@
 
   /**
    * Starts tracking changes of the wifi networks specified in a hotlist
-   *
-   * @param hotspotInfos
-   *          a 'hotlist' specifying which wifi networks to track
-   * @param apLostThreshold
-   *          signal strength below which an AP is considered lost
+   * @param hotspotInfos a 'hotlist' specifying which wifi networks to track
+   * @param apLostThreshold signal strength below which an AP is considered lost
    * @return the id of the hotlist listener associated with this track
    * @throws Exception
    */
   @Rpc(description = "Starts tracking changes of the APs on hotlist")
   public Integer setHotlist(String[] hotspotInfos, Integer apLostThreshold) throws Exception {
-    // Instantiates HotspotInfo objs
+    //Instantiates HotspotInfo objs
     HotspotInfo[] mHotspotInfos = new HotspotInfo[hotspotInfos.length];
-    for (int i = 0; i < hotspotInfos.length; i++) {
+    for(int i=0; i<hotspotInfos.length; i++) {
       Log.d("android_scripting " + hotspotInfos[i]);
       String[] tokens = hotspotInfos[i].split(" ");
-      if (tokens.length != 3) {
-        throw new Exception("Invalid hotspot info: " + hotspotInfos[i]);
-
+      if(tokens.length!=3) {
+        throw new Exception("Invalid hotspot info: "+hotspotInfos[i]);
       }
       int a = Integer.parseInt(tokens[1]);
       int b = Integer.parseInt(tokens[2]);
       HotspotInfo mHI = new HotspotInfo();
       mHI.bssid = tokens[0];
-      mHI.low = a < b ? a : b;
-      mHI.high = a < b ? b : a;
+      mHI.low = a<b? a:b;
+      mHI.high = a<b? b:a;
       mHotspotInfos[i] = mHI;
     }
     WifiHotspotListener mWHL = genWifiHotspotListener();
@@ -383,9 +358,7 @@
 
   /**
    * Stops tracking the hotlist associated with the input listener
-   *
-   * @param listener_index
-   *          the id of the hotspot listener whose track to stop
+   * @param listener_index the id of the hotspot listener whose track to stop
    */
   @Rpc(description = "Stops tracking changes of the APs on hotlist")
   public void resetHotlist(@RpcParameter(name = "listener") Integer listener_index) {
@@ -399,7 +372,7 @@
    */
   @Rpc(description = "Shuts down all WifiScanner activities")
   public void wifiScannerShutdown() {
-    shutdown();
+    this.shutdown();
   }
 
   /**
@@ -407,19 +380,19 @@
    */
   @Override
   public void shutdown() {
-    if (!wifiScannerListenerList.isEmpty()) {
-      for (int i : wifiScannerListenerList.keySet()) {
-        stopWifiBackgroundScan(i);
+    if(!wifiScannerListenerList.isEmpty()) {
+      for(int i : wifiScannerListenerList.keySet()) {
+        this.stopWifiBackgroundScan(i);
       }
     }
-    if (!wifiChangeListenerList.isEmpty()) {
-      for (int i : wifiChangeListenerList.keySet()) {
-        stopTrackingChange(i);
+    if(!wifiChangeListenerList.isEmpty()) {
+      for(int i : wifiChangeListenerList.keySet()) {
+        this.stopTrackingChange(i);
       }
     }
-    if (!wifiHotspotListenerList.isEmpty()) {
-      for (int i : wifiHotspotListenerList.keySet()) {
-        resetHotlist(i);
+    if(!wifiHotspotListenerList.isEmpty()) {
+      for(int i : wifiHotspotListenerList.keySet()) {
+        this.resetHotlist(i);
       }
     }
   }
diff --git a/Common/src/com/googlecode/android_scripting/facade/SettingsFacade.java b/Common/src/com/googlecode/android_scripting/facade/SettingsFacade.java
index 7fb1988..1635ffc 100644
--- a/Common/src/com/googlecode/android_scripting/facade/SettingsFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/SettingsFacade.java
@@ -84,7 +84,7 @@
   public Boolean checkAirplaneMode() {
     try {
       return android.provider.Settings.System.getInt(mService.getContentResolver(),
-          android.provider.Settings.System.AIRPLANE_MODE_ON) == AIRPLANE_MODE_ON;
+          android.provider.Settings.Global.AIRPLANE_MODE_ON) == AIRPLANE_MODE_ON;
     } catch (SettingNotFoundException e) {
       return false;
     }
@@ -96,7 +96,7 @@
       enabled = !checkAirplaneMode();
     }
     android.provider.Settings.System.putInt(mService.getContentResolver(),
-        android.provider.Settings.System.AIRPLANE_MODE_ON, enabled ? AIRPLANE_MODE_ON
+        android.provider.Settings.Global.AIRPLANE_MODE_ON, enabled ? AIRPLANE_MODE_ON
             : AIRPLANE_MODE_OFF);
     Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
     intent.putExtra("state", enabled);
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
index bb74fa1..522610d 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
@@ -295,8 +295,7 @@
     return result;
   }
 
-  private static JSONObject buildNeighboringCellInfo(NeighboringCellInfo data)
-      throws JSONException {
+  private static JSONObject buildNeighboringCellInfo(NeighboringCellInfo data) throws JSONException {
     // TODO(damonkohler): Additional information available at API level 5.
     JSONObject result = new JSONObject();
     result.put("cid", data.getCid());
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonRpcServer.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonRpcServer.java
index 94324d8..73171dc 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonRpcServer.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonRpcServer.java
@@ -57,18 +57,33 @@
   public void shutdown() {
     super.shutdown();
     // Notify all RPC receiving objects. They may have to clean up some of their state.
-    for (RpcReceiverManager manager : mRpcReceiverManagerFactory.getRpcReceiverManagers()) {
+    for (RpcReceiverManager manager : mRpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
       manager.shutdown();
     }
   }
 
+
   @Override
-  protected void handleConnection(Socket socket) throws Exception {
-    RpcReceiverManager receiverManager = mRpcReceiverManagerFactory.create();
+  protected void handleRPCConnection(Socket sock, Integer UID, BufferedReader reader, PrintWriter writer) throws Exception {
+    RpcReceiverManager receiverManager = null;
+    //Log.d("Sock state 3: "+sock.isClosed());
+    Log.d("UID "+UID);
+    Log.d("manager map size: "+mRpcReceiverManagerFactory.getRpcReceiverManagers().size());
+    Log.d("manager map keys: "+mRpcReceiverManagerFactory.getRpcReceiverManagers().keySet());
+    if(mRpcReceiverManagerFactory.getRpcReceiverManagers().containsKey(UID)) {
+      Log.d("Look up existing session");
+      receiverManager = mRpcReceiverManagerFactory.getRpcReceiverManagers().get(UID);
+    }else{
+      Log.d("Create a new session");
+      receiverManager = mRpcReceiverManagerFactory.create(UID);
+    }
+    //Log.d("Sock state 4: "+sock.isClosed());
+
+    /*RpcReceiverManager receiverManager = mRpcReceiverManagerFactory.create();
     BufferedReader reader =
         new BufferedReader(new InputStreamReader(socket.getInputStream()), 8192);
-    PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
-    boolean passedAuthentication = false;
+    PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);*/
+    //boolean passedAuthentication = false;
     String data;
     while ((data = reader.readLine()) != null) {
       Log.v("Received: " + data);
@@ -78,17 +93,17 @@
       JSONArray params = request.getJSONArray("params");
 
       // First RPC must be _authenticate if a handshake was specified.
-      if (!passedAuthentication && mHandshake != null) {
-        if (!checkHandshake(method, params)) {
-          SecurityException exception = new SecurityException("Authentication failed!");
-          send(writer, JsonRpcResult.error(id, exception));
-          shutdown();
-          throw exception;
-        }
-        passedAuthentication = true;
-        send(writer, JsonRpcResult.result(id, true));
-        continue;
-      }
+//      if (!passedAuthentication && mHandshake != null) {
+//        if (!checkHandshake(method, params)) {
+//          SecurityException exception = new SecurityException("Authentication failed!");
+//          send(writer, JsonRpcResult.error(id, exception));
+//          shutdown();
+//          throw exception;
+//        }
+//        passedAuthentication = true;
+//        send(writer, JsonRpcResult.result(id, true));
+//        continue;
+//      }
 
       MethodDescriptor rpc = receiverManager.getMethodDescriptor(method);
       if (rpc == null) {
@@ -116,4 +131,8 @@
     writer.flush();
     Log.v("Sent: " + result);
   }
+
+  @Override
+  protected void handleConnection(Socket socket) throws Exception {
+  }
 }
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/RpcReceiverManagerFactory.java b/Common/src/com/googlecode/android_scripting/jsonrpc/RpcReceiverManagerFactory.java
index 8ac810c..6e03078 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/RpcReceiverManagerFactory.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/RpcReceiverManagerFactory.java
@@ -16,10 +16,10 @@
 
 package com.googlecode.android_scripting.jsonrpc;
 
-import java.util.List;
+import java.util.Map;
 
 public interface RpcReceiverManagerFactory {
-  public RpcReceiverManager create();
+  public RpcReceiverManager create(Integer UID);
 
-  public List<RpcReceiverManager> getRpcReceiverManagers();
+  public Map<Integer, RpcReceiverManager> getRpcReceiverManagers();
 }
diff --git a/QuickAction/src/net/londatiga/android/QuickAction.java b/QuickAction/src/net/londatiga/android/QuickAction.java
index 532e6cf..2c1cfb4 100644
--- a/QuickAction/src/net/londatiga/android/QuickAction.java
+++ b/QuickAction/src/net/londatiga/android/QuickAction.java
@@ -19,7 +19,6 @@
 import java.util.ArrayList;

 import java.util.Arrays;

 import java.util.List;

-import net.londatiga.android.R;

 

 /**

  * Popup window, shows action list as icon and text (QuickContact / Twitter app).

diff --git a/README b/README
index ffbb891..41f9585 100644
--- a/README
+++ b/README
@@ -1,3 +1,9 @@
-This directory contains Eclipse projects related to the Scripting Layer For Android.
-
+This directory contains projects related to the Scripting Layer For Android.
 ScriptingLayerForAndroid/ contains the main SL4A project.
+
+To build, modify the BRANCH_ROOT and other paths if necessary in build_all.sh
+Plug in your device and run build_all.sh
+
+To run, add android.py to your $PYTHONPATH
+
+Note: the sl4a build is dependent on prebuilt libraries in your repo branch. So your local repo needs to be able to build first.
diff --git a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/ActivityFlinger.java b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/ActivityFlinger.java
index 17cca3c..389b55e 100644
--- a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/ActivityFlinger.java
+++ b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/ActivityFlinger.java
@@ -20,9 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.View.OnTouchListener;
 
 import com.googlecode.android_scripting.activity.InterpreterManager;
@@ -70,8 +70,8 @@
     Class<? extends Activity> current = null;
     Class<? extends Activity> right = null;
 
-    for (Iterator<Class<? extends Activity>> it = entries.iterator();
-        it.hasNext() || current != null;) {
+    for (Iterator<Class<? extends Activity>> it = entries.iterator(); it.hasNext()
+        || current != null;) {
       if (current == null) {
         current = it.next();
       }
@@ -126,8 +126,7 @@
     Runnable mRightRunnable;
 
     @Override
-    public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,
-        float velocityY) {
+    public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
       if (Math.abs(event1.getY() - event2.getY()) > SWIPE_MAX_OFF_PATH) {
         return false;
       }
diff --git a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/activity/ScriptingLayerService.java b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/activity/ScriptingLayerService.java
index b3e74b6..ce95ec5 100644
--- a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/activity/ScriptingLayerService.java
+++ b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/activity/ScriptingLayerService.java
@@ -213,7 +213,7 @@
     AndroidProxy androidProxy = new AndroidProxy(this, intent, requiresHandshake);
     boolean usePublicIp = intent.getBooleanExtra(Constants.EXTRA_USE_EXTERNAL_IP, false);
     int usePort = intent.getIntExtra(Constants.EXTRA_USE_SERVICE_PORT, 0);
-    // If port is in use, fall back to defaut behaviour
+    // If port is in use, fall back to default behaviour
     if (!tryPort(androidProxy, usePublicIp, usePort)) {
       if (usePort != 0) {
         tryPort(androidProxy, usePublicIp, 0);
diff --git a/ScriptingLayerForAndroid/src/org/connectbot/TerminalView.java b/ScriptingLayerForAndroid/src/org/connectbot/TerminalView.java
index 507a553..dfe231b 100644
--- a/ScriptingLayerForAndroid/src/org/connectbot/TerminalView.java
+++ b/ScriptingLayerForAndroid/src/org/connectbot/TerminalView.java
@@ -41,7 +41,6 @@
 /**
  * User interface {@link View} for showing a TerminalBridge in an {@link Activity}. Handles drawing
  * bitmap updates and passing keystrokes down to terminal.
- *
  * @author jsharkey
  */
 public class TerminalView extends View implements FontSizeChangedListener {
@@ -208,8 +207,9 @@
       if (bridge.isSelectingForCopy()) {
         SelectionArea area = bridge.getSelectionArea();
         canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(area.getLeft() * bridge.charWidth, area.getTop() * bridge.charHeight,
-            (area.getRight() + 1) * bridge.charWidth, (area.getBottom() + 1) * bridge.charHeight);
+        canvas.clipRect(area.getLeft() * bridge.charWidth, area.getTop() * bridge.charHeight, (area
+            .getRight() + 1)
+            * bridge.charWidth, (area.getBottom() + 1) * bridge.charHeight);
         canvas.drawPaint(cursorPaint);
         canvas.restore();
       }
@@ -239,7 +239,6 @@
 
   /**
    * Ask the {@link TerminalBridge} we're connected to to resize to a specific size.
-   *
    * @param width
    * @param height
    */
@@ -249,7 +248,6 @@
 
   /**
    * Sets the ability for the TerminalView to display Toast notifications to the user.
-   *
    * @param value
    *          whether to enable notifications or not
    */
@@ -264,8 +262,9 @@
 
   @Override
   public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-    outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_ENTER_ACTION
-        | EditorInfo.IME_ACTION_NONE;
+    outAttrs.imeOptions |=
+        EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_ENTER_ACTION
+            | EditorInfo.IME_ACTION_NONE;
     outAttrs.inputType = EditorInfo.TYPE_NULL;
     return new BaseInputConnection(this, false);
   }
diff --git a/ScriptingLayerForAndroid/src/org/connectbot/service/TerminalBridge.java b/ScriptingLayerForAndroid/src/org/connectbot/service/TerminalBridge.java
index 75c0974..9f988fd 100644
--- a/ScriptingLayerForAndroid/src/org/connectbot/service/TerminalBridge.java
+++ b/ScriptingLayerForAndroid/src/org/connectbot/service/TerminalBridge.java
@@ -825,7 +825,7 @@
   public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
     if (mProcess.isAlive()) {
       RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
-      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers()) {
+      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
         UiFacade facade = manager.getReceiver(UiFacade.class);
         facade.onCreateContextMenu(menu, v, menuInfo);
       }
@@ -836,7 +836,7 @@
     boolean returnValue = false;
     if (mProcess.isAlive()) {
       RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
-      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers()) {
+      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
         UiFacade facade = manager.getReceiver(UiFacade.class);
         returnValue = returnValue || facade.onPrepareOptionsMenu(menu);
       }
diff --git a/Utils/src/com/googlecode/android_scripting/ForegroundService.java b/Utils/src/com/googlecode/android_scripting/ForegroundService.java
index 175681c..9e289bb 100644
--- a/Utils/src/com/googlecode/android_scripting/ForegroundService.java
+++ b/Utils/src/com/googlecode/android_scripting/ForegroundService.java
@@ -4,7 +4,6 @@
 import android.app.NotificationManager;
 import android.app.Service;
 
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 public abstract class ForegroundService extends Service {
@@ -72,18 +71,6 @@
     setForeground(false);
   }
 
-  void invokeMethod(Method method, Object[] args) {
-    try {
-      method.invoke(this, args);
-    } catch (InvocationTargetException e) {
-      // Should not happen.
-      Log.e(e);
-    } catch (IllegalAccessException e) {
-      // Should not happen.
-      Log.e(e);
-    }
-  }
-
   @Override
   public void onCreate() {
     mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
diff --git a/Utils/src/com/googlecode/android_scripting/SimpleServer.java b/Utils/src/com/googlecode/android_scripting/SimpleServer.java
index 2c62beb..ed7ac89 100644
--- a/Utils/src/com/googlecode/android_scripting/SimpleServer.java
+++ b/Utils/src/com/googlecode/android_scripting/SimpleServer.java
@@ -18,7 +18,16 @@
 
 import com.google.common.collect.Lists;
 
+//import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -30,17 +39,16 @@
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * A simple server.
- * 
  * @author Damon Kohler (damonkohler@gmail.com)
  */
 public abstract class SimpleServer {
-
-  private final CopyOnWriteArrayList<ConnectionThread> mConnectionThreads =
-      new CopyOnWriteArrayList<ConnectionThread>();
+  private static int threadIndex = 0;
+  private final ConcurrentHashMap<Integer, ConnectionThread> mConnectionThreads =
+      new ConcurrentHashMap<Integer, ConnectionThread>();
   private final List<SimpleServerObserver> mObservers = Lists.newArrayList();
   private volatile boolean mStopServer = false;
   private ServerSocket mServer;
@@ -53,6 +61,10 @@
   }
 
   protected abstract void handleConnection(Socket socket) throws Exception;
+  protected abstract void handleRPCConnection(Socket socket,
+                                              Integer UID,
+                                              BufferedReader reader,
+                                              PrintWriter writer) throws Exception;
 
   /** Adds an observer. */
   public void addObserver(SimpleServerObserver observer) {
@@ -78,17 +90,32 @@
 
   private final class ConnectionThread extends Thread {
     private final Socket mmSocket;
+    private final BufferedReader reader;
+    private final PrintWriter writer;
+    private final Integer UID;
+    private final boolean isRpc;
 
-    private ConnectionThread(Socket socket) {
+    private ConnectionThread(Socket socket, boolean rpc, Integer uid, BufferedReader reader, PrintWriter writer) {
       setName("SimpleServer ConnectionThread " + getId());
       mmSocket = socket;
+      this.UID = uid;
+      this.reader = reader;
+      this.writer = writer;
+      this.isRpc = rpc;
     }
 
     @Override
     public void run() {
       Log.v("Server thread " + getId() + " started.");
       try {
-        handleConnection(mmSocket);
+        if(isRpc) {
+          Log.d("Sock state 2: "+mmSocket.isClosed());
+          Log.d("Handling RPC connection in "+getId());
+          handleRPCConnection(mmSocket, UID, reader, writer);
+        }else{
+          Log.d("Handling Non-RPC connection in "+getId());
+          handleConnection(mmSocket);
+        }
       } catch (Exception e) {
         if (!mStopServer) {
           Log.e("Server error.", e);
@@ -241,6 +268,10 @@
             if (!mStopServer) {
               Log.e("Failed to accept connection.", e);
             }
+          } catch (JSONException e) {
+            if (!mStopServer) {
+              Log.e("Failed to parse request.", e);
+            }
           }
         }
       }
@@ -250,11 +281,57 @@
     return mServer.getLocalPort();
   }
 
-  private void startConnectionThread(final Socket sock) {
-    ConnectionThread networkThread = new ConnectionThread(sock);
-    mConnectionThreads.add(networkThread);
-    networkThread.start();
-    notifyOnConnect();
+  private void startConnectionThread(final Socket sock) throws IOException, JSONException {
+    BufferedReader reader =
+        new BufferedReader(new InputStreamReader(sock.getInputStream()), 8192);
+    PrintWriter writer = new PrintWriter(sock.getOutputStream(), true);
+    String data;
+    if((data = reader.readLine()) != null) {
+      Log.v("Received: " + data);
+      JSONObject request = new JSONObject(data);
+      if(request.has("cmd") && request.has("uid")) {
+        String cmd = request.getString("cmd");
+        int uid = request.getInt("uid");
+        JSONObject result = new JSONObject();
+        if(cmd.equals("initiate")) {
+          Log.d("Initiate a new session");
+          threadIndex += 1;
+          int mUID = threadIndex;
+          //Log.d("Sock state 1: "+sock.isClosed());
+          ConnectionThread networkThread = new ConnectionThread(sock,true,mUID,reader,writer);
+          mConnectionThreads.put(uid, networkThread);
+          networkThread.start();
+          notifyOnConnect();
+          result.put("uid", mUID);
+          result.put("status",true);
+        }else if(cmd.equals("continue")) {
+          Log.d("Continue an existing session");
+          ConnectionThread networkThread = new ConnectionThread(sock,true,uid,reader,writer);
+          mConnectionThreads.put(uid, networkThread);
+          networkThread.start();
+          notifyOnConnect();
+          result.put("uid", uid);
+          result.put("status",true);
+        }else if(cmd.equals("terminate")) {
+          Log.d("Terminate an existing session");
+          mConnectionThreads.remove(uid);
+          result.put("uid", uid);
+          result.put("status",true);
+        }else {
+          result.put("uid", uid);
+          result.put("status",false);
+          result.put("message", "Unrecognized command.");
+        }
+        writer.write(result + "\n");
+        writer.flush();
+        Log.v("Sent: " + result);
+      }else{
+        ConnectionThread networkThread = new ConnectionThread(sock,false,0,reader,writer);
+        mConnectionThreads.put(0, networkThread);
+        networkThread.start();
+        notifyOnConnect();
+      }
+    }
   }
 
   public void shutdown() {
@@ -271,7 +348,7 @@
     // threads. In the worst case, one of the running threads will already have
     // shut down. Since this is a CopyOnWriteList, we don't have to worry about
     // concurrency issues while iterating over the set of threads.
-    for (ConnectionThread connectionThread : mConnectionThreads) {
+    for (ConnectionThread connectionThread : mConnectionThreads.values()) {
       connectionThread.close();
     }
     for (SimpleServerObserver observer : mObservers) {
diff --git a/WebCamFacade/src/com/googlecode/android_scripting/facade/MjpegServer.java b/WebCamFacade/src/com/googlecode/android_scripting/facade/MjpegServer.java
index a79c1ff..ad1e615 100644
--- a/WebCamFacade/src/com/googlecode/android_scripting/facade/MjpegServer.java
+++ b/WebCamFacade/src/com/googlecode/android_scripting/facade/MjpegServer.java
@@ -16,9 +16,11 @@
 
 package com.googlecode.android_scripting.facade;
 
+import java.io.BufferedReader;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.net.Socket;
-
+import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.SimpleServer;
 
 class MjpegServer extends SimpleServer {
@@ -31,6 +33,7 @@
 
   @Override
   protected void handleConnection(Socket socket) throws Exception {
+    Log.d("handle Mjpeg connection");
     byte[] data = mProvider.getJpeg();
     if (data == null) {
       return;
@@ -58,4 +61,9 @@
       outputStream.flush();
     }
   }
-}
\ No newline at end of file
+
+  @Override
+  protected void handleRPCConnection(Socket sock, Integer UID, BufferedReader reader, PrintWriter writer)
+      throws Exception {
+  }
+}
diff --git a/android.py b/android.py
new file mode 100644
index 0000000..ca7eaf4
--- /dev/null
+++ b/android.py
@@ -0,0 +1,73 @@
+# Copyright (C) 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+__author__ = 'Damon Kohler <damonkohler@gmail.com>'
+
+import collections
+import json
+import os
+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')
+
+
+class Android(object):
+
+  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 _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 __getattr__(self, name):
+    def rpc_call(*args):
+      #print(args)
+      return self._rpc(name, *args)
+    return rpc_call