Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e74c60d authored by Wink Saville's avatar Wink Saville Committed by Android (Google) Code Review
Browse files

Merge "Enhance AsyncChannel." into honeycomb-LTE

parents aa44b52e 0246bbc8
Loading
Loading
Loading
Loading
+153 −56
Original line number Diff line number Diff line
@@ -44,16 +44,16 @@ import java.util.Stack;
 * 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 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 @@ import java.util.Stack;
 * 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 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 @@ public class AsyncChannel {
    /** 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 @@ public class AsyncChannel {
     * 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 @@ public class AsyncChannel {
     *
     * 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 @@ public class AsyncChannel {
     *
     * 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 @@ public class AsyncChannel {
    /** 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 @@ public class AsyncChannel {
    }

    /**
     * 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 @@ public class AsyncChannel {
     * @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 @@ public class AsyncChannel {
        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;
    }

        if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
    /**
     * 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 @@ public class AsyncChannel {
                mDstClassName = dstClassName;
            }

            @Override
            public void run() {
                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
                int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
                        mDstClassName);
                replyHalfConnected(result);
            }
        }

@@ -281,6 +349,28 @@ public class AsyncChannel {
    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        if (DBG) log("connect srcHandler to the dstMessenger  E");

        // We are connected
        connected(srcContext, srcHandler, dstMessenger);

        // Tell source we are half connected
        replyHalfConnected(STATUS_SUCCESSFUL);

        if (DBG) log("connect srcHandler to the dstMessenger X");
    }

    /**
     * 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.
     *
     * @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;
@@ -289,21 +379,12 @@ public class AsyncChannel {
        // Initialize destination fields
        mDstMessenger = dstMessenger;

        if (DBG) log("tell source we are half connected");

        // Tell source we are half connected
        replyHalfConnected(STATUS_SUCCESSFUL);

        if (DBG) log("connect srcHandler to the dstMessenger X");
        if (DBG) log("connected srcHandler to the dstMessenger X");
    }

    /**
     * Connect two local Handlers.
     *
     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
     *      msg.arg1 = status
     *      msg.obj = the AsyncChannel
     *
     * @param srcContext is the context of the source
     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
     *            messages
@@ -331,6 +412,7 @@ public class AsyncChannel {
     * 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 @@ public class AsyncChannel {
     * 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 @@ public class AsyncChannel {
        try {
            mDstMessenger.send(msg);
        } catch (RemoteException e) {
            log("TODO: handle sendMessage RemoteException" + e);
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }
    }

@@ -444,6 +522,7 @@ public class AsyncChannel {
     */
    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,11 +773,15 @@ public class AsyncChannel {
        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
            SyncMessenger sm = SyncMessenger.obtain();
            try {
                if (dstMessenger != null && msg != null) {
                    msg.replyTo = sm.mMessenger;
                dstMessenger.send(msg);
                    synchronized (sm.mHandler.mLockObject) {
                        dstMessenger.send(msg);
                        sm.mHandler.mLockObject.wait();
                    }
                } else {
                    sm.mHandler.mResultMsg = null;
                }
            } catch (InterruptedException e) {
                sm.mHandler.mResultMsg = null;
            } catch (RemoteException e) {
@@ -712,6 +795,7 @@ public class AsyncChannel {

    /**
     * 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
     */
@@ -723,6 +807,21 @@ public class AsyncChannel {
        mSrcHandler.sendMessage(msg);
    }

    /**
     * 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.
     */
@@ -730,17 +829,15 @@ public class AsyncChannel {
        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);
        }
    }

+11 −3
Original line number Diff line number Diff line
@@ -31,7 +31,15 @@ package com.android.internal.util;
public class Protocol {
    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
}