summaryrefslogtreecommitdiffstats
path: root/core/java/android/net/nsd
diff options
context:
space:
mode:
authorIrfan Sheriff <isheriff@google.com>2012-04-13 12:15:41 -0700
committerIrfan Sheriff <isheriff@google.com>2012-04-15 17:09:48 -0700
commit92784670c48759c0db604ddb95c05a7b9bdebed8 (patch)
treeb7cc7829b366f64e6bf9e8b200c2e6d915d6e945 /core/java/android/net/nsd
parentd4fecc2567ca54427b9c86900bccbc7103ea8fde (diff)
downloadframeworks_base-92784670c48759c0db604ddb95c05a7b9bdebed8.zip
frameworks_base-92784670c48759c0db604ddb95c05a7b9bdebed8.tar.gz
frameworks_base-92784670c48759c0db604ddb95c05a7b9bdebed8.tar.bz2
Open network service discovery API
Add support for DNS based network service discovery API. This allows applications to discover and resolve applications on a local network such as Wi-Fi Change-Id: Ie89895edd35d12b7f7a23fb5fed36cb2b2079f7a
Diffstat (limited to 'core/java/android/net/nsd')
-rw-r--r--core/java/android/net/nsd/DnsSdServiceInfo.java20
-rw-r--r--core/java/android/net/nsd/NsdManager.java334
2 files changed, 264 insertions, 90 deletions
diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/DnsSdServiceInfo.java
index 33c3eb9..66abd3a 100644
--- a/core/java/android/net/nsd/DnsSdServiceInfo.java
+++ b/core/java/android/net/nsd/DnsSdServiceInfo.java
@@ -22,8 +22,8 @@ import android.os.Parcel;
import java.net.InetAddress;
/**
- * Defines a service based on DNS service discovery
- * {@hide}
+ * A class representing service information for network service discovery
+ * {@see NsdManager}
*/
public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
@@ -40,56 +40,63 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
public DnsSdServiceInfo() {
}
+ /** @hide */
public DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
mServiceName = sn;
mServiceType = rt;
mTxtRecord = tr;
}
+ /** Get the service name */
@Override
- /** @hide */
public String getServiceName() {
return mServiceName;
}
+ /** Set the service name */
@Override
- /** @hide */
public void setServiceName(String s) {
mServiceName = s;
}
+ /** Get the service type */
@Override
- /** @hide */
public String getServiceType() {
return mServiceType;
}
+ /** Set the service type */
@Override
- /** @hide */
public void setServiceType(String s) {
mServiceType = s;
}
+ /** @hide */
public DnsSdTxtRecord getTxtRecord() {
return mTxtRecord;
}
+ /** @hide */
public void setTxtRecord(DnsSdTxtRecord t) {
mTxtRecord = new DnsSdTxtRecord(t);
}
+ /** Get the host address. The host address is valid for a resolved service. */
public InetAddress getHost() {
return mHost;
}
+ /** Set the host address */
public void setHost(InetAddress s) {
mHost = s;
}
+ /** Get port number. The port number is valid for a resolved service. */
public int getPort() {
return mPort;
}
+ /** Set port number */
public void setPort(int p) {
mPort = p;
}
@@ -147,5 +154,4 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
return new DnsSdServiceInfo[size];
}
};
-
}
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 505f11b..dac8d20 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -24,29 +24,110 @@ 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 com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
/**
- * The Network Service Discovery Manager class provides the API for service
- * discovery. Service discovery enables applications to discover and connect with services
- * on a network. Example applications include a game application discovering another instance
- * of the game application or a printer application discovering other printers on a network.
+ * The Network Service Discovery Manager class provides the API to discover services
+ * on a network. As an example, if device A and device B are connected over a Wi-Fi
+ * network, a game registered on device A can be discovered by a game on device
+ * 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
*
* <p> The API is asynchronous and responses to requests from an application are on listener
- * callbacks provided by the application. The application needs to do an initialization with
- * {@link #initialize} before doing any operation.
+ * callbacks provided by the application. The application must invoke {@link #initialize} before
+ * doing any other operation.
+ *
+ * <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
+ *
+ * </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}
+ *
+ * <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}.
+ *
+ * <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}.
*
- * <p> Android currently supports DNS based service discovery and it is limited to a local
- * network with the use of multicast DNS. In future, this class will be
- * extended to support other service discovery mechanisms.
+ * 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/assignments/service-names-port-numbers/service-names-port-numbers.xml
*
* Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
* Context.getSystemService(Context.NSD_SERVICE)}.
- * @hide
*
+ * {@see DnsSdServiceInfo}
*/
public class NsdManager {
private static final String TAG = "NsdManager";
@@ -80,27 +161,32 @@ public class NsdManager {
public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
/** @hide */
- public static final int UPDATE_SERVICE = BASE + 12;
+ public static final int UNREGISTER_SERVICE = BASE + 12;
/** @hide */
- public static final int UPDATE_SERVICE_FAILED = BASE + 13;
+ public static final int UNREGISTER_SERVICE_FAILED = BASE + 13;
/** @hide */
- public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 14;
+ public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14;
/** @hide */
- public static final int RESOLVE_SERVICE = BASE + 15;
+ public static final int UPDATE_SERVICE = BASE + 15;
/** @hide */
- public static final int RESOLVE_SERVICE_FAILED = BASE + 16;
+ public static final int UPDATE_SERVICE_FAILED = BASE + 16;
/** @hide */
- public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 17;
+ public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 17;
/** @hide */
- public static final int STOP_RESOLVE = BASE + 18;
+ public static final int RESOLVE_SERVICE = BASE + 18;
/** @hide */
- public static final int STOP_RESOLVE_FAILED = BASE + 19;
+ public static final int RESOLVE_SERVICE_FAILED = BASE + 19;
/** @hide */
- public static final int STOP_RESOLVE_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;
/**
* Create a new Nsd instance. Applications use
@@ -115,36 +201,44 @@ public class NsdManager {
}
/**
+ * Passed with onFailure() calls.
* Indicates that the operation failed due to an internal error.
*/
public static final int ERROR = 0;
/**
- * Indicates that the operation failed because service discovery is unsupported on the device.
+ * Passed with onFailure() calls.
+ * Indicates that the operation failed because service discovery
+ * is unsupported on the device.
*/
public static final int UNSUPPORTED = 1;
/**
- * Indicates that the operation failed because the framework is busy and
- * unable to service the request.
+ * 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;
/**
+ * Passed with onFailure() calls.
* Indicates that the operation failed because maximum limit on
* service registrations has 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.
@@ -153,6 +247,7 @@ public class NsdManager {
public void onChannelDisconnected();
}
+ /** Generic interface for callback invocation for a success or failure */
public interface ActionListener {
public void onFailure(int errorCode);
@@ -160,11 +255,12 @@ public class NsdManager {
public void onSuccess();
}
+ /** Interface for callback invocation for service discovery */
public interface DnsSdDiscoveryListener {
public void onFailure(int errorCode);
- public void onStarted(String registrationType);
+ public void onStarted(String serviceType);
public void onServiceFound(DnsSdServiceInfo serviceInfo);
@@ -172,6 +268,7 @@ public class NsdManager {
}
+ /** Interface for callback invocation for service registration */
public interface DnsSdRegisterListener {
public void onFailure(int errorCode);
@@ -179,6 +276,7 @@ public class NsdManager {
public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo);
}
+ /** @hide */
public interface DnsSdUpdateRegistrationListener {
public void onFailure(int errorCode);
@@ -186,6 +284,7 @@ public class NsdManager {
public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
}
+ /** Interface for callback invocation for service resolution */
public interface DnsSdResolveListener {
public void onFailure(int errorCode);
@@ -208,6 +307,7 @@ public class NsdManager {
private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
private ActionListener mDnsSdStopDiscoveryListener;
private DnsSdRegisterListener mDnsSdRegisterListener;
+ private ActionListener mDnsSdUnregisterListener;
private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
private DnsSdResolveListener mDnsSdResolveListener;
private ActionListener mDnsSdStopResolveListener;
@@ -279,7 +379,17 @@ public class NsdManager {
(DnsSdServiceInfo) message.obj);
}
break;
- case UPDATE_SERVICE_FAILED:
+ 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);
}
@@ -319,6 +429,10 @@ public class NsdManager {
}
}
+ private static void checkChannel(Channel c) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ }
+
/**
* 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
@@ -327,7 +441,7 @@ public class NsdManager {
*
* @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.
+ * @param listener for callback at loss of framework communication. Cannot be null.
*/
public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
Messenger messenger = getMessenger();
@@ -339,88 +453,142 @@ public class NsdManager {
}
/**
- * Set the listener for service discovery. Can be null.
- */
- public void setDiscoveryListener(Channel c, DnsSdDiscoveryListener b) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdDiscoveryListener = b;
- }
-
- /**
- * Set the listener for stop service discovery. Can be null.
- */
- public void setStopDiscoveryListener(Channel c, ActionListener a) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdStopDiscoveryListener = a;
- }
-
- /**
- * Set the listener for service registration. Can be null.
- */
- public void setRegisterListener(Channel c, DnsSdRegisterListener b) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdRegisterListener = b;
- }
-
- /**
- * Set the listener for service registration. Can be null.
+ * 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 setUpdateRegistrationListener(Channel c, DnsSdUpdateRegistrationListener b) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdUpdateListener = b;
+ public void deinitialize(Channel c) {
+ checkChannel(c);
+ c.mAsyncChannel.disconnect();
}
/**
- * Set the listener for service resolution. Can be null.
+ * Register a service to be discovered by other services.
+ *
+ * <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}.
+ *
+ * @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.
*/
- public void setResolveListener(Channel c, DnsSdResolveListener b) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdResolveListener = b;
+ public void registerService(Channel c, String serviceName, String serviceType, int port,
+ DnsSdRegisterListener listener) {
+ checkChannel(c);
+ if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service name or type cannot be empty");
+ }
+ if (port <= 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);
}
/**
- * Set the listener for stopping service resolution. Can be null.
+ * 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.
*/
- public void setStopResolveListener(Channel c, ActionListener b) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- c.mDnsSdStopResolveListener = b;
- }
-
- public void registerService(Channel c, DnsSdServiceInfo serviceInfo) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
- c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
+ 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) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ checkChannel(c);
c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
}
- public void discoverServices(Channel c, String serviceType) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- if (c.mDnsSdDiscoveryListener == null) throw new
- IllegalStateException("Discovery listener needs to be set first");
+ /**
+ * Initiate service discovery to browse for instances of a service type. Service discovery
+ * consumes network bandwidth and will continue until the application calls
+ * {@link #stopServiceDiscovery}.
+ *
+ * <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}.
+ *
+ * <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}.
+ *
+ * <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.
+ */
+ public void discoverServices(Channel c, String serviceType, DnsSdDiscoveryListener listener) {
+ checkChannel(c);
+ if (listener == null) {
+ throw new IllegalStateException("Discovery listener needs to be set first");
+ }
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalStateException("Service type cannot be empty");
+ }
DnsSdServiceInfo s = new DnsSdServiceInfo();
s.setServiceType(serviceType);
+ c.mDnsSdDiscoveryListener = listener;
c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
}
- public void stopServiceDiscovery(Channel c) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ /**
+ * 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.
+ *
+ * <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.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener notifies success or failure. Can be null.
+ */
+ public void stopServiceDiscovery(Channel c, ActionListener listener) {
+ checkChannel(c);
+ c.mDnsSdStopDiscoveryListener = listener;
c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
}
- public void resolveService(Channel c, DnsSdServiceInfo serviceInfo) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
- if (c.mDnsSdResolveListener == null) throw new
- IllegalStateException("Resolve listener needs to be set first");
+ /**
+ * Resolve a discovered service. An application can resolve a service right before
+ * 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 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)) {
+ 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) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ checkChannel(c);
if (c.mDnsSdResolveListener == null) throw new
IllegalStateException("Resolve listener needs to be set first");
c.mAsyncChannel.sendMessage(STOP_RESOLVE);