956 lines
31 KiB
Java
956 lines
31 KiB
Java
/**
|
|
* 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.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.ServiceConnection;
|
|
import android.os.Build;
|
|
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.Log;
|
|
|
|
import java.util.Stack;
|
|
|
|
/**
|
|
* <p>An asynchronous channel between two handlers.</p>
|
|
*
|
|
* <p>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.</p>
|
|
*
|
|
* <p>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:</p>
|
|
*<ol>
|
|
* <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</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}</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
|
|
* 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:</p>
|
|
*<ol>
|
|
* <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#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}</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 */
|
|
private static final String TAG = "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
|
|
* 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.obj == the AsyncChannel
|
|
* msg.replyTo == dstMessenger if successful
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
|
|
|
|
/**
|
|
* 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} would typically be called.
|
|
*/
|
|
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}
|
|
* 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 = 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 = BASE + 4;
|
|
|
|
private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED - BASE + 1;
|
|
private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
|
|
static {
|
|
sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED";
|
|
sCmdToString[CMD_CHANNEL_FULL_CONNECTION - BASE] = "CMD_CHANNEL_FULL_CONNECTION";
|
|
sCmdToString[CMD_CHANNEL_FULLY_CONNECTED - BASE] = "CMD_CHANNEL_FULLY_CONNECTED";
|
|
sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT";
|
|
sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED";
|
|
}
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
|
|
protected static String cmdToString(int cmd) {
|
|
cmd -= BASE;
|
|
if ((cmd >= 0) && (cmd < sCmdToString.length)) {
|
|
return sCmdToString[cmd];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Successful status always 0, !0 is an unsuccessful status */
|
|
@UnsupportedAppUsage
|
|
public static final int STATUS_SUCCESSFUL = 0;
|
|
|
|
/** 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;
|
|
|
|
/** Error indicating abnormal termination of destination messenger */
|
|
public static final int STATUS_REMOTE_DISCONNECTION = 4;
|
|
|
|
/** 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;
|
|
|
|
/** Death Monitor for destination messenger */
|
|
private DeathMonitor mDeathMonitor;
|
|
|
|
/**
|
|
* AsyncChannel constructor
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public AsyncChannel() {
|
|
}
|
|
|
|
/**
|
|
* Connect handler to named package/class synchronously.
|
|
*
|
|
* @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)
|
|
*
|
|
* @return STATUS_SUCCESSFUL on success any other value is an error.
|
|
*/
|
|
public int connectSrcHandlerToPackageSync(
|
|
Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
|
|
if (DBG) log("connect srcHandler to dst Package & class E");
|
|
|
|
mConnection = new AsyncChannelConnection();
|
|
|
|
/* 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 (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.
|
|
*/
|
|
@UnsupportedAppUsage
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Connect handler to named package/class.
|
|
*
|
|
* 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
|
|
* @param dstPackageName is the destination package name
|
|
* @param dstClassName is the fully qualified class name (i.e. contains
|
|
* package name)
|
|
*/
|
|
public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
|
|
String dstClassName) {
|
|
if (DBG) log("connect srcHandler to dst Package & class E");
|
|
|
|
final class ConnectAsync implements Runnable {
|
|
Context mSrcCtx;
|
|
Handler mSrcHdlr;
|
|
String mDstPackageName;
|
|
String mDstClassName;
|
|
|
|
ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
|
|
String dstClassName) {
|
|
mSrcCtx = srcContext;
|
|
mSrcHdlr = srcHandler;
|
|
mDstPackageName = dstPackageName;
|
|
mDstClassName = dstClassName;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
|
|
mDstClassName);
|
|
replyHalfConnected(result);
|
|
}
|
|
}
|
|
|
|
ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
|
|
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.obj = the AsyncChannel
|
|
*
|
|
* @param srcContext
|
|
* @param srcHandler
|
|
* @param klass is the class to send messages to.
|
|
*/
|
|
public void connect(Context srcContext, Handler srcHandler, Class<?> klass) {
|
|
connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName());
|
|
}
|
|
|
|
/**
|
|
* Connect handler and messenger.
|
|
*
|
|
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
|
|
* msg.arg1 = status
|
|
* msg.obj = the AsyncChannel
|
|
*
|
|
* @param srcContext
|
|
* @param srcHandler
|
|
* @param dstMessenger
|
|
*/
|
|
@UnsupportedAppUsage
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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
|
|
* messages
|
|
* @param dstHandler is the hander to send messages to.
|
|
*/
|
|
public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
|
|
connect(srcContext, srcHandler, new Messenger(dstHandler));
|
|
}
|
|
|
|
/**
|
|
* Connect service and messenger.
|
|
*
|
|
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
|
|
* msg.arg1 = status
|
|
* msg.obj = the AsyncChannel
|
|
*
|
|
* @param srcAsyncService
|
|
* @param dstMessenger
|
|
*/
|
|
public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
|
|
connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
|
|
}
|
|
|
|
/**
|
|
* To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
|
|
*/
|
|
public void disconnected() {
|
|
mSrcContext = null;
|
|
mSrcHandler = null;
|
|
mSrcMessenger = null;
|
|
mDstMessenger = null;
|
|
mDeathMonitor = null;
|
|
mConnection = null;
|
|
}
|
|
|
|
/**
|
|
* Disconnect
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public void disconnect() {
|
|
if ((mConnection != null) && (mSrcContext != null)) {
|
|
mSrcContext.unbindService(mConnection);
|
|
mConnection = null;
|
|
}
|
|
try {
|
|
// Send the DISCONNECTED, although it may not be received
|
|
// but its the best we can do.
|
|
Message msg = Message.obtain();
|
|
msg.what = CMD_CHANNEL_DISCONNECTED;
|
|
msg.replyTo = mSrcMessenger;
|
|
mDstMessenger.send(msg);
|
|
} catch(Exception e) {
|
|
}
|
|
// Tell source we're disconnected.
|
|
replyDisconnected(STATUS_SUCCESSFUL);
|
|
mSrcHandler = null;
|
|
// Unlink only when bindService isn't used
|
|
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
|
|
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
|
|
mDeathMonitor = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a message to the destination handler.
|
|
*
|
|
* @param msg
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public void sendMessage(Message msg) {
|
|
msg.replyTo = mSrcMessenger;
|
|
try {
|
|
mDstMessenger.send(msg);
|
|
} catch (RemoteException e) {
|
|
replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a message to the destination handler
|
|
*
|
|
* @param what
|
|
*/
|
|
@UnsupportedAppUsage
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public void replyToMessage(Message srcMsg, Message dstMsg) {
|
|
try {
|
|
dstMsg.replyTo = mSrcMessenger;
|
|
srcMsg.replyTo.send(dstMsg);
|
|
} catch (RemoteException e) {
|
|
log("TODO: handle replyToMessage RemoteException" + e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reply to srcMsg
|
|
*
|
|
* @param srcMsg
|
|
* @param what
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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.
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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.
|
|
*/
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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) {
|
|
Message msgCopy = Message.obtain();
|
|
msgCopy.copyFrom(msg);
|
|
synchronized(mLockObject) {
|
|
mResultMsg = msgCopy;
|
|
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();
|
|
Message resultMsg = null;
|
|
try {
|
|
if (dstMessenger != null && msg != null) {
|
|
msg.replyTo = sm.mMessenger;
|
|
synchronized (sm.mHandler.mLockObject) {
|
|
if (sm.mHandler.mResultMsg != null) {
|
|
Log.wtf(TAG, "mResultMsg should be null here");
|
|
sm.mHandler.mResultMsg = null;
|
|
}
|
|
dstMessenger.send(msg);
|
|
sm.mHandler.mLockObject.wait();
|
|
resultMsg = sm.mHandler.mResultMsg;
|
|
sm.mHandler.mResultMsg = null;
|
|
}
|
|
}
|
|
} catch (InterruptedException e) {
|
|
Log.e(TAG, "error in sendMessageSynchronously", e);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "error in sendMessageSynchronously", e);
|
|
}
|
|
sm.recycle();
|
|
return resultMsg;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private void replyHalfConnected(int status) {
|
|
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
|
|
msg.arg1 = status;
|
|
msg.obj = this;
|
|
msg.replyTo = mDstMessenger;
|
|
if (!linkToDeathMonitor()) {
|
|
// Override status to indicate failure
|
|
msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
|
|
}
|
|
|
|
mSrcHandler.sendMessage(msg);
|
|
}
|
|
|
|
/**
|
|
* Link to death monitor for destination messenger. Returns true if successfully binded to
|
|
* destination messenger; false otherwise.
|
|
*/
|
|
private boolean linkToDeathMonitor() {
|
|
// Link to death only when bindService isn't used and not already linked.
|
|
if (mConnection == null && mDeathMonitor == null) {
|
|
mDeathMonitor = new DeathMonitor();
|
|
try {
|
|
mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
|
|
} catch (RemoteException e) {
|
|
mDeathMonitor = null;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
// Can't reply if already disconnected. Avoid NullPointerException.
|
|
if (mSrcHandler == null) return;
|
|
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) {
|
|
replyDisconnected(STATUS_SUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log the string.
|
|
*
|
|
* @param s
|
|
*/
|
|
private static void log(String s) {
|
|
Log.d(TAG, s);
|
|
}
|
|
|
|
private final class DeathMonitor implements IBinder.DeathRecipient {
|
|
|
|
DeathMonitor() {
|
|
}
|
|
|
|
public void binderDied() {
|
|
replyDisconnected(STATUS_REMOTE_DISCONNECTION);
|
|
}
|
|
|
|
}
|
|
}
|