summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Wink Saville <wink@google.com> 2010-10-20 15:17:28 -0700
committer Wink Saville <wink@google.com> 2010-10-20 15:17:28 -0700
commitd20a5d6b5a821e28d73eba6502a2135134014a84 (patch)
treed5b7c44cb521498859622bc64e95d4a190c56249
parent92f987a07a0b2d03a769a8b5fc57fe97a7bd7541 (diff)
Add AsyncChannel and AsyncService.
Change-Id: Ie6f9aed58f49defcd1c051611ce791e2e62a9474
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java778
-rw-r--r--core/java/com/android/internal/util/AsyncService.java128
-rw-r--r--core/tests/coretests/src/android/os/AsyncChannelTest.java38
3 files changed, 944 insertions, 0 deletions
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
new file mode 100644
index 000000000000..15b929054699
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -0,0 +1,778 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Stack;
+
+/**
+ * An asynchronous channel between two handlers.
+ *
+ * The handlers maybe in the same process or in another process. There
+ * are two protocol styles that can be used with an AysncChannel. The
+ * first is a simple request/reply protocol where the server does
+ * not need to know which client is issuing the request.
+ *
+ * In a simple request/reply protocol the client/source sends requests to the
+ * server/destination. And the server uses the replyToMessage methods.
+ * In this usage model there is no need for the destination to
+ * use the connect methods. The typical sequence of operations is:
+ *
+ * 1) Client calls AsyncChannel#connect
+ * 2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+
+ * 3) Client calls AsyncChannel#sendMessage(msgX)
+ * 4) Server receives and processes msgX
+ * 5) Server optionally calls AsyncChannel#replyToMessage(msgY)
+ * and if sent Client receives and processes msgY
+ * 6) Loop to step 3 until done
+ *
+ * 7) When done Client calls {@link AsyncChannel#disconnect(int)}
+ * 8) Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ *
+ * A second usage model is where the server/destination needs to know
+ * which client it's connected too. For example the server needs to
+ * send unsolicited messages back to the client. Or the server keeps
+ * different state for each client. In this model the server will also
+ * use the connect methods. The typical sequence of operation is:
+ *
+ * 1) Client calls AsyncChannel#connect
+ * 2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ * 3) Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)
+ * 4) Server receives CMD_CHANNEL_FULL_CONNECTION
+ * 5) Server calls AsyncChannel#connect
+ * 6) Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ * 7) Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)
+ * 8) Client receives CMD_CHANNEL_FULLY_CONNECTED
+ *
+ * 9) Client/Server uses AsyncChannel#sendMessage/replyToMessage
+ * to communicate and perform work
+ * 10) Loop to step 9 until done
+ *
+ * 11) When done Client/Server calls {@link AsyncChannel#disconnect(int)}
+ * 12) Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ */
+public class AsyncChannel {
+ /** Log tag */
+ private static final String TAG = "AsyncChannel";
+
+ /** Enable to turn on debugging */
+ private static final boolean DBG = false;
+
+ /**
+ * Command sent when the channel is half connected. Half connected
+ * means that the channel can be used to send commends to the destination
+ * but the destination is unaware that the channel exists. The first
+ * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
+ * it is desired to establish a long term connection, but any command maybe
+ * sent.
+ *
+ * msg.arg1 == 0 : STATUS_SUCCESSFUL
+ * 1 : STATUS_BINDING_UNSUCCESSFUL
+ * msg.arg2 == token parameter
+ * msg.replyTo == dstMessenger if successful
+ */
+ public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+
+ /**
+ * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
+ * This is used to initiate a long term connection with the destination and
+ * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
+ *
+ * msg.replyTo = srcMessenger.
+ */
+ public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+
+ /**
+ * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
+ * This signifies the acceptance or rejection of the channel by the sender.
+ *
+ * msg.arg1 == 0 : Accept connection
+ * : All other values signify the destination rejected the connection
+ * and {@link AsyncChannel#disconnect(int)} would typically be called.
+ */
+ public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+
+ /**
+ * 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)}
+ * 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;
+
+ /**
+ * 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
+ * : All other values signify failure and the channel state is indeterminate
+ * msg.arg2 == token parameter
+ * msg.replyTo = messenger disconnecting or null if it was never connected.
+ */
+ public static final int CMD_CHANNEL_DISCONNECTED = -5;
+
+ /** Successful status always 0, !0 is an unsuccessful status */
+ public static final int STATUS_SUCCESSFUL = 0;
+
+ /** Error attempting to bind on a connect */
+ public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
+
+ /** Service connection */
+ private AsyncChannelConnection mConnection;
+
+ /** Context for source */
+ private Context mSrcContext;
+
+ /** Handler for source */
+ private Handler mSrcHandler;
+
+ /** Messenger for source */
+ private Messenger mSrcMessenger;
+
+ /** Messenger for destination */
+ private Messenger mDstMessenger;
+
+ /**
+ * AsyncChannel constructor
+ */
+ public AsyncChannel() {
+ }
+
+ /**
+ * Connect handler to named package/class.
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+ *
+ * @param srcContext is the context of the source
+ * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+ * messages
+ * @param dstPackageName is the destination package name
+ * @param dstClassName is the fully qualified class name (i.e. contains
+ * package name)
+ * @param token unique id for this connection
+ */
+ private void connectSrcHandlerToPackage(
+ Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
+ int token) {
+ if (DBG) log("connect srcHandler to dst Package & class E");
+
+ mConnection = new AsyncChannelConnection(token);
+
+ /* Initialize the source information */
+ mSrcContext = srcContext;
+ mSrcHandler = srcHandler;
+ mSrcMessenger = new Messenger(srcHandler);
+
+ /*
+ * Initialize destination information to null they will
+ * be initialized when the AsyncChannelConnection#onServiceConnected
+ * is called
+ */
+ mDstMessenger = null;
+
+ /* Send intent to create the connection */
+ 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, token);
+ }
+
+ if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+ }
+
+ /**
+ * Connect handler to named package/class.
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+ * msg.arg1 = status
+ * msg.arg2 = token
+ *
+ * @param srcContext is the context of the source
+ * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+ * messages
+ * @param dstPackageName is the destination package name
+ * @param dstClassName is the fully qualified class name (i.e. contains
+ * package name)
+ * @param token returned in msg.arg2
+ */
+ public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
+ String dstClassName, int token) {
+ if (DBG) log("connect srcHandler to dst Package & class E");
+
+ final class ConnectAsync implements Runnable {
+ Context mSrcCtx;
+ Handler mSrcHdlr;
+ String mDstPackageName;
+ String mDstClassName;
+ int mToken;
+
+ ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
+ String dstClassName, int token) {
+ mSrcCtx = srcContext;
+ mSrcHdlr = srcHandler;
+ mDstPackageName = dstPackageName;
+ mDstClassName = dstClassName;
+ mToken = token;
+ }
+
+ @Override
+ public void run() {
+ connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
+ mToken);
+ }
+ }
+
+ ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
+ token);
+ new Thread(ca).start();
+
+ if (DBG) log("connect srcHandler to dst Package & class X");
+ }
+
+ /**
+ * Connect handler to a class
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+ * msg.arg1 = status
+ * msg.arg2 = token
+ *
+ * @param srcContext
+ * @param srcHandler
+ * @param klass is the class to send messages to.
+ * @param token returned in msg.arg2
+ */
+ public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
+ connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
+ }
+
+ /**
+ * Connect handler and messenger.
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+ * msg.arg1 = status
+ * msg.arg2 = token
+ *
+ * @param srcContext
+ * @param srcHandler
+ * @param dstMessenger
+ * @param token returned in msg.arg2
+ */
+ public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
+ 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");
+
+ // Tell source we are half connected
+ replyHalfConnected(STATUS_SUCCESSFUL, token);
+
+ if (DBG) log("connect srcHandler to the dstMessenger X");
+ }
+
+ /**
+ * Connect two local Handlers.
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+ * msg.arg1 = status
+ * msg.arg2 = token
+ *
+ * @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.
+ * @param token returned in msg.arg2
+ */
+ public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
+ connect(srcContext, srcHandler, new Messenger(dstHandler), token);
+ }
+
+ /**
+ * Connect service and messenger.
+ *
+ * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
+ * msg.arg1 = status
+ * msg.arg2 = token
+ *
+ * @param srcAsyncService
+ * @param dstMessenger
+ * @param token returned in msg.arg2
+ */
+ public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
+ connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
+ }
+
+ /**
+ * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
+ */
+ public void disconnected() {
+ mSrcHandler = null;
+ mSrcMessenger = null;
+ mDstMessenger = null;
+ mConnection = null;
+ }
+
+ /**
+ * Disconnect
+ */
+ public void disconnect(int token) {
+ if (mConnection != null) {
+ mConnection.setToken(token);
+ mSrcContext.unbindService(mConnection);
+ }
+ if (mSrcHandler != null) {
+ Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+ msg.arg1 = STATUS_SUCCESSFUL;
+ msg.arg2 = token;
+ msg.replyTo = mDstMessenger;
+ mSrcHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Send a message to the destination handler.
+ *
+ * @param msg
+ */
+ public void sendMessage(Message msg) {
+ msg.replyTo = mSrcMessenger;
+ try {
+ mDstMessenger.send(msg);
+ } catch (RemoteException e) {
+ log("TODO: handle sendMessage RemoteException" + e);
+ }
+ }
+
+ /**
+ * Send a message to the destination handler
+ *
+ * @param what
+ */
+ public void sendMessage(int what) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ sendMessage(msg);
+ }
+
+ /**
+ * Send a message to the destination handler
+ *
+ * @param what
+ * @param arg1
+ */
+ public void sendMessage(int what, int arg1) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ sendMessage(msg);
+ }
+
+ /**
+ * Send a message to the destination handler
+ *
+ * @param what
+ * @param arg1
+ * @param arg2
+ */
+ public void sendMessage(int what, int arg1, int arg2) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ sendMessage(msg);
+ }
+
+ /**
+ * Send a message to the destination handler
+ *
+ * @param what
+ * @param arg1
+ * @param arg2
+ * @param obj
+ */
+ public void sendMessage(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ sendMessage(msg);
+ }
+
+ /**
+ * Send a message to the destination handler
+ *
+ * @param what
+ * @param obj
+ */
+ public void sendMessage(int what, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ sendMessage(msg);
+ }
+
+ /**
+ * Reply to srcMsg sending dstMsg
+ *
+ * @param srcMsg
+ * @param dstMsg
+ */
+ public void replyToMessage(Message srcMsg, Message dstMsg) {
+ try {
+ srcMsg.replyTo.send(dstMsg);
+ } catch (RemoteException e) {
+ log("TODO: handle replyToMessage RemoteException" + e);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param what
+ */
+ public void replyToMessage(Message srcMsg, int what) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ replyToMessage(srcMsg, msg);
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param what
+ * @param arg1
+ */
+ public void replyToMessage(Message srcMsg, int what, int arg1) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ replyToMessage(srcMsg, msg);
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param what
+ * @param arg1
+ * @param arg2
+ */
+ public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ replyToMessage(srcMsg, msg);
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param what
+ * @param arg1
+ * @param arg2
+ * @param obj
+ */
+ public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ replyToMessage(srcMsg, msg);
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param what
+ * @param obj
+ */
+ public void replyToMessage(Message srcMsg, int what, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ replyToMessage(srcMsg, msg);
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param msg to send
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(Message msg) {
+ Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
+ return resultMsg;
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param what
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(int what) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ Message resultMsg = sendMessageSynchronously(msg);
+ return resultMsg;
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param what
+ * @param arg1
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(int what, int arg1) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ Message resultMsg = sendMessageSynchronously(msg);
+ return resultMsg;
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param what
+ * @param arg1
+ * @param arg2
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(int what, int arg1, int arg2) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ Message resultMsg = sendMessageSynchronously(msg);
+ return resultMsg;
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param what
+ * @param arg1
+ * @param arg2
+ * @param obj
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ Message resultMsg = sendMessageSynchronously(msg);
+ return resultMsg;
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param what
+ * @param obj
+ * @return reply message or null if an error.
+ */
+ public Message sendMessageSynchronously(int what, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ Message resultMsg = sendMessageSynchronously(msg);
+ return resultMsg;
+ }
+
+ /**
+ * Helper class to send messages synchronously
+ */
+ private static class SyncMessenger {
+ /** A stack of SyncMessengers */
+ private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
+ /** A number of SyncMessengers created */
+ private static int sCount = 0;
+ /** The handler thread */
+ private HandlerThread mHandlerThread;
+ /** The handler that will receive the result */
+ private SyncHandler mHandler;
+ /** The messenger used to send the message */
+ private Messenger mMessenger;
+
+ /** private constructor */
+ private SyncMessenger() {
+ }
+
+ /** Synchronous Handler class */
+ private class SyncHandler extends Handler {
+ /** The object used to wait/notify */
+ private Object mLockObject = new Object();
+ /** The resulting message */
+ private Message mResultMsg;
+
+ /** Constructor */
+ private SyncHandler(Looper looper) {
+ super(looper);
+ }
+
+ /** Handle of the reply message */
+ @Override
+ public void handleMessage(Message msg) {
+ mResultMsg = Message.obtain();
+ mResultMsg.copyFrom(msg);
+ synchronized(mLockObject) {
+ mLockObject.notify();
+ }
+ }
+ }
+
+ /**
+ * @return the SyncMessenger
+ */
+ private static SyncMessenger obtain() {
+ SyncMessenger sm;
+ synchronized (sStack) {
+ if (sStack.isEmpty()) {
+ sm = new SyncMessenger();
+ sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
+ sm.mHandlerThread.start();
+ sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
+ sm.mMessenger = new Messenger(sm.mHandler);
+ } else {
+ sm = sStack.pop();
+ }
+ }
+ return sm;
+ }
+
+ /**
+ * Recycle this object
+ */
+ private void recycle() {
+ synchronized (sStack) {
+ sStack.push(this);
+ }
+ }
+
+ /**
+ * Send a message synchronously.
+ *
+ * @param msg to send
+ * @return result message or null if an error occurs
+ */
+ 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();
+ }
+ } catch (InterruptedException e) {
+ sm.mHandler.mResultMsg = null;
+ } catch (RemoteException e) {
+ sm.mHandler.mResultMsg = null;
+ }
+ Message resultMsg = sm.mHandler.mResultMsg;
+ sm.recycle();
+ return resultMsg;
+ }
+ }
+
+ /**
+ * Reply to the src handler that we're half connected.
+ *
+ * @param status to be stored in msg.arg1
+ */
+ private void replyHalfConnected(int status, int token) {
+ Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
+ msg.arg1 = status;
+ msg.arg2 = token;
+ msg.replyTo = mDstMessenger;
+ mSrcHandler.sendMessage(msg);
+ }
+
+ /**
+ * ServiceConnection to receive call backs.
+ */
+ class AsyncChannelConnection implements ServiceConnection {
+ private int mToken;
+
+ AsyncChannelConnection(int token) {
+ mToken = token;
+ }
+
+ /**
+ * @param token
+ */
+ public void setToken(int token) {
+ mToken = token;
+ }
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mDstMessenger = new Messenger(service);
+ replyHalfConnected(STATUS_SUCCESSFUL, mToken);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+ msg.arg1 = STATUS_SUCCESSFUL;
+ msg.arg2 = mToken;
+ msg.replyTo = mDstMessenger;
+ mSrcHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Log the string.
+ *
+ * @param s
+ */
+ private static void log(String s) {
+ Slog.d(TAG, s);
+ }
+}
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
new file mode 100644
index 000000000000..54d3c4260bc6
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.internal.util;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Slog;
+
+/**
+ * A service that receives Intents and IBinder transactions
+ * as messages via an AsyncChannel.
+ * <p>
+ * The Start Intent arrives as CMD_ASYNC_SERVICE_ON_START_INTENT with msg.arg1 = flags,
+ * msg.arg2 = startId, and msg.obj = intent.
+ * <p>
+ */
+abstract public class AsyncService extends Service {
+ private static final String TAG = "AsyncService";
+
+ protected static final boolean DBG = true;
+
+ /** The command sent when a onStartCommand is invoked */
+ public static final int CMD_ASYNC_SERVICE_ON_START_INTENT = IBinder.LAST_CALL_TRANSACTION;
+
+ /** The command sent when a onDestroy is invoked */
+ public static final int CMD_ASYNC_SERVICE_DESTROY = IBinder.LAST_CALL_TRANSACTION + 1;
+
+ /** Messenger transport */
+ protected Messenger mMessenger;
+
+ /** Message Handler that will receive messages */
+ Handler mHandler;
+
+ public static final class AsyncServiceInfo {
+ /** Message Handler that will receive messages */
+ public Handler mHandler;
+
+ /**
+ * The flags returned by onStartCommand on how to restart.
+ * For instance @see android.app.Service#START_STICKY
+ */
+ public int mRestartFlags;
+ }
+
+ AsyncServiceInfo mAsyncServiceInfo;
+
+ /**
+ * Create the service's handler returning AsyncServiceInfo.
+ *
+ * @return AsyncServiceInfo
+ */
+ abstract public AsyncServiceInfo createHandler();
+
+ /**
+ * Get the handler
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * onCreate
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mAsyncServiceInfo = createHandler();
+ mHandler = mAsyncServiceInfo.mHandler;
+ mMessenger = new Messenger(mHandler);
+ }
+
+ /**
+ * Sends the CMD_ASYNC_SERVICE_ON_START_INTENT message.
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (DBG) Slog.d(TAG, "onStartCommand");
+
+ Message msg = mHandler.obtainMessage();
+ msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT;
+ msg.arg1 = flags;
+ msg.arg2 = startId;
+ msg.obj = intent;
+ mHandler.sendMessage(msg);
+
+ return mAsyncServiceInfo.mRestartFlags;
+ }
+
+ /**
+ * Called when service is destroyed. After returning the
+ * service is dead an no more processing should be expected
+ * to occur.
+ */
+ @Override
+ public void onDestroy() {
+ if (DBG) Slog.d(TAG, "onDestroy");
+
+ Message msg = mHandler.obtainMessage();
+ msg.what = CMD_ASYNC_SERVICE_DESTROY;
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Returns the Messenger's binder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+}
diff --git a/core/tests/coretests/src/android/os/AsyncChannelTest.java b/core/tests/coretests/src/android/os/AsyncChannelTest.java
new file mode 100644
index 000000000000..43c8290810c2
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AsyncChannelTest.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.os;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for AsyncChannel.
+ */
+public class AsyncChannelTest extends TestCase {
+ private static final boolean DBG = true;
+ private static final boolean WAIT_FOR_DEBUGGER = true;
+ private static final String TAG = "AsyncChannelTest";
+
+ @SmallTest
+ public void test1() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+ Log.d(TAG, "test1");
+ assertTrue(1 == 1);
+ }
+}