diff options
author | Irfan Sheriff <isheriff@google.com> | 2012-04-13 12:15:41 -0700 |
---|---|---|
committer | Irfan Sheriff <isheriff@google.com> | 2012-04-15 17:09:48 -0700 |
commit | 92784670c48759c0db604ddb95c05a7b9bdebed8 (patch) | |
tree | b7cc7829b366f64e6bf9e8b200c2e6d915d6e945 /core/java/android/net/nsd | |
parent | d4fecc2567ca54427b9c86900bccbc7103ea8fde (diff) | |
download | frameworks_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.java | 20 | ||||
-rw-r--r-- | core/java/android/net/nsd/NsdManager.java | 334 |
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); |