Merge "Avoid updating thresholds during shutdown." into jb-dev
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 77e97e1..08ba728 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -22,12 +22,16 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.Messenger;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.concurrent.CountDownLatch;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
@@ -39,88 +43,73 @@
* 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
- * limited to a local network over Multicast DNS. In future, it will be extended to
- * support wide area discovery and other service discovery mechanisms.
- * DNS service discovery is described at http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
+ * limited to a local network over Multicast DNS. 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
- * callbacks provided by the application. The application must invoke {@link #initialize} before
- * doing any other operation.
+ * callbacks on a seperate thread.
*
* <p> There are three main operations the API supports - registration, discovery and resolution.
* <pre>
* Application start
* |
- * | <----------------------------------------------
- * initialize() |
- * | |
- * | Wait until channel connects |
- * | before doing any operation |
- * | |
- * onChannelConnected() __________ |
- * | | |
- * | | |
- * | onServiceRegistered() | |
- * Register any local services / | |
- * to be advertised with \ | | If application needs to
- * registerService() onFailure() | | do any further operations
- * | | | again, it needs to
- * | | | initialize() connection
- * discoverServices() | | to framework again
- * | | |
- * Maintain a list to track | |
- * discovered services | |
- * | | |
- * |---------> |-> onChannelDisconnected()
- * | | |
- * | onServiceFound() |
- * | | |
- * | add service to list |
- * | | |
- * |<---------- |
- * | |
- * |---------> |
- * | | |
- * | onServiceLost() |
- * | | |
- * | remove service from list |
- * | | |
- * |<---------- |
- * | |
- * | |
- * | Connect to a service |
- * | from list ? |
- * | |
- * resolveService() |
- * | |
- * onServiceResolved() |
- * | |
- * Establish connection to service |
- * with the host and port information |
- * | |
- * | ___________|
- * deinitialize()
- * when done with all operations
- * or before quit
+ * |
+ * | onServiceRegistered()
+ * Register any local services /
+ * to be advertised with \
+ * registerService() onRegistrationFailed()
+ * |
+ * |
+ * discoverServices()
+ * |
+ * Maintain a list to track
+ * discovered services
+ * |
+ * |--------->
+ * | |
+ * | onServiceFound()
+ * | |
+ * | add service to list
+ * | |
+ * |<----------
+ * |
+ * |--------->
+ * | |
+ * | onServiceLost()
+ * | |
+ * | remove service from list
+ * | |
+ * |<----------
+ * |
+ * |
+ * | Connect to a service
+ * | from list ?
+ * |
+ * resolveService()
+ * |
+ * onServiceResolved()
+ * |
+ * Establish connection to service
+ * with the host and port information
*
* </pre>
* 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
* 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
- * {@link DnsSdRegisterListener#onServiceRegistered} and a failure to register is notified
- * over {@link DnsSdRegisterListener#onFailure}
+ * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified
+ * over {@link RegistrationListener#onRegistrationFailed}
*
* <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
- * to {@link DnsSdDiscoveryListener#onServiceFound} and a service lost is notified on
- * {@link DnsSdDiscoveryListener#onServiceLost}.
+ * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on
+ * {@link DiscoveryListener#onServiceLost}.
*
* <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
* 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
- * on {@link DnsSdResolveListener#onFailure}.
+ * resolve is notified on {@link ResolveListener#onServiceResolved} and a failure is notified
+ * on {@link ResolveListener#onResolveFailed}.
*
* Applications can reserve for a service type at
* http://www.iana.org/form/ports-service. Existing services can be found at
@@ -129,9 +118,9 @@
* Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
* Context.getSystemService(Context.NSD_SERVICE)}.
*
- * {@see DnsSdServiceInfo}
+ * {@see NsdServiceInfo}
*/
-public class NsdManager {
+public final class NsdManager {
private static final String TAG = "NsdManager";
INsdManager mService;
@@ -204,13 +193,6 @@
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 */
public static final int RESOLVE_SERVICE = BASE + 18;
/** @hide */
public static final int RESOLVE_SERVICE_FAILED = BASE + 19;
@@ -218,17 +200,27 @@
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 */
public static final int ENABLE = BASE + 24;
/** @hide */
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
@@ -238,271 +230,213 @@
* @hide - hide this because it takes in a parameter of type INsdManager, which
* is a system private class.
*/
- public NsdManager(INsdManager service) {
+ public NsdManager(Context context, INsdManager 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.
*/
- 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.
*/
- 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 maximum limit on
- * service registrations has reached.
+ * Indicates that the operation failed because the maximum outstanding
+ * requests from the applications have reached.
*/
- public static final int MAX_REGS_REACHED = 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();
- }
+ public static final int FAILURE_MAX_LIMIT = 4;
/** 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 */
- 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 interface DnsSdUpdateRegistrationListener {
+ public void onServiceRegistered(NsdServiceInfo serviceInfo);
- public void onFailure(int errorCode);
-
- public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
+ public void onServiceUnregistered(NsdServiceInfo serviceInfo);
}
/** 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);
}
- /**
- * A channel that connects the application to the NetworkService framework.
- * Most service operations require a Channel as an argument. An instance of Channel is obtained
- * 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 class ServiceHandler extends Handler {
+ ServiceHandler(Looper looper) {
+ super(looper);
}
- 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;
- private ServiceHandler mHandler;
- class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
+ @Override
+ public void handleMessage(Message message) {
+ Object listener = getListener(message.arg2);
+ boolean listenerRemove = true;
+ 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;
}
-
- @Override
- 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;
- }
+ if (listenerRemove) {
+ removeListener(message.arg2);
}
}
- }
-
- 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
- * 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.
+ * Initialize AsyncChannel
*/
- public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
- Messenger messenger = getMessenger();
+ private void init() {
+ final Messenger messenger = getMessenger();
if (messenger == null) throw new RuntimeException("Failed to initialize");
- if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null");
-
- Channel c = new Channel(srcLooper, listener);
- c.mAsyncChannel.connect(srcContext, c.mHandler, messenger);
- }
-
- /**
- * Disconnects application from service discovery framework. No further operations
- * 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();
+ HandlerThread t = new HandlerThread("NsdManager");
+ t.start();
+ mHandler = new ServiceHandler(t.getLooper());
+ mAsyncChannel.connect(mContext, mHandler, messenger);
+ try {
+ mConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
+ }
}
/**
@@ -510,45 +444,51 @@
*
* <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
- * discovery through the callback {@link DnsSdRegisterListener#onServiceRegistered} or a failure
- * through {@link DnsSdRegisterListener#onFailure}.
+ * discovery through the callback {@link RegistrationListener#onServiceRegistered} or a failure
+ * through {@link RegistrationListener#onRegistrationFailed}.
*
- * @param c is the channel created at {@link #initialize}
- * @param serviceType The service type being advertised.
- * @param port on which the service is listenering for incoming connections
- * @param listener for success or failure callback. Can be null.
+ * @param serviceInfo The service being registered
+ * @param protocolType The service discovery protocol
+ * @param listener The listener notifies of a successful registration and is used to
+ * unregister this service through a call on {@link #unregisterService}. Cannot be null.
*/
- public void registerService(Channel c, String serviceName, String serviceType, int port,
- DnsSdRegisterListener listener) {
- checkChannel(c);
- if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
+ public void registerService(NsdServiceInfo serviceInfo, int protocolType,
+ RegistrationListener listener) {
+ if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
+ TextUtils.isEmpty(serviceInfo.getServiceType())) {
throw new IllegalArgumentException("Service name or type cannot be empty");
}
- if (port <= 0) {
+ if (serviceInfo.getPort() <= 0) {
throw new IllegalArgumentException("Invalid port number");
}
- DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null);
- serviceInfo.setPort(port);
- c.mDnsSdRegisterListener = listener;
- c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ 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}
- * @param c is the channel created at {@link #initialize}
- * @param registeredId is obtained at {@link DnsSdRegisterListener#onServiceRegistered}
- * @param listener provides callbacks for success or failure. Can be null.
+ * Unregister a service registered through {@link #registerService}. A successful
+ * unregister is notified to the application with a call to
+ * {@link RegistrationListener#onServiceUnregistered}.
+ *
+ * @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) {
- checkChannel(c);
- c.mDnsSdUnregisterListener = listener;
- c.mAsyncChannel.sendMessage(UNREGISTER_SERVICE, registeredId);
- }
-
- /** @hide */
- public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) {
- checkChannel(c);
- c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
+ public void unregisterService(RegistrationListener listener) {
+ int id = getListenerKey(listener);
+ if (id == INVALID_LISTENER_KEY) {
+ throw new IllegalArgumentException("listener not registered");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
}
/**
@@ -558,51 +498,61 @@
*
* <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 through the callback {@link DnsSdDiscoveryListener#onStarted} or a failure
- * through {@link DnsSdDiscoveryListener#onFailure}.
+ * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
+ * through {@link DiscoveryListener#onStartDiscoveryFailed}.
*
* <p> Upon successful start, application is notified when a service is found with
- * {@link DnsSdDiscoveryListener#onServiceFound} or when a service is lost with
- * {@link DnsSdDiscoveryListener#onServiceLost}.
+ * {@link DiscoveryListener#onServiceFound} or when a service is lost with
+ * {@link DiscoveryListener#onServiceLost}.
*
* <p> Upon failure to start, service discovery is not active and application does
* 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
* 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) {
- checkChannel(c);
+ public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
if (listener == null) {
- throw new IllegalStateException("Discovery listener needs to be set first");
+ throw new IllegalArgumentException("listener cannot be null");
}
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);
- c.mDnsSdDiscoveryListener = listener;
- c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
+ mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, putListener(listener, s), s);
}
/**
* Stop service discovery initiated with {@link #discoverServices}. An active service
- * discovery is notified to the application with {@link DnsSdDiscoveryListener#onStarted}
- * and it stays active until the application invokes a stop service discovery.
+ * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted}
+ * 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
- * {@link DnsSdDiscoveryListener#onFailure} service discovery is not active and
- * application does not need to stop it.
+ * <p> Upon failure to stop service discovery, application is notified through
+ * {@link DiscoveryListener#onStopDiscoveryFailed}.
*
- * @param c is the channel created at {@link #initialize}
- * @param listener notifies success or failure. Can be null.
+ * @param listener This should be the listener object that was passed to {@link #discoverServices}.
+ * It identifies the discovery that should be stopped and notifies of a successful stop.
*/
- public void stopServiceDiscovery(Channel c, ActionListener listener) {
- checkChannel(c);
- c.mDnsSdStopDiscoveryListener = listener;
- c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
+ public void stopServiceDiscovery(DiscoveryListener listener) {
+ int id = getListenerKey(listener);
+ if (id == INVALID_LISTENER_KEY) {
+ 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 @@
* establishing a connection to fetch the IP and port details on which to setup
* the connection.
*
- * @param c is the channel created at {@link #initialize}
- * @param serviceName of the the service
- * @param serviceType of the service
+ * @param serviceInfo service to be resolved
* @param listener to receive callback upon success or failure. Cannot be null.
*/
- public void resolveService(Channel c, String serviceName, String serviceType,
- DnsSdResolveListener listener) {
- checkChannel(c);
- if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
+ public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
+ if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
+ TextUtils.isEmpty(serviceInfo.getServiceType())) {
throw new IllegalArgumentException("Service name or type cannot be empty");
}
- if (listener == null) throw new
- IllegalStateException("Resolve listener cannot be null");
- c.mDnsSdResolveListener = listener;
- DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null);
- c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, 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);
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, putListener(listener, serviceInfo),
+ serviceInfo);
}
/** Internal use only @hide */
diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java
similarity index 86%
rename from core/java/android/net/nsd/DnsSdServiceInfo.java
rename to core/java/android/net/nsd/NsdServiceInfo.java
index 66abd3a..205a21d 100644
--- a/core/java/android/net/nsd/DnsSdServiceInfo.java
+++ b/core/java/android/net/nsd/NsdServiceInfo.java
@@ -25,7 +25,7 @@
* A class representing service information for network service discovery
* {@see NsdManager}
*/
-public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
+public final class NsdServiceInfo implements Parcelable {
private String mServiceName;
@@ -37,36 +37,32 @@
private int mPort;
- public DnsSdServiceInfo() {
+ public NsdServiceInfo() {
}
/** @hide */
- public DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
+ public NsdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
mServiceName = sn;
mServiceType = rt;
mTxtRecord = tr;
}
/** Get the service name */
- @Override
public String getServiceName() {
return mServiceName;
}
/** Set the service name */
- @Override
public void setServiceName(String s) {
mServiceName = s;
}
/** Get the service type */
- @Override
public String getServiceType() {
return mServiceType;
}
/** Set the service type */
- @Override
public void setServiceType(String s) {
mServiceType = s;
}
@@ -132,10 +128,10 @@
}
/** Implement the Parcelable interface */
- public static final Creator<DnsSdServiceInfo> CREATOR =
- new Creator<DnsSdServiceInfo>() {
- public DnsSdServiceInfo createFromParcel(Parcel in) {
- DnsSdServiceInfo info = new DnsSdServiceInfo();
+ public static final Creator<NsdServiceInfo> CREATOR =
+ new Creator<NsdServiceInfo>() {
+ public NsdServiceInfo createFromParcel(Parcel in) {
+ NsdServiceInfo info = new NsdServiceInfo();
info.mServiceName = in.readString();
info.mServiceType = in.readString();
info.mTxtRecord = in.readParcelable(null);
@@ -150,8 +146,8 @@
return info;
}
- public DnsSdServiceInfo[] newArray(int size) {
- return new DnsSdServiceInfo[size];
+ public NsdServiceInfo[] newArray(int size) {
+ return new NsdServiceInfo[size];
}
};
}
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index f33bf8b..cc8e6a4 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -20,7 +20,7 @@
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.nsd.DnsSdServiceInfo;
+import android.net.nsd.NsdServiceInfo;
import android.net.nsd.DnsSdTxtRecord;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.provider.Settings;
import android.util.Slog;
+import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,13 +73,16 @@
*/
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 int INVALID_ID = 0;
private int mUniqueId = 1;
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];
static {
@@ -87,7 +91,6 @@
sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER";
sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER";
sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE";
- sCmdToString[NsdManager.STOP_RESOLVE - BASE] = "STOP-RESOLVE";
}
private static String cmdToString(int cmd) {
@@ -101,9 +104,9 @@
private class NsdStateMachine extends StateMachine {
- private DefaultState mDefaultState = new DefaultState();
- private DisabledState mDisabledState = new DisabledState();
- private EnabledState mEnabledState = new EnabledState();
+ private final DefaultState mDefaultState = new DefaultState();
+ private final DisabledState mDisabledState = new DisabledState();
+ private final EnabledState mEnabledState = new EnabledState();
@Override
protected String getMessageInfo(Message msg) {
@@ -151,29 +154,26 @@
ac.connect(mContext, getHandler(), msg.replyTo);
break;
case NsdManager.DISCOVER_SERVICES:
- mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.BUSY);
+ replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NsdManager.STOP_DISCOVERY:
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NsdManager.REGISTER_SERVICE:
- mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NsdManager.UNREGISTER_SERVICE:
- mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NsdManager.RESOLVE_SERVICE:
- mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
- case NsdManager.STOP_RESOLVE:
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED,
- NsdManager.ERROR);
- break;
+ case NsdManager.NATIVE_DAEMON_EVENT:
default:
Slog.e(TAG, "Unhandled " + msg);
return NOT_HANDLED;
@@ -217,11 +217,30 @@
}
}
+ 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
public boolean processMessage(Message msg) {
ClientInfo clientInfo;
- DnsSdServiceInfo servInfo;
+ NsdServiceInfo servInfo;
boolean result = HANDLED;
+ int id;
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
//First client
@@ -244,111 +263,112 @@
break;
case NsdManager.DISCOVER_SERVICES:
if (DBG) Slog.d(TAG, "Discover services");
- servInfo = (DnsSdServiceInfo) msg.obj;
+ servInfo = (NsdServiceInfo) msg.obj;
clientInfo = mClients.get(msg.replyTo);
- if (clientInfo.mDiscoveryId != INVALID_ID) {
- //discovery already in progress
- if (DBG) Slog.d(TAG, "discovery in progress");
- mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.ALREADY_ACTIVE);
+
+ if (requestLimitReached(clientInfo)) {
+ replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
+ NsdManager.FAILURE_MAX_LIMIT);
break;
}
- clientInfo.mDiscoveryId = getUniqueId();
- if (discoverServices(clientInfo.mDiscoveryId, servInfo.getServiceType())) {
- mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED);
+
+ id = getUniqueId();
+ 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 {
- mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.ERROR);
- clientInfo.mDiscoveryId = INVALID_ID;
+ stopServiceDiscovery(id);
+ replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery");
clientInfo = mClients.get(msg.replyTo);
- if (clientInfo.mDiscoveryId == INVALID_ID) {
- //already stopped
- if (DBG) Slog.d(TAG, "discovery already stopped");
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.ALREADY_ACTIVE);
+
+ try {
+ id = clientInfo.mClientIds.get(msg.arg2).intValue();
+ } catch (NullPointerException e) {
+ replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
- if (stopServiceDiscovery(clientInfo.mDiscoveryId)) {
- clientInfo.mDiscoveryId = INVALID_ID;
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
+ removeRequestMap(msg.arg2, id, clientInfo);
+ if (stopServiceDiscovery(id)) {
+ replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
} else {
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
clientInfo = mClients.get(msg.replyTo);
- if (clientInfo.mRegisteredIds.size() >= ClientInfo.MAX_REG) {
- if (DBG) Slog.d(TAG, "register service exceeds limit");
- mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.MAX_REGS_REACHED);
+ if (requestLimitReached(clientInfo)) {
+ replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
+ NsdManager.FAILURE_MAX_LIMIT);
+ break;
}
- int id = getUniqueId();
- if (registerService(id, (DnsSdServiceInfo) msg.obj)) {
- clientInfo.mRegisteredIds.add(id);
+ id = getUniqueId();
+ if (registerService(id, (NsdServiceInfo) msg.obj)) {
+ if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
+ storeRequestMap(msg.arg2, id, clientInfo);
+ // Return success after mDns reports success
} else {
- mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.ERROR);
+ unregisterService(id);
+ replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.UNREGISTER_SERVICE:
if (DBG) Slog.d(TAG, "unregister service");
clientInfo = mClients.get(msg.replyTo);
- int regId = msg.arg1;
- if (clientInfo.mRegisteredIds.remove(new Integer(regId)) &&
- unregisterService(regId)) {
- mReplyChannel.replyToMessage(msg,
- NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
- } else {
- mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.ERROR);
+ try {
+ id = clientInfo.mClientIds.get(msg.arg2).intValue();
+ } catch (NullPointerException e) {
+ replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
+ break;
}
- break;
- case NsdManager.UPDATE_SERVICE:
- if (DBG) Slog.d(TAG, "Update service");
- //TODO: implement
- mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
+ 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.RESOLVE_SERVICE:
if (DBG) Slog.d(TAG, "Resolve service");
- servInfo = (DnsSdServiceInfo) msg.obj;
+ servInfo = (NsdServiceInfo) msg.obj;
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)) {
- mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.ERROR);
- 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);
+
+ if (clientInfo.mResolvedService != null) {
+ replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
+ NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
- if (stopResolveService(clientInfo.mResolveId)) {
- clientInfo.mResolveId = INVALID_ID;
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_SUCCEEDED);
+
+ id = getUniqueId();
+ if (resolveService(id, servInfo)) {
+ clientInfo.mResolvedService = new NsdServiceInfo();
+ storeRequestMap(msg.arg2, id, clientInfo);
} else {
- mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED,
- NsdManager.ERROR);
+ replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
+ case NsdManager.NATIVE_DAEMON_EVENT:
+ NativeEvent event = (NativeEvent) msg.obj;
+ handleNativeEvent(event.code, event.raw,
+ NativeDaemonEvent.unescapeArgs(event.raw));
+ break;
default:
result = NOT_HANDLED;
break;
@@ -439,121 +459,144 @@
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 {
public void onDaemonConnected() {
mNativeDaemonConnected.countDown();
}
public boolean onEvent(int code, String raw, String[] cooked) {
- ClientInfo clientInfo;
- DnsSdServiceInfo servInfo;
- int id = Integer.parseInt(cooked[1]);
- switch (code) {
- case NativeResponseCode.SERVICE_FOUND:
- /* NNN uniqueId serviceName regType domain */
- if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
- clientInfo = getClientByDiscovery(id);
- if (clientInfo == null) break;
+ // TODO: NDC translates a message to a callback, we could enhance NDC to
+ // directly interact with a state machine through messages
+ NativeEvent event = new NativeEvent(code, raw);
+ mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
+ return true;
+ }
+ }
- servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, servInfo);
+ private void handleNativeEvent(int code, String raw, String[] cooked) {
+ 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;
- case NativeResponseCode.SERVICE_LOST:
- /* NNN uniqueId serviceName regType domain */
- if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
- clientInfo = getClientByDiscovery(id);
- if (clientInfo == null) break;
+ }
+ String name = cooked[2].substring(0, index);
+ String rest = cooked[2].substring(index);
+ String type = rest.replace(".local.", "");
- servInfo = new DnsSdServiceInfo(cooked[2], cooked[3], null);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, servInfo);
- break;
- 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.mResolvedService.setServiceName(name);
+ clientInfo.mResolvedService.setServiceType(type);
+ clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
- 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,
- NsdManager.ERROR);
- break;
- case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
- /* NNN resolveId hostname ttl addr */
- if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
- clientInfo = getClientByResolve(id);
- if (clientInfo == null || clientInfo.mResolvedService == null) break;
-
- try {
- clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- clientInfo.mResolvedService);
- clientInfo.mResolvedService = null;
- clientInfo.mResolveId = INVALID_ID;
- } catch (java.net.UnknownHostException e) {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.ERROR);
- }
- stopGetAddrInfo(id);
- break;
- default:
- break;
- }
- return false;
+ NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ mIdToClientInfoMap.remove(id);
+ clientInfo.mResolvedService = null;
+ }
+ break;
+ case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
+ /* NNN resolveId errorCode */
+ if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
+ stopResolveService(id);
+ mIdToClientInfoMap.remove(id);
+ clientInfo.mResolvedService = null;
+ clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ break;
+ case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
+ /* NNN resolveId errorCode */
+ stopGetAddrInfo(id);
+ mIdToClientInfoMap.remove(id);
+ clientInfo.mResolvedService = null;
+ if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
+ clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
+ NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ break;
+ 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 @@
return true;
}
- private boolean registerService(int regId, DnsSdServiceInfo service) {
+ private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
try {
//Add txtlen and txtdata
@@ -637,7 +680,7 @@
return true;
}
- private boolean resolveService(int resolveId, DnsSdServiceInfo service) {
+ private boolean resolveService(int resolveId, NsdServiceInfo service) {
if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
try {
mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
@@ -700,49 +743,52 @@
mNsdStateMachine.dump(fd, pw, args);
}
- private ClientInfo getClientByDiscovery(int discoveryId) {
- for (ClientInfo c: mClients.values()) {
- if (c.mDiscoveryId == discoveryId) {
- return c;
- }
- }
- return null;
+ /* arg2 on the source message has an id that needs to be retained in replies
+ * see NsdManager for details */
+ private Message obtainMessage(Message srcMsg) {
+ Message msg = Message.obtain();
+ msg.arg2 = srcMsg.arg2;
+ return msg;
}
- private ClientInfo getClientByResolve(int resolveId) {
- for (ClientInfo c: mClients.values()) {
- if (c.mResolveId == resolveId) {
- return c;
- }
- }
- return null;
+ private void replyToMessage(Message msg, int what) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessage(msg);
+ dstMsg.what = what;
+ mReplyChannel.replyToMessage(msg, dstMsg);
}
- private ClientInfo getClientByRegistration(int regId) {
- for (ClientInfo c: mClients.values()) {
- if (c.mRegisteredIds.contains(regId)) {
- return c;
- }
- }
- return null;
+ private void replyToMessage(Message msg, int what, int arg1) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessage(msg);
+ dstMsg.what = what;
+ dstMsg.arg1 = arg1;
+ mReplyChannel.replyToMessage(msg, dstMsg);
+ }
+
+ 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 */
private class ClientInfo {
- private static final int MAX_REG = 5;
+ private static final int MAX_LIMIT = 10;
private AsyncChannel mChannel;
private Messenger mMessenger;
- private int mDiscoveryId;
- private int mResolveId;
/* Remembers a resolved service until getaddrinfo completes */
- private DnsSdServiceInfo mResolvedService;
- private ArrayList<Integer> mRegisteredIds = new ArrayList<Integer>();
+ private NsdServiceInfo mResolvedService;
+
+ /* A map from client id to unique id sent to mDns */
+ private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
private ClientInfo(AsyncChannel c, Messenger m) {
mChannel = c;
mMessenger = m;
- mDiscoveryId = mResolveId = INVALID_ID;
if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
}
@@ -751,11 +797,10 @@
StringBuffer sb = new StringBuffer();
sb.append("mChannel ").append(mChannel).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");
- for(int regId : mRegisteredIds) {
- sb.append("regId ").append(regId).append("\n");
+ for(int i = 0; i< mClientIds.size(); i++) {
+ sb.append("clientId ").append(mClientIds.keyAt(i));
+ sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
}
return sb.toString();
}