Merge "API review fixes" into jb-dev

This commit is contained in:
Irfan Sheriff
2012-05-09 14:04:04 -07:00
committed by Android (Google) Code Review
3 changed files with 592 additions and 612 deletions

View File

@@ -22,12 +22,16 @@ import android.content.Context;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.Messenger; import android.os.Messenger;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import java.util.concurrent.CountDownLatch;
import com.android.internal.util.AsyncChannel; import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol; import com.android.internal.util.Protocol;
@@ -39,88 +43,73 @@ import com.android.internal.util.Protocol;
* B. Another example use case is an application discovering printers on the network. * B. Another example use case is an application discovering printers on the network.
* *
* <p> The API currently supports DNS based service discovery and discovery is currently * <p> The API currently supports DNS based service discovery and discovery is currently
* limited to a local network over Multicast DNS. In future, it will be extended to * limited to a local network over Multicast DNS. DNS service discovery is described at
* support wide area discovery and other service discovery mechanisms. * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
* DNS service discovery is described at http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
* *
* <p> The API is asynchronous and responses to requests from an application are on listener * <p> The API is asynchronous and responses to requests from an application are on listener
* callbacks provided by the application. The application must invoke {@link #initialize} before * callbacks on a seperate thread.
* doing any other operation.
* *
* <p> There are three main operations the API supports - registration, discovery and resolution. * <p> There are three main operations the API supports - registration, discovery and resolution.
* <pre> * <pre>
* Application start * Application start
* | * |
* | <---------------------------------------------- * |
* initialize() | * | onServiceRegistered()
* | | * Register any local services /
* | Wait until channel connects | * to be advertised with \
* | before doing any operation | * registerService() onRegistrationFailed()
* | | * |
* onChannelConnected() __________ | * |
* | | | * discoverServices()
* | | | * |
* | onServiceRegistered() | | * Maintain a list to track
* Register any local services / | | * discovered services
* to be advertised with \ | | If application needs to * |
* registerService() onFailure() | | do any further operations * |--------->
* | | | again, it needs to * | |
* | | | initialize() connection * | onServiceFound()
* discoverServices() | | to framework again * | |
* | | | * | add service to list
* Maintain a list to track | | * | |
* discovered services | | * |<----------
* | | | * |
* |---------> |-> onChannelDisconnected() * |--------->
* | | | * | |
* | onServiceFound() | * | onServiceLost()
* | | | * | |
* | add service to list | * | remove service from list
* | | | * | |
* |<---------- | * |<----------
* | | * |
* |---------> | * |
* | | | * | Connect to a service
* | onServiceLost() | * | from list ?
* | | | * |
* | remove service from list | * resolveService()
* | | | * |
* |<---------- | * onServiceResolved()
* | | * |
* | | * Establish connection to service
* | Connect to a service | * with the host and port information
* | from list ? |
* | |
* resolveService() |
* | |
* onServiceResolved() |
* | |
* Establish connection to service |
* with the host and port information |
* | |
* | ___________|
* deinitialize()
* when done with all operations
* or before quit
* *
* </pre> * </pre>
* An application that needs to advertise itself over a network for other applications to * An application that needs to advertise itself over a network for other applications to
* discover it can do so with a call to {@link #registerService}. If Example is a http based * discover it can do so with a call to {@link #registerService}. If Example is a http based
* application that can provide HTML data to peer services, it can register a name "Example" * application that can provide HTML data to peer services, it can register a name "Example"
* with service type "_http._tcp". A successful registration is notified with a callback to * with service type "_http._tcp". A successful registration is notified with a callback to
* {@link DnsSdRegisterListener#onServiceRegistered} and a failure to register is notified * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified
* over {@link DnsSdRegisterListener#onFailure} * over {@link RegistrationListener#onRegistrationFailed}
* *
* <p> A peer application looking for http services can initiate a discovery for "_http._tcp" * <p> A peer application looking for http services can initiate a discovery for "_http._tcp"
* with a call to {@link #discoverServices}. A service found is notified with a callback * with a call to {@link #discoverServices}. A service found is notified with a callback
* to {@link DnsSdDiscoveryListener#onServiceFound} and a service lost is notified on * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on
* {@link DnsSdDiscoveryListener#onServiceLost}. * {@link DiscoveryListener#onServiceLost}.
* *
* <p> Once the peer application discovers the "Example" http srevice, and needs to receive data * <p> Once the peer application discovers the "Example" http srevice, and needs to receive data
* from the "Example" application, it can initiate a resolve with {@link #resolveService} to * from the "Example" application, it can initiate a resolve with {@link #resolveService} to
* resolve the host and port details for the purpose of establishing a connection. A successful * resolve the host and port details for the purpose of establishing a connection. A successful
* resolve is notified on {@link DnsSdResolveListener#onServiceResolved} and a failure is notified * resolve is notified on {@link ResolveListener#onServiceResolved} and a failure is notified
* on {@link DnsSdResolveListener#onFailure}. * on {@link ResolveListener#onResolveFailed}.
* *
* Applications can reserve for a service type at * Applications can reserve for a service type at
* http://www.iana.org/form/ports-service. Existing services can be found at * http://www.iana.org/form/ports-service. Existing services can be found at
@@ -129,9 +118,9 @@ import com.android.internal.util.Protocol;
* Get an instance of this class by calling {@link android.content.Context#getSystemService(String) * Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
* Context.getSystemService(Context.NSD_SERVICE)}. * Context.getSystemService(Context.NSD_SERVICE)}.
* *
* {@see DnsSdServiceInfo} * {@see NsdServiceInfo}
*/ */
public class NsdManager { public final class NsdManager {
private static final String TAG = "NsdManager"; private static final String TAG = "NsdManager";
INsdManager mService; INsdManager mService;
@@ -203,13 +192,6 @@ public class NsdManager {
/** @hide */ /** @hide */
public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14; public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14;
/** @hide */
public static final int UPDATE_SERVICE = BASE + 15;
/** @hide */
public static final int UPDATE_SERVICE_FAILED = BASE + 16;
/** @hide */
public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 17;
/** @hide */ /** @hide */
public static final int RESOLVE_SERVICE = BASE + 18; public static final int RESOLVE_SERVICE = BASE + 18;
/** @hide */ /** @hide */
@@ -217,18 +199,28 @@ public class NsdManager {
/** @hide */ /** @hide */
public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20;
/** @hide */
public static final int STOP_RESOLVE = BASE + 21;
/** @hide */
public static final int STOP_RESOLVE_FAILED = BASE + 22;
/** @hide */
public static final int STOP_RESOLVE_SUCCEEDED = BASE + 23;
/** @hide */ /** @hide */
public static final int ENABLE = BASE + 24; public static final int ENABLE = BASE + 24;
/** @hide */ /** @hide */
public static final int DISABLE = BASE + 25; public static final int DISABLE = BASE + 25;
/** @hide */
public static final int NATIVE_DAEMON_EVENT = BASE + 26;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
private Context mContext;
private static final int INVALID_LISTENER_KEY = 0;
private int mListenerKey = 1;
private final SparseArray mListenerMap = new SparseArray();
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<NsdServiceInfo>();
private final Object mMapLock = new Object();
private final AsyncChannel mAsyncChannel = new AsyncChannel();
private ServiceHandler mHandler;
private final CountDownLatch mConnected = new CountDownLatch(1);
/** /**
* Create a new Nsd instance. Applications use * Create a new Nsd instance. Applications use
@@ -238,271 +230,213 @@ public class NsdManager {
* @hide - hide this because it takes in a parameter of type INsdManager, which * @hide - hide this because it takes in a parameter of type INsdManager, which
* is a system private class. * is a system private class.
*/ */
public NsdManager(INsdManager service) { public NsdManager(Context context, INsdManager service) {
mService = service; mService = service;
mContext = context;
init();
} }
/** /**
* Passed with onFailure() calls. * Failures are passed with {@link RegistrationListener#onRegistrationFailed},
* {@link RegistrationListener#onUnregistrationFailed},
* {@link DiscoveryListener#onStartDiscoveryFailed},
* {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}.
*
* Indicates that the operation failed due to an internal error. * Indicates that the operation failed due to an internal error.
*/ */
public static final int ERROR = 0; public static final int FAILURE_INTERNAL_ERROR = 0;
/** /**
* Passed with onFailure() calls.
* Indicates that the operation failed because service discovery
* is unsupported on the device.
*/
public static final int UNSUPPORTED = 1;
/**
* Passed with onFailure() calls.
* Indicates that the operation failed because the framework is
* busy and unable to service the request.
*/
public static final int BUSY = 2;
/**
* Passed with onFailure() calls.
* Indicates that the operation failed because it is already active. * Indicates that the operation failed because it is already active.
*/ */
public static final int ALREADY_ACTIVE = 3; public static final int FAILURE_ALREADY_ACTIVE = 3;
/** /**
* Passed with onFailure() calls. * Indicates that the operation failed because the maximum outstanding
* Indicates that the operation failed because maximum limit on * requests from the applications have reached.
* service registrations has reached.
*/ */
public static final int MAX_REGS_REACHED = 4; public static final int FAILURE_MAX_LIMIT = 4;
/** Interface for callback invocation when framework channel is connected or lost */
public interface ChannelListener {
/**
* The channel to the framework is connected.
* Application can initiate calls into the framework using the channel instance passed.
*/
public void onChannelConnected(Channel c);
/**
* The channel to the framework has been disconnected.
* Application could try re-initializing using {@link #initialize}
*/
public void onChannelDisconnected();
}
/** Generic interface for callback invocation for a success or failure */
public interface ActionListener {
public void onFailure(int errorCode);
public void onSuccess();
}
/** Interface for callback invocation for service discovery */ /** Interface for callback invocation for service discovery */
public interface DnsSdDiscoveryListener { public interface DiscoveryListener {
public void onFailure(int errorCode); public void onStartDiscoveryFailed(String serviceType, int errorCode);
public void onStarted(String serviceType); public void onStopDiscoveryFailed(String serviceType, int errorCode);
public void onServiceFound(DnsSdServiceInfo serviceInfo); public void onDiscoveryStarted(String serviceType);
public void onServiceLost(DnsSdServiceInfo serviceInfo); public void onDiscoveryStopped(String serviceType);
public void onServiceFound(NsdServiceInfo serviceInfo);
public void onServiceLost(NsdServiceInfo serviceInfo);
} }
/** Interface for callback invocation for service registration */ /** Interface for callback invocation for service registration */
public interface DnsSdRegisterListener { public interface RegistrationListener {
public void onFailure(int errorCode); public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode);
public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo); public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode);
}
/** @hide */ public void onServiceRegistered(NsdServiceInfo serviceInfo);
public interface DnsSdUpdateRegistrationListener {
public void onFailure(int errorCode); public void onServiceUnregistered(NsdServiceInfo serviceInfo);
public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
} }
/** Interface for callback invocation for service resolution */ /** Interface for callback invocation for service resolution */
public interface DnsSdResolveListener { public interface ResolveListener {
public void onFailure(int errorCode); public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode);
public void onServiceResolved(DnsSdServiceInfo serviceInfo); public void onServiceResolved(NsdServiceInfo serviceInfo);
} }
/** private class ServiceHandler extends Handler {
* A channel that connects the application to the NetworkService framework. ServiceHandler(Looper looper) {
* Most service operations require a Channel as an argument. An instance of Channel is obtained super(looper);
* by doing a call on {@link #initialize}
*/
public static class Channel {
Channel(Looper looper, ChannelListener l) {
mAsyncChannel = new AsyncChannel();
mHandler = new ServiceHandler(looper);
mChannelListener = l;
} }
private ChannelListener mChannelListener;
private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
private ActionListener mDnsSdStopDiscoveryListener;
private DnsSdRegisterListener mDnsSdRegisterListener;
private ActionListener mDnsSdUnregisterListener;
private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
private DnsSdResolveListener mDnsSdResolveListener;
private ActionListener mDnsSdStopResolveListener;
private AsyncChannel mAsyncChannel; @Override
private ServiceHandler mHandler; public void handleMessage(Message message) {
class ServiceHandler extends Handler { Object listener = getListener(message.arg2);
ServiceHandler(Looper looper) { boolean listenerRemove = true;
super(looper); switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
mConnected.countDown();
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
// Ignore
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel lost");
break;
case DISCOVER_SERVICES_STARTED:
String s = ((NsdServiceInfo) message.obj).getServiceType();
((DiscoveryListener) listener).onDiscoveryStarted(s);
// Keep listener until stop discovery
listenerRemove = false;
break;
case DISCOVER_SERVICES_FAILED:
((DiscoveryListener) listener).onStartDiscoveryFailed(
getNsdService(message.arg2).getServiceType(), message.arg1);
break;
case SERVICE_FOUND:
((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj);
// Keep listener until stop discovery
listenerRemove = false;
break;
case SERVICE_LOST:
((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj);
// Keep listener until stop discovery
listenerRemove = false;
break;
case STOP_DISCOVERY_FAILED:
((DiscoveryListener) listener).onStopDiscoveryFailed(
getNsdService(message.arg2).getServiceType(), message.arg1);
break;
case STOP_DISCOVERY_SUCCEEDED:
((DiscoveryListener) listener).onDiscoveryStopped(
getNsdService(message.arg2).getServiceType());
break;
case REGISTER_SERVICE_FAILED:
((RegistrationListener) listener).onRegistrationFailed(
getNsdService(message.arg2), message.arg1);
break;
case REGISTER_SERVICE_SUCCEEDED:
((RegistrationListener) listener).onServiceRegistered(
(NsdServiceInfo) message.obj);
// Keep listener until unregister
listenerRemove = false;
break;
case UNREGISTER_SERVICE_FAILED:
((RegistrationListener) listener).onUnregistrationFailed(
getNsdService(message.arg2), message.arg1);
break;
case UNREGISTER_SERVICE_SUCCEEDED:
((RegistrationListener) listener).onServiceUnregistered(
getNsdService(message.arg2));
break;
case RESOLVE_SERVICE_FAILED:
((ResolveListener) listener).onResolveFailed(
getNsdService(message.arg2), message.arg1);
break;
case RESOLVE_SERVICE_SUCCEEDED:
((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj);
break;
default:
Log.d(TAG, "Ignored " + message);
break;
} }
if (listenerRemove) {
@Override removeListener(message.arg2);
public void handleMessage(Message message) {
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
if (mChannelListener != null) {
mChannelListener.onChannelConnected(Channel.this);
}
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
if (mChannelListener != null) {
mChannelListener.onChannelDisconnected();
mChannelListener = null;
}
break;
case DISCOVER_SERVICES_STARTED:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onStarted((String) message.obj);
}
break;
case DISCOVER_SERVICES_FAILED:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onFailure(message.arg1);
}
break;
case SERVICE_FOUND:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onServiceFound(
(DnsSdServiceInfo) message.obj);
}
break;
case SERVICE_LOST:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onServiceLost(
(DnsSdServiceInfo) message.obj);
}
break;
case STOP_DISCOVERY_FAILED:
if (mDnsSdStopDiscoveryListener != null) {
mDnsSdStopDiscoveryListener.onFailure(message.arg1);
}
break;
case STOP_DISCOVERY_SUCCEEDED:
if (mDnsSdStopDiscoveryListener != null) {
mDnsSdStopDiscoveryListener.onSuccess();
}
break;
case REGISTER_SERVICE_FAILED:
if (mDnsSdRegisterListener != null) {
mDnsSdRegisterListener.onFailure(message.arg1);
}
break;
case REGISTER_SERVICE_SUCCEEDED:
if (mDnsSdRegisterListener != null) {
mDnsSdRegisterListener.onServiceRegistered(message.arg1,
(DnsSdServiceInfo) message.obj);
}
break;
case UNREGISTER_SERVICE_FAILED:
if (mDnsSdUnregisterListener != null) {
mDnsSdUnregisterListener.onFailure(message.arg1);
}
break;
case UNREGISTER_SERVICE_SUCCEEDED:
if (mDnsSdUnregisterListener != null) {
mDnsSdUnregisterListener.onSuccess();
}
break;
case UPDATE_SERVICE_FAILED:
if (mDnsSdUpdateListener != null) {
mDnsSdUpdateListener.onFailure(message.arg1);
}
break;
case UPDATE_SERVICE_SUCCEEDED:
if (mDnsSdUpdateListener != null) {
mDnsSdUpdateListener.onServiceUpdated(message.arg1,
(DnsSdTxtRecord) message.obj);
}
break;
case RESOLVE_SERVICE_FAILED:
if (mDnsSdResolveListener != null) {
mDnsSdResolveListener.onFailure(message.arg1);
}
break;
case RESOLVE_SERVICE_SUCCEEDED:
if (mDnsSdResolveListener != null) {
mDnsSdResolveListener.onServiceResolved(
(DnsSdServiceInfo) message.obj);
}
break;
case STOP_RESOLVE_FAILED:
if (mDnsSdStopResolveListener!= null) {
mDnsSdStopResolveListener.onFailure(message.arg1);
}
break;
case STOP_RESOLVE_SUCCEEDED:
if (mDnsSdStopResolveListener != null) {
mDnsSdStopResolveListener.onSuccess();
}
break;
default:
Log.d(TAG, "Ignored " + message);
break;
}
} }
} }
}
private static void checkChannel(Channel c) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
} }
private int putListener(Object listener, NsdServiceInfo s) {
if (listener == null) return INVALID_LISTENER_KEY;
int key;
synchronized (mMapLock) {
do {
key = mListenerKey++;
} while (key == INVALID_LISTENER_KEY);
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
}
return key;
}
private Object getListener(int key) {
if (key == INVALID_LISTENER_KEY) return null;
synchronized (mMapLock) {
return mListenerMap.get(key);
}
}
private NsdServiceInfo getNsdService(int key) {
synchronized (mMapLock) {
return mServiceMap.get(key);
}
}
private void removeListener(int key) {
if (key == INVALID_LISTENER_KEY) return;
synchronized (mMapLock) {
mListenerMap.remove(key);
mServiceMap.remove(key);
}
}
private int getListenerKey(Object listener) {
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
if (valueIndex != -1) {
return mListenerMap.keyAt(valueIndex);
}
}
return INVALID_LISTENER_KEY;
}
/** /**
* Registers the application with the service discovery framework. This function * Initialize AsyncChannel
* must be the first to be called before any other operations are performed. No service
* discovery operations must be performed until the ChannelListener callback notifies
* that the channel is connected
*
* @param srcContext is the context of the source
* @param srcLooper is the Looper on which the callbacks are receivied
* @param listener for callback at loss of framework communication. Cannot be null.
*/ */
public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { private void init() {
Messenger messenger = getMessenger(); final Messenger messenger = getMessenger();
if (messenger == null) throw new RuntimeException("Failed to initialize"); if (messenger == null) throw new RuntimeException("Failed to initialize");
if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null"); HandlerThread t = new HandlerThread("NsdManager");
t.start();
Channel c = new Channel(srcLooper, listener); mHandler = new ServiceHandler(t.getLooper());
c.mAsyncChannel.connect(srcContext, c.mHandler, messenger); mAsyncChannel.connect(mContext, mHandler, messenger);
} try {
mConnected.await();
/** } catch (InterruptedException e) {
* Disconnects application from service discovery framework. No further operations Log.e(TAG, "interrupted wait at init");
* will succeed until a {@link #initialize} is called again. }
*
* @param c channel initialized with {@link #initialize}
*/
public void deinitialize(Channel c) {
checkChannel(c);
c.mAsyncChannel.disconnect();
} }
/** /**
@@ -510,45 +444,51 @@ public class NsdManager {
* *
* <p> The function call immediately returns after sending a request to register service * <p> The function call immediately returns after sending a request to register service
* to the framework. The application is notified of a success to initiate * to the framework. The application is notified of a success to initiate
* discovery through the callback {@link DnsSdRegisterListener#onServiceRegistered} or a failure * discovery through the callback {@link RegistrationListener#onServiceRegistered} or a failure
* through {@link DnsSdRegisterListener#onFailure}. * through {@link RegistrationListener#onRegistrationFailed}.
* *
* @param c is the channel created at {@link #initialize} * @param serviceInfo The service being registered
* @param serviceType The service type being advertised. * @param protocolType The service discovery protocol
* @param port on which the service is listenering for incoming connections * @param listener The listener notifies of a successful registration and is used to
* @param listener for success or failure callback. Can be null. * unregister this service through a call on {@link #unregisterService}. Cannot be null.
*/ */
public void registerService(Channel c, String serviceName, String serviceType, int port, public void registerService(NsdServiceInfo serviceInfo, int protocolType,
DnsSdRegisterListener listener) { RegistrationListener listener) {
checkChannel(c); if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) { TextUtils.isEmpty(serviceInfo.getServiceType())) {
throw new IllegalArgumentException("Service name or type cannot be empty"); throw new IllegalArgumentException("Service name or type cannot be empty");
} }
if (port <= 0) { if (serviceInfo.getPort() <= 0) {
throw new IllegalArgumentException("Invalid port number"); throw new IllegalArgumentException("Invalid port number");
} }
DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null); if (listener == null) {
serviceInfo.setPort(port); throw new IllegalArgumentException("listener cannot be null");
c.mDnsSdRegisterListener = listener; }
c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo); if (protocolType != PROTOCOL_DNS_SD) {
throw new IllegalArgumentException("Unsupported protocol");
}
mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, putListener(listener, serviceInfo),
serviceInfo);
} }
/** /**
* Unregister a service registered through {@link #registerService} * Unregister a service registered through {@link #registerService}. A successful
* @param c is the channel created at {@link #initialize} * unregister is notified to the application with a call to
* @param registeredId is obtained at {@link DnsSdRegisterListener#onServiceRegistered} * {@link RegistrationListener#onServiceUnregistered}.
* @param listener provides callbacks for success or failure. Can be null. *
* @param listener This should be the listener object that was passed to
* {@link #registerService}. It identifies the service that should be unregistered
* and notifies of a successful unregistration.
*/ */
public void unregisterService(Channel c, int registeredId, ActionListener listener) { public void unregisterService(RegistrationListener listener) {
checkChannel(c); int id = getListenerKey(listener);
c.mDnsSdUnregisterListener = listener; if (id == INVALID_LISTENER_KEY) {
c.mAsyncChannel.sendMessage(UNREGISTER_SERVICE, registeredId); throw new IllegalArgumentException("listener not registered");
} }
if (listener == null) {
/** @hide */ throw new IllegalArgumentException("listener cannot be null");
public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) { }
checkChannel(c); mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
} }
/** /**
@@ -558,51 +498,61 @@ public class NsdManager {
* *
* <p> The function call immediately returns after sending a request to start service * <p> The function call immediately returns after sending a request to start service
* discovery to the framework. The application is notified of a success to initiate * discovery to the framework. The application is notified of a success to initiate
* discovery through the callback {@link DnsSdDiscoveryListener#onStarted} or a failure * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
* through {@link DnsSdDiscoveryListener#onFailure}. * through {@link DiscoveryListener#onStartDiscoveryFailed}.
* *
* <p> Upon successful start, application is notified when a service is found with * <p> Upon successful start, application is notified when a service is found with
* {@link DnsSdDiscoveryListener#onServiceFound} or when a service is lost with * {@link DiscoveryListener#onServiceFound} or when a service is lost with
* {@link DnsSdDiscoveryListener#onServiceLost}. * {@link DiscoveryListener#onServiceLost}.
* *
* <p> Upon failure to start, service discovery is not active and application does * <p> Upon failure to start, service discovery is not active and application does
* not need to invoke {@link #stopServiceDiscovery} * not need to invoke {@link #stopServiceDiscovery}
* *
* @param c is the channel created at {@link #initialize}
* @param serviceType The service type being discovered. Examples include "_http._tcp" for * @param serviceType The service type being discovered. Examples include "_http._tcp" for
* http services or "_ipp._tcp" for printers * http services or "_ipp._tcp" for printers
* @param listener provides callbacks when service is found or lost. Cannot be null. * @param protocolType The service discovery protocol
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
* Cannot be null.
*/ */
public void discoverServices(Channel c, String serviceType, DnsSdDiscoveryListener listener) { public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
checkChannel(c);
if (listener == null) { if (listener == null) {
throw new IllegalStateException("Discovery listener needs to be set first"); throw new IllegalArgumentException("listener cannot be null");
} }
if (TextUtils.isEmpty(serviceType)) { if (TextUtils.isEmpty(serviceType)) {
throw new IllegalStateException("Service type cannot be empty"); throw new IllegalArgumentException("Service type cannot be empty");
} }
DnsSdServiceInfo s = new DnsSdServiceInfo();
if (protocolType != PROTOCOL_DNS_SD) {
throw new IllegalArgumentException("Unsupported protocol");
}
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType); s.setServiceType(serviceType);
c.mDnsSdDiscoveryListener = listener; mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, putListener(listener, s), s);
c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
} }
/** /**
* Stop service discovery initiated with {@link #discoverServices}. An active service * Stop service discovery initiated with {@link #discoverServices}. An active service
* discovery is notified to the application with {@link DnsSdDiscoveryListener#onStarted} * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted}
* and it stays active until the application invokes a stop service discovery. * and it stays active until the application invokes a stop service discovery. A successful
* stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}.
* *
* <p> Upon failure to start service discovery notified through * <p> Upon failure to stop service discovery, application is notified through
* {@link DnsSdDiscoveryListener#onFailure} service discovery is not active and * {@link DiscoveryListener#onStopDiscoveryFailed}.
* application does not need to stop it.
* *
* @param c is the channel created at {@link #initialize} * @param listener This should be the listener object that was passed to {@link #discoverServices}.
* @param listener notifies success or failure. Can be null. * It identifies the discovery that should be stopped and notifies of a successful stop.
*/ */
public void stopServiceDiscovery(Channel c, ActionListener listener) { public void stopServiceDiscovery(DiscoveryListener listener) {
checkChannel(c); int id = getListenerKey(listener);
c.mDnsSdStopDiscoveryListener = listener; if (id == INVALID_LISTENER_KEY) {
c.mAsyncChannel.sendMessage(STOP_DISCOVERY); throw new IllegalArgumentException("service discovery not active on listener");
}
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
} }
/** /**
@@ -610,30 +560,19 @@ public class NsdManager {
* establishing a connection to fetch the IP and port details on which to setup * establishing a connection to fetch the IP and port details on which to setup
* the connection. * the connection.
* *
* @param c is the channel created at {@link #initialize} * @param serviceInfo service to be resolved
* @param serviceName of the the service
* @param serviceType of the service
* @param listener to receive callback upon success or failure. Cannot be null. * @param listener to receive callback upon success or failure. Cannot be null.
*/ */
public void resolveService(Channel c, String serviceName, String serviceType, public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
DnsSdResolveListener listener) { if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
checkChannel(c); TextUtils.isEmpty(serviceInfo.getServiceType())) {
if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service name or type cannot be empty"); throw new IllegalArgumentException("Service name or type cannot be empty");
} }
if (listener == null) throw new if (listener == null) {
IllegalStateException("Resolve listener cannot be null"); throw new IllegalArgumentException("listener cannot be null");
c.mDnsSdResolveListener = listener; }
DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null); mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, putListener(listener, serviceInfo),
c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo); serviceInfo);
}
/** @hide */
public void stopServiceResolve(Channel c) {
checkChannel(c);
if (c.mDnsSdResolveListener == null) throw new
IllegalStateException("Resolve listener needs to be set first");
c.mAsyncChannel.sendMessage(STOP_RESOLVE);
} }
/** Internal use only @hide */ /** Internal use only @hide */

View File

@@ -25,7 +25,7 @@ import java.net.InetAddress;
* A class representing service information for network service discovery * A class representing service information for network service discovery
* {@see NsdManager} * {@see NsdManager}
*/ */
public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { public final class NsdServiceInfo implements Parcelable {
private String mServiceName; private String mServiceName;
@@ -37,36 +37,32 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
private int mPort; private int mPort;
public DnsSdServiceInfo() { public NsdServiceInfo() {
} }
/** @hide */ /** @hide */
public DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { public NsdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
mServiceName = sn; mServiceName = sn;
mServiceType = rt; mServiceType = rt;
mTxtRecord = tr; mTxtRecord = tr;
} }
/** Get the service name */ /** Get the service name */
@Override
public String getServiceName() { public String getServiceName() {
return mServiceName; return mServiceName;
} }
/** Set the service name */ /** Set the service name */
@Override
public void setServiceName(String s) { public void setServiceName(String s) {
mServiceName = s; mServiceName = s;
} }
/** Get the service type */ /** Get the service type */
@Override
public String getServiceType() { public String getServiceType() {
return mServiceType; return mServiceType;
} }
/** Set the service type */ /** Set the service type */
@Override
public void setServiceType(String s) { public void setServiceType(String s) {
mServiceType = s; mServiceType = s;
} }
@@ -132,10 +128,10 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
} }
/** Implement the Parcelable interface */ /** Implement the Parcelable interface */
public static final Creator<DnsSdServiceInfo> CREATOR = public static final Creator<NsdServiceInfo> CREATOR =
new Creator<DnsSdServiceInfo>() { new Creator<NsdServiceInfo>() {
public DnsSdServiceInfo createFromParcel(Parcel in) { public NsdServiceInfo createFromParcel(Parcel in) {
DnsSdServiceInfo info = new DnsSdServiceInfo(); NsdServiceInfo info = new NsdServiceInfo();
info.mServiceName = in.readString(); info.mServiceName = in.readString();
info.mServiceType = in.readString(); info.mServiceType = in.readString();
info.mTxtRecord = in.readParcelable(null); info.mTxtRecord = in.readParcelable(null);
@@ -150,8 +146,8 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
return info; return info;
} }
public DnsSdServiceInfo[] newArray(int size) { public NsdServiceInfo[] newArray(int size) {
return new DnsSdServiceInfo[size]; return new NsdServiceInfo[size];
} }
}; };
} }

View File

@@ -20,7 +20,7 @@ import android.content.Context;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.nsd.DnsSdServiceInfo; import android.net.nsd.NsdServiceInfo;
import android.net.nsd.DnsSdTxtRecord; import android.net.nsd.DnsSdTxtRecord;
import android.net.nsd.INsdManager; import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager; import android.net.nsd.NsdManager;
@@ -32,6 +32,7 @@ import android.os.Messenger;
import android.os.IBinder; import android.os.IBinder;
import android.provider.Settings; import android.provider.Settings;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -72,13 +73,16 @@ public class NsdService extends INsdManager.Stub {
*/ */
private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(); private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
/* A map from unique id to client info */
private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>();
private AsyncChannel mReplyChannel = new AsyncChannel(); private AsyncChannel mReplyChannel = new AsyncChannel();
private int INVALID_ID = 0; private int INVALID_ID = 0;
private int mUniqueId = 1; private int mUniqueId = 1;
private static final int BASE = Protocol.BASE_NSD_MANAGER; private static final int BASE = Protocol.BASE_NSD_MANAGER;
private static final int CMD_TO_STRING_COUNT = NsdManager.STOP_RESOLVE - BASE + 1; private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1;
private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
static { static {
@@ -87,7 +91,6 @@ public class NsdService extends INsdManager.Stub {
sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER"; sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER";
sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER"; sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER";
sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE"; sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE";
sCmdToString[NsdManager.STOP_RESOLVE - BASE] = "STOP-RESOLVE";
} }
private static String cmdToString(int cmd) { private static String cmdToString(int cmd) {
@@ -101,9 +104,9 @@ public class NsdService extends INsdManager.Stub {
private class NsdStateMachine extends StateMachine { private class NsdStateMachine extends StateMachine {
private DefaultState mDefaultState = new DefaultState(); private final DefaultState mDefaultState = new DefaultState();
private DisabledState mDisabledState = new DisabledState(); private final DisabledState mDisabledState = new DisabledState();
private EnabledState mEnabledState = new EnabledState(); private final EnabledState mEnabledState = new EnabledState();
@Override @Override
protected String getMessageInfo(Message msg) { protected String getMessageInfo(Message msg) {
@@ -151,29 +154,26 @@ public class NsdService extends INsdManager.Stub {
ac.connect(mContext, getHandler(), msg.replyTo); ac.connect(mContext, getHandler(), msg.replyTo);
break; break;
case NsdManager.DISCOVER_SERVICES: case NsdManager.DISCOVER_SERVICES:
mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
NsdManager.BUSY); NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NsdManager.STOP_DISCOVERY: case NsdManager.STOP_DISCOVERY:
mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NsdManager.REGISTER_SERVICE: case NsdManager.REGISTER_SERVICE:
mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NsdManager.UNREGISTER_SERVICE: case NsdManager.UNREGISTER_SERVICE:
mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NsdManager.RESOLVE_SERVICE: case NsdManager.RESOLVE_SERVICE:
mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NsdManager.STOP_RESOLVE:
mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED,
NsdManager.ERROR);
break; break;
case NsdManager.NATIVE_DAEMON_EVENT:
default: default:
Slog.e(TAG, "Unhandled " + msg); Slog.e(TAG, "Unhandled " + msg);
return NOT_HANDLED; return NOT_HANDLED;
@@ -217,11 +217,30 @@ public class NsdService extends INsdManager.Stub {
} }
} }
private boolean requestLimitReached(ClientInfo clientInfo) {
if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
return true;
}
return false;
}
private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
clientInfo.mClientIds.put(clientId, globalId);
mIdToClientInfoMap.put(globalId, clientInfo);
}
private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
clientInfo.mClientIds.remove(clientId);
mIdToClientInfoMap.remove(globalId);
}
@Override @Override
public boolean processMessage(Message msg) { public boolean processMessage(Message msg) {
ClientInfo clientInfo; ClientInfo clientInfo;
DnsSdServiceInfo servInfo; NsdServiceInfo servInfo;
boolean result = HANDLED; boolean result = HANDLED;
int id;
switch (msg.what) { switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
//First client //First client
@@ -244,111 +263,112 @@ public class NsdService extends INsdManager.Stub {
break; break;
case NsdManager.DISCOVER_SERVICES: case NsdManager.DISCOVER_SERVICES:
if (DBG) Slog.d(TAG, "Discover services"); if (DBG) Slog.d(TAG, "Discover services");
servInfo = (DnsSdServiceInfo) msg.obj; servInfo = (NsdServiceInfo) msg.obj;
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(msg.replyTo);
if (clientInfo.mDiscoveryId != INVALID_ID) {
//discovery already in progress if (requestLimitReached(clientInfo)) {
if (DBG) Slog.d(TAG, "discovery in progress"); replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, NsdManager.FAILURE_MAX_LIMIT);
NsdManager.ALREADY_ACTIVE);
break; break;
} }
clientInfo.mDiscoveryId = getUniqueId();
if (discoverServices(clientInfo.mDiscoveryId, servInfo.getServiceType())) { id = getUniqueId();
mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED); if (discoverServices(id, servInfo.getServiceType())) {
if (DBG) {
Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
servInfo.getServiceType());
}
storeRequestMap(msg.arg2, id, clientInfo);
replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
} else { } else {
mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, stopServiceDiscovery(id);
NsdManager.ERROR); replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
clientInfo.mDiscoveryId = INVALID_ID; NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.STOP_DISCOVERY: case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery"); if (DBG) Slog.d(TAG, "Stop service discovery");
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(msg.replyTo);
if (clientInfo.mDiscoveryId == INVALID_ID) {
//already stopped try {
if (DBG) Slog.d(TAG, "discovery already stopped"); id = clientInfo.mClientIds.get(msg.arg2).intValue();
mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, } catch (NullPointerException e) {
NsdManager.ALREADY_ACTIVE); replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
} }
if (stopServiceDiscovery(clientInfo.mDiscoveryId)) { removeRequestMap(msg.arg2, id, clientInfo);
clientInfo.mDiscoveryId = INVALID_ID; if (stopServiceDiscovery(id)) {
mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
} else { } else {
mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.REGISTER_SERVICE: case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service"); if (DBG) Slog.d(TAG, "Register service");
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(msg.replyTo);
if (clientInfo.mRegisteredIds.size() >= ClientInfo.MAX_REG) { if (requestLimitReached(clientInfo)) {
if (DBG) Slog.d(TAG, "register service exceeds limit"); replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, NsdManager.FAILURE_MAX_LIMIT);
NsdManager.MAX_REGS_REACHED); break;
} }
int id = getUniqueId(); id = getUniqueId();
if (registerService(id, (DnsSdServiceInfo) msg.obj)) { if (registerService(id, (NsdServiceInfo) msg.obj)) {
clientInfo.mRegisteredIds.add(id); if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
storeRequestMap(msg.arg2, id, clientInfo);
// Return success after mDns reports success
} else { } else {
mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, unregisterService(id);
NsdManager.ERROR); replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.UNREGISTER_SERVICE: case NsdManager.UNREGISTER_SERVICE:
if (DBG) Slog.d(TAG, "unregister service"); if (DBG) Slog.d(TAG, "unregister service");
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(msg.replyTo);
int regId = msg.arg1; try {
if (clientInfo.mRegisteredIds.remove(new Integer(regId)) && id = clientInfo.mClientIds.get(msg.arg2).intValue();
unregisterService(regId)) { } catch (NullPointerException e) {
mReplyChannel.replyToMessage(msg, replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
NsdManager.UNREGISTER_SERVICE_SUCCEEDED); NsdManager.FAILURE_INTERNAL_ERROR);
} else { break;
mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, }
NsdManager.ERROR); removeRequestMap(msg.arg2, id, clientInfo);
if (unregisterService(id)) {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
} else {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
} }
break;
case NsdManager.UPDATE_SERVICE:
if (DBG) Slog.d(TAG, "Update service");
//TODO: implement
mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
break; break;
case NsdManager.RESOLVE_SERVICE: case NsdManager.RESOLVE_SERVICE:
if (DBG) Slog.d(TAG, "Resolve service"); if (DBG) Slog.d(TAG, "Resolve service");
servInfo = (DnsSdServiceInfo) msg.obj; servInfo = (NsdServiceInfo) msg.obj;
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(msg.replyTo);
if (clientInfo.mResolveId != INVALID_ID) {
//first cancel existing resolve
stopResolveService(clientInfo.mResolveId);
}
clientInfo.mResolveId = getUniqueId();
if (!resolveService(clientInfo.mResolveId, servInfo)) { if (clientInfo.mResolvedService != null) {
mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_ALREADY_ACTIVE);
clientInfo.mResolveId = INVALID_ID;
}
break;
case NsdManager.STOP_RESOLVE:
if (DBG) Slog.d(TAG, "Stop resolve");
clientInfo = mClients.get(msg.replyTo);
if (clientInfo.mResolveId == INVALID_ID) {
//already stopped
if (DBG) Slog.d(TAG, "resolve already stopped");
mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED,
NsdManager.ALREADY_ACTIVE);
break; break;
} }
if (stopResolveService(clientInfo.mResolveId)) {
clientInfo.mResolveId = INVALID_ID; id = getUniqueId();
mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_SUCCEEDED); if (resolveService(id, servInfo)) {
clientInfo.mResolvedService = new NsdServiceInfo();
storeRequestMap(msg.arg2, id, clientInfo);
} else { } else {
mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.NATIVE_DAEMON_EVENT:
NativeEvent event = (NativeEvent) msg.obj;
handleNativeEvent(event.code, event.raw,
NativeDaemonEvent.unescapeArgs(event.raw));
break;
default: default:
result = NOT_HANDLED; result = NOT_HANDLED;
break; break;
@@ -439,121 +459,144 @@ public class NsdService extends INsdManager.Stub {
public static final int SERVICE_GET_ADDR_SUCCESS = 612; public static final int SERVICE_GET_ADDR_SUCCESS = 612;
} }
private class NativeEvent {
int code;
String raw;
NativeEvent(int code, String raw) {
this.code = code;
this.raw = raw;
}
}
class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks { class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
public void onDaemonConnected() { public void onDaemonConnected() {
mNativeDaemonConnected.countDown(); mNativeDaemonConnected.countDown();
} }
public boolean onEvent(int code, String raw, String[] cooked) { public boolean onEvent(int code, String raw, String[] cooked) {
ClientInfo clientInfo; // TODO: NDC translates a message to a callback, we could enhance NDC to
DnsSdServiceInfo servInfo; // directly interact with a state machine through messages
int id = Integer.parseInt(cooked[1]); NativeEvent event = new NativeEvent(code, raw);
switch (code) { mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
case NativeResponseCode.SERVICE_FOUND: return true;
/* NNN uniqueId serviceName regType domain */ }
if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw); }
clientInfo = getClientByDiscovery(id);
if (clientInfo == null) break;
servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null); private void handleNativeEvent(int code, String raw, String[] cooked) {
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, servInfo); NsdServiceInfo servInfo;
int id = Integer.parseInt(cooked[1]);
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
if (clientInfo == null) {
Slog.e(TAG, "Unique id with no client mapping: " + id);
return;
}
/* This goes in response as msg.arg2 */
int clientId = -1;
int keyId = clientInfo.mClientIds.indexOfValue(id);
if (keyId != -1) {
clientId = clientInfo.mClientIds.keyAt(keyId);
}
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType domain */
if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
clientId, servInfo);
break;
case NativeResponseCode.SERVICE_LOST:
/* NNN uniqueId serviceName regType domain */
if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
clientId, servInfo);
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR, clientId);
break;
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
servInfo = new NsdServiceInfo(cooked[2], null, null);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
id, clientId, servInfo);
break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR, clientId);
break;
case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */
break;
case NativeResponseCode.SERVICE_UPDATE_FAILED:
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_RESOLVED:
/* NNN resolveId fullName hostName port txtlen txtdata */
if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
int index = cooked[2].indexOf(".");
if (index == -1) {
Slog.e(TAG, "Invalid service found " + raw);
break; break;
case NativeResponseCode.SERVICE_LOST: }
/* NNN uniqueId serviceName regType domain */ String name = cooked[2].substring(0, index);
if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw); String rest = cooked[2].substring(index);
clientInfo = getClientByDiscovery(id); String type = rest.replace(".local.", "");
if (clientInfo == null) break;
servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null); clientInfo.mResolvedService.setServiceName(name);
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, servInfo); clientInfo.mResolvedService.setServiceType(type);
break; clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
clientInfo = getClientByDiscovery(id);
if (clientInfo == null) break;
clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
NsdManager.ERROR);
break;
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
clientInfo = getClientByRegistration(id);
if (clientInfo == null) break;
servInfo = new DnsSdServiceInfo(cooked[2], null, null);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
id, 0, servInfo);
break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
clientInfo = getClientByRegistration(id);
if (clientInfo == null) break;
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.ERROR);
break;
case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */
break;
case NativeResponseCode.SERVICE_UPDATE_FAILED:
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_RESOLVED:
/* NNN resolveId fullName hostName port txtlen txtdata */
if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
clientInfo = getClientByResolve(id);
if (clientInfo == null) break;
int index = cooked[2].indexOf(".");
if (index == -1) {
Slog.e(TAG, "Invalid service found " + raw);
break;
}
String name = cooked[2].substring(0, index);
String rest = cooked[2].substring(index);
String type = rest.replace(".local.", "");
clientInfo.mResolvedService = new DnsSdServiceInfo(name, type, null);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
stopResolveService(id);
getAddrInfo(id, cooked[3]);
break;
case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
clientInfo = getClientByResolve(id);
if (clientInfo == null) break;
stopResolveService(id);
if (!getAddrInfo(id, cooked[3])) {
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.ERROR); NsdManager.FAILURE_INTERNAL_ERROR, clientId);
break; mIdToClientInfoMap.remove(id);
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: clientInfo.mResolvedService = null;
/* NNN resolveId hostname ttl addr */ }
if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw); break;
clientInfo = getClientByResolve(id); case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
if (clientInfo == null || clientInfo.mResolvedService == null) break; /* NNN resolveId errorCode */
if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
try { stopResolveService(id);
clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); mIdToClientInfoMap.remove(id);
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, clientInfo.mResolvedService = null;
clientInfo.mResolvedService); clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
clientInfo.mResolvedService = null; NsdManager.FAILURE_INTERNAL_ERROR, clientId);
clientInfo.mResolveId = INVALID_ID; break;
} catch (java.net.UnknownHostException e) { case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, /* NNN resolveId errorCode */
NsdManager.ERROR); stopGetAddrInfo(id);
} mIdToClientInfoMap.remove(id);
stopGetAddrInfo(id); clientInfo.mResolvedService = null;
break; if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
default: clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
break; NsdManager.FAILURE_INTERNAL_ERROR, clientId);
} break;
return false; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
/* NNN resolveId hostname ttl addr */
if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
try {
clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
0, clientId, clientInfo.mResolvedService);
} catch (java.net.UnknownHostException e) {
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR, clientId);
}
stopGetAddrInfo(id);
mIdToClientInfoMap.remove(id);
clientInfo.mResolvedService = null;
break;
default:
break;
} }
} }
@@ -579,7 +622,7 @@ public class NsdService extends INsdManager.Stub {
return true; return true;
} }
private boolean registerService(int regId, DnsSdServiceInfo service) { private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
try { try {
//Add txtlen and txtdata //Add txtlen and txtdata
@@ -637,7 +680,7 @@ public class NsdService extends INsdManager.Stub {
return true; return true;
} }
private boolean resolveService(int resolveId, DnsSdServiceInfo service) { private boolean resolveService(int resolveId, NsdServiceInfo service) {
if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
try { try {
mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
@@ -700,49 +743,52 @@ public class NsdService extends INsdManager.Stub {
mNsdStateMachine.dump(fd, pw, args); mNsdStateMachine.dump(fd, pw, args);
} }
private ClientInfo getClientByDiscovery(int discoveryId) { /* arg2 on the source message has an id that needs to be retained in replies
for (ClientInfo c: mClients.values()) { * see NsdManager for details */
if (c.mDiscoveryId == discoveryId) { private Message obtainMessage(Message srcMsg) {
return c; Message msg = Message.obtain();
} msg.arg2 = srcMsg.arg2;
} return msg;
return null;
} }
private ClientInfo getClientByResolve(int resolveId) { private void replyToMessage(Message msg, int what) {
for (ClientInfo c: mClients.values()) { if (msg.replyTo == null) return;
if (c.mResolveId == resolveId) { Message dstMsg = obtainMessage(msg);
return c; dstMsg.what = what;
} mReplyChannel.replyToMessage(msg, dstMsg);
}
return null;
} }
private ClientInfo getClientByRegistration(int regId) { private void replyToMessage(Message msg, int what, int arg1) {
for (ClientInfo c: mClients.values()) { if (msg.replyTo == null) return;
if (c.mRegisteredIds.contains(regId)) { Message dstMsg = obtainMessage(msg);
return c; dstMsg.what = what;
} dstMsg.arg1 = arg1;
} mReplyChannel.replyToMessage(msg, dstMsg);
return null; }
private void replyToMessage(Message msg, int what, Object obj) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessage(msg);
dstMsg.what = what;
dstMsg.obj = obj;
mReplyChannel.replyToMessage(msg, dstMsg);
} }
/* Information tracked per client */ /* Information tracked per client */
private class ClientInfo { private class ClientInfo {
private static final int MAX_REG = 5; private static final int MAX_LIMIT = 10;
private AsyncChannel mChannel; private AsyncChannel mChannel;
private Messenger mMessenger; private Messenger mMessenger;
private int mDiscoveryId;
private int mResolveId;
/* Remembers a resolved service until getaddrinfo completes */ /* Remembers a resolved service until getaddrinfo completes */
private DnsSdServiceInfo mResolvedService; private NsdServiceInfo mResolvedService;
private ArrayList<Integer> mRegisteredIds = new ArrayList<Integer>();
/* A map from client id to unique id sent to mDns */
private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
private ClientInfo(AsyncChannel c, Messenger m) { private ClientInfo(AsyncChannel c, Messenger m) {
mChannel = c; mChannel = c;
mMessenger = m; mMessenger = m;
mDiscoveryId = mResolveId = INVALID_ID;
if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
} }
@@ -751,11 +797,10 @@ public class NsdService extends INsdManager.Stub {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append("mChannel ").append(mChannel).append("\n"); sb.append("mChannel ").append(mChannel).append("\n");
sb.append("mMessenger ").append(mMessenger).append("\n"); sb.append("mMessenger ").append(mMessenger).append("\n");
sb.append("mDiscoveryId ").append(mDiscoveryId).append("\n");
sb.append("mResolveId ").append(mResolveId).append("\n");
sb.append("mResolvedService ").append(mResolvedService).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n");
for(int regId : mRegisteredIds) { for(int i = 0; i< mClientIds.size(); i++) {
sb.append("regId ").append(regId).append("\n"); sb.append("clientId ").append(mClientIds.keyAt(i));
sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
} }
return sb.toString(); return sb.toString();
} }