Enhance AsyncChannel.

- Use  Protocol for BASE of CMD's.
- Add synchronous connect methods.
- Back port sha1 c23971b3e4f92ac31996d4f9f32eef3cb4ef65fd from master

Change-Id: If0b3f6cbbb8d4268cad6ca19d26ab2ee42390d86
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 101dd91..3973344 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -44,16 +44,16 @@
  * In this usage model there is no need for the destination to
  * use the connect methods. The typical sequence of operations is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Client calls AsyncChannel#connectSync or Asynchronously:</li>
+ *      <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *      </ol>
  *   <li><code>comm-loop:</code></li>
- *   <li>Client calls AsyncChannel#sendMessage(msgX)</li>
- *   <li>Server receives and processes msgX</li>
- *   <li>Server optionally calls AsyncChannel#replyToMessage(msgY)
- *       and if sent Client receives and processes msgY</li>
+ *   <li>Client calls AsyncChannel#sendMessage</li>
+ *   <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li>
- *   <li>Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
+ *   <li>When done Client calls {@link AsyncChannel#disconnect}</li>
+ *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
  *<br/>
  * <p>A second usage model is where the server/destination needs to know
@@ -62,21 +62,26 @@
  * different state for each client. In this model the server will also
  * use the connect methods. The typical sequence of operation is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
- *   <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *   <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li>
+ *      <ol>For an asynchronous full connection it calls AsyncChannel#connect</li>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *          <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *      </ol>
  *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
- *   <li>Server calls AsyncChannel#connect</li>
- *   <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Server calls AsyncChannel#connected</li>
  *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
  *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
  *   <li><code>comm-loop:</code></li>
  *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
  *       to communicate and perform work</li>
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li>
+ *   <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li>
  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
+ *
+ * TODO: Consider simplifying where we have connect and fullyConnect with only one response
+ * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and
+ * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT.
  */
 public class AsyncChannel {
     /** Log tag */
@@ -85,6 +90,8 @@
     /** Enable to turn on debugging */
     private static final boolean DBG = false;
 
+    private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
+
     /**
      * Command sent when the channel is half connected. Half connected
      * means that the channel can be used to send commends to the destination
@@ -98,7 +105,7 @@
      * msg.obj  == the AsyncChannel
      * msg.replyTo == dstMessenger if successful
      */
-    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+    public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
 
     /**
      * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
@@ -107,7 +114,7 @@
      *
      * msg.replyTo = srcMessenger.
      */
-    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+    public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
 
     /**
      * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
@@ -115,31 +122,33 @@
      *
      * msg.arg1 == 0 : Accept connection
      *               : All other values signify the destination rejected the connection
-     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
+     *                 and {@link AsyncChannel#disconnect} would typically be called.
      */
-    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+    public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
 
     /**
      * Command sent when one side or the other wishes to disconnect. The sender
      * may or may not be able to receive a reply depending upon the protocol and
-     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
+     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect}
      * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
      * when the channel is closed.
      *
      * msg.replyTo = messenger that is disconnecting
      */
-    public static final int CMD_CHANNEL_DISCONNECT = -4;
+    public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
 
     /**
      * Command sent when the channel becomes disconnected. This is sent when the
      * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
      *
      * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *             1 : STATUS_BINDING_UNSUCCESSFUL
+     *             2 : STATUS_SEND_UNSUCCESSFUL
      *               : All other values signify failure and the channel state is indeterminate
      * msg.obj  == the AsyncChannel
      * msg.replyTo = messenger disconnecting or null if it was never connected.
      */
-    public static final int CMD_CHANNEL_DISCONNECTED = -5;
+    public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
 
     /** Successful status always 0, !0 is an unsuccessful status */
     public static final int STATUS_SUCCESSFUL = 0;
@@ -147,6 +156,12 @@
     /** Error attempting to bind on a connect */
     public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
 
+    /** Error attempting to send a message */
+    public static final int STATUS_SEND_UNSUCCESSFUL = 2;
+
+    /** CMD_FULLY_CONNECTED refused because a connection already exists*/
+    public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;
+
     /** Service connection */
     private AsyncChannelConnection mConnection;
 
@@ -169,9 +184,7 @@
     }
 
     /**
-     * Connect handler to named package/class.
-     *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     * Connect handler to named package/class synchronously.
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -179,8 +192,10 @@
      * @param dstPackageName is the destination package name
      * @param dstClassName is the fully qualified class name (i.e. contains
      *            package name)
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
      */
-    private void connectSrcHandlerToPackage(
+    public int connectSrcHandlerToPackageSync(
             Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
         if (DBG) log("connect srcHandler to dst Package & class E");
 
@@ -202,11 +217,61 @@
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(dstPackageName, dstClassName);
         boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-        if (!result) {
-            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL);
-        }
-
         if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+        return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
+    }
+
+    /**
+     * Connect a handler to Messenger synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstMessenger is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");
+
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
+
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
+        return STATUS_SUCCESSFUL;
+    }
+
+    /**
+     * connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
+    }
+
+    /**
+     * Fully connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        int status = connectSync(srcContext, srcHandler, dstHandler);
+        if (status == STATUS_SUCCESSFUL) {
+            Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
+            status = response.arg1;
+        }
+        return status;
     }
 
     /**
@@ -241,8 +306,11 @@
                 mDstClassName = dstClassName;
             }
 
+            @Override
             public void run() {
-                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
+                int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
+                        mDstClassName);
+                replyHalfConnected(result);
             }
         }
 
@@ -281,15 +349,8 @@
     public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
         if (DBG) log("connect srcHandler to the dstMessenger  E");
 
-        // Initialize source fields
-        mSrcContext = srcContext;
-        mSrcHandler = srcHandler;
-        mSrcMessenger = new Messenger(mSrcHandler);
-
-        // Initialize destination fields
-        mDstMessenger = dstMessenger;
-
-        if (DBG) log("tell source we are half connected");
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
 
         // Tell source we are half connected
         replyHalfConnected(STATUS_SUCCESSFUL);
@@ -298,11 +359,31 @@
     }
 
     /**
-     * Connect two local Handlers.
+     * Connect handler to messenger. This method is typically called
+     * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
+     * and initializes the internal instance variables to allow communication
+     * with the dstMessenger.
      *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
-     *      msg.arg1 = status
-     *      msg.obj = the AsyncChannel
+     * @param srcContext
+     * @param srcHandler
+     * @param dstMessenger
+     */
+    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("connected srcHandler to the dstMessenger  E");
+
+        // Initialize source fields
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(mSrcHandler);
+
+        // Initialize destination fields
+        mDstMessenger = dstMessenger;
+
+        if (DBG) log("connected srcHandler to the dstMessenger X");
+    }
+
+    /**
+     * Connect two local Handlers.
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -331,6 +412,7 @@
      * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
      */
     public void disconnected() {
+        mSrcContext = null;
         mSrcHandler = null;
         mSrcMessenger = null;
         mDstMessenger = null;
@@ -341,15 +423,11 @@
      * Disconnect
      */
     public void disconnect() {
-        if (mConnection != null) {
+        if ((mConnection != null) && (mSrcContext != null)) {
             mSrcContext.unbindService(mConnection);
         }
         if (mSrcHandler != null) {
-            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
-            msg.arg1 = STATUS_SUCCESSFUL;
-            msg.obj = this;
-            msg.replyTo = mDstMessenger;
-            mSrcHandler.sendMessage(msg);
+            replyDisconnected(STATUS_SUCCESSFUL);
         }
     }
 
@@ -363,7 +441,7 @@
         try {
             mDstMessenger.send(msg);
         } catch (RemoteException e) {
-            log("TODO: handle sendMessage RemoteException" + e);
+            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
         }
     }
 
@@ -444,6 +522,7 @@
      */
     public void replyToMessage(Message srcMsg, Message dstMsg) {
         try {
+            dstMsg.replyTo = mSrcMessenger;
             srcMsg.replyTo.send(dstMsg);
         } catch (RemoteException e) {
             log("TODO: handle replyToMessage RemoteException" + e);
@@ -694,10 +773,14 @@
         private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
             SyncMessenger sm = SyncMessenger.obtain();
             try {
-                msg.replyTo = sm.mMessenger;
-                dstMessenger.send(msg);
-                synchronized (sm.mHandler.mLockObject) {
-                    sm.mHandler.mLockObject.wait();
+                if (dstMessenger != null && msg != null) {
+                    msg.replyTo = sm.mMessenger;
+                    synchronized (sm.mHandler.mLockObject) {
+                        dstMessenger.send(msg);
+                        sm.mHandler.mLockObject.wait();
+                    }
+                } else {
+                    sm.mHandler.mResultMsg = null;
                 }
             } catch (InterruptedException e) {
                 sm.mHandler.mResultMsg = null;
@@ -712,6 +795,7 @@
 
     /**
      * Reply to the src handler that we're half connected.
+     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
      *
      * @param status to be stored in msg.arg1
      */
@@ -724,23 +808,36 @@
     }
 
     /**
+     * Reply to the src handler that we are disconnected
+     * see: CMD_CHANNEL_DISCONNECTED for message contents
+     *
+     * @param status to be stored in msg.arg1
+     */
+    private void replyDisconnected(int status) {
+        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+        msg.arg1 = status;
+        msg.obj = this;
+        msg.replyTo = mDstMessenger;
+        mSrcHandler.sendMessage(msg);
+    }
+
+
+    /**
      * ServiceConnection to receive call backs.
      */
     class AsyncChannelConnection implements ServiceConnection {
         AsyncChannelConnection() {
         }
 
+        @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             mDstMessenger = new Messenger(service);
             replyHalfConnected(STATUS_SUCCESSFUL);
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName className) {
-            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
-            msg.arg1 = STATUS_SUCCESSFUL;
-            msg.obj = AsyncChannel.this;
-            msg.replyTo = mDstMessenger;
-            mSrcHandler.sendMessage(msg);
+            replyDisconnected(STATUS_SUCCESSFUL);
         }
     }
 
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 2689f09..b35f615 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -29,9 +29,17 @@
  * {@hide}
  */
 public class Protocol {
-    public static final int MAX_MESSAGE     =  0x0000FFFF;
+    public static final int MAX_MESSAGE                                             = 0x0000FFFF;
 
-    public static final int BASE_WIFI       =  0x00010000;
-    public static final int BASE_DHCP       =  0x00020000;
+    /** Base reserved for system */
+    public static final int BASE_SYSTEM_RESERVED                                    = 0x00010000;
+    public static final int BASE_SYSTEM_ASYNC_CHANNEL                               = 0x00011000;
+
+    /** Non system protocols */
+    public static final int BASE_WIFI                                               = 0x00020000;
+    public static final int BASE_DHCP                                               = 0x00030000;
+    public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
+    public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00050000;
+
     //TODO: define all used protocols
 }