Merge "API review fixes" into jb-dev

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

View File

@ -12708,21 +12708,6 @@ package android.net.http {
package android.net.nsd {
public class DnsSdServiceInfo implements android.os.Parcelable {
ctor public DnsSdServiceInfo();
method public int describeContents();
method public java.net.InetAddress getHost();
method public int getPort();
method public java.lang.String getServiceName();
method public java.lang.String getServiceType();
method public void setHost(java.net.InetAddress);
method public void setPort(int);
method public void setServiceName(java.lang.String);
method public void setServiceType(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public class DnsSdTxtRecord implements android.os.Parcelable {
ctor public DnsSdTxtRecord();
ctor public DnsSdTxtRecord(byte[]);
@ -12739,53 +12724,56 @@ package android.net.nsd {
field public static final android.os.Parcelable.Creator CREATOR;
}
public class NsdManager {
method public void deinitialize(android.net.nsd.NsdManager.Channel);
method public void discoverServices(android.net.nsd.NsdManager.Channel, java.lang.String, android.net.nsd.NsdManager.DnsSdDiscoveryListener);
method public void initialize(android.content.Context, android.os.Looper, android.net.nsd.NsdManager.ChannelListener);
method public void registerService(android.net.nsd.NsdManager.Channel, java.lang.String, java.lang.String, int, android.net.nsd.NsdManager.DnsSdRegisterListener);
method public void resolveService(android.net.nsd.NsdManager.Channel, java.lang.String, java.lang.String, android.net.nsd.NsdManager.DnsSdResolveListener);
method public void stopServiceDiscovery(android.net.nsd.NsdManager.Channel, android.net.nsd.NsdManager.ActionListener);
method public void unregisterService(android.net.nsd.NsdManager.Channel, int, android.net.nsd.NsdManager.ActionListener);
public final class NsdManager {
method public void discoverServices(java.lang.String, int, android.net.nsd.NsdManager.DiscoveryListener);
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
field public static final java.lang.String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
field public static final int ALREADY_ACTIVE = 3; // 0x3
field public static final int BUSY = 2; // 0x2
field public static final int ERROR = 0; // 0x0
field public static final java.lang.String EXTRA_NSD_STATE = "nsd_state";
field public static final int MAX_REGS_REACHED = 4; // 0x4
field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3
field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0
field public static final int FAILURE_MAX_LIMIT = 4; // 0x4
field public static final int NSD_STATE_DISABLED = 1; // 0x1
field public static final int NSD_STATE_ENABLED = 2; // 0x2
field public static final int UNSUPPORTED = 1; // 0x1
field public static final int PROTOCOL_DNS_SD = 1; // 0x1
}
public static abstract interface NsdManager.ActionListener {
method public abstract void onFailure(int);
method public abstract void onSuccess();
public static abstract interface NsdManager.DiscoveryListener {
method public abstract void onDiscoveryStarted(java.lang.String);
method public abstract void onDiscoveryStopped(java.lang.String);
method public abstract void onServiceFound(android.net.nsd.NsdServiceInfo);
method public abstract void onServiceLost(android.net.nsd.NsdServiceInfo);
method public abstract void onStartDiscoveryFailed(java.lang.String, int);
method public abstract void onStopDiscoveryFailed(java.lang.String, int);
}
public static class NsdManager.Channel {
public static abstract interface NsdManager.RegistrationListener {
method public abstract void onRegistrationFailed(android.net.nsd.NsdServiceInfo, int);
method public abstract void onServiceRegistered(android.net.nsd.NsdServiceInfo);
method public abstract void onServiceUnregistered(android.net.nsd.NsdServiceInfo);
method public abstract void onUnregistrationFailed(android.net.nsd.NsdServiceInfo, int);
}
public static abstract interface NsdManager.ChannelListener {
method public abstract void onChannelConnected(android.net.nsd.NsdManager.Channel);
method public abstract void onChannelDisconnected();
public static abstract interface NsdManager.ResolveListener {
method public abstract void onResolveFailed(android.net.nsd.NsdServiceInfo, int);
method public abstract void onServiceResolved(android.net.nsd.NsdServiceInfo);
}
public static abstract interface NsdManager.DnsSdDiscoveryListener {
method public abstract void onFailure(int);
method public abstract void onServiceFound(android.net.nsd.DnsSdServiceInfo);
method public abstract void onServiceLost(android.net.nsd.DnsSdServiceInfo);
method public abstract void onStarted(java.lang.String);
}
public static abstract interface NsdManager.DnsSdRegisterListener {
method public abstract void onFailure(int);
method public abstract void onServiceRegistered(int, android.net.nsd.DnsSdServiceInfo);
}
public static abstract interface NsdManager.DnsSdResolveListener {
method public abstract void onFailure(int);
method public abstract void onServiceResolved(android.net.nsd.DnsSdServiceInfo);
public final class NsdServiceInfo implements android.os.Parcelable {
ctor public NsdServiceInfo();
method public int describeContents();
method public java.net.InetAddress getHost();
method public int getPort();
method public java.lang.String getServiceName();
method public java.lang.String getServiceType();
method public void setHost(java.net.InetAddress);
method public void setPort(int);
method public void setServiceName(java.lang.String);
method public void setServiceType(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
}

View File

@ -387,7 +387,7 @@ class ContextImpl extends Context {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(NSD_SERVICE);
INsdManager service = INsdManager.Stub.asInterface(b);
return new NsdManager(service);
return new NsdManager(ctx.getOuterContext(), service);
}});
// Note: this was previously cached in a static variable, but

View File

@ -1,32 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.nsd;
/**
* Interface for a network service.
*
* {@hide}
*/
public interface NetworkServiceInfo {
String getServiceName();
void setServiceName(String s);
String getServiceType();
void setServiceType(String s);
}

View File

@ -22,12 +22,16 @@ import android.content.Context;
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 @@ import com.android.internal.util.Protocol;
* 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 @@ import com.android.internal.util.Protocol;
* 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;
@ -203,13 +192,6 @@ public class NsdManager {
/** @hide */
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 */
@ -217,18 +199,28 @@ public class NsdManager {
/** @hide */
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 @@ public class NsdManager {
* @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 @@ public class NsdManager {
*
* <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 @@ public class NsdManager {
*
* <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 @@ public class NsdManager {
* 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 */

View File

@ -25,7 +25,7 @@ import java.net.InetAddress;
* 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 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
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 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
}
/** 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 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
return info;
}
public DnsSdServiceInfo[] newArray(int size) {
return new DnsSdServiceInfo[size];
public NsdServiceInfo[] newArray(int size) {
return new NsdServiceInfo[size];
}
};
}

View File

@ -20,7 +20,7 @@ import android.content.Context;
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.Messenger;
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 @@ public class NsdService extends INsdManager.Stub {
*/
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 @@ public class NsdService extends INsdManager.Stub {
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 @@ public class NsdService extends INsdManager.Stub {
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 @@ public class NsdService extends INsdManager.Stub {
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);
break;
case NsdManager.STOP_RESOLVE:
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:
default:
Slog.e(TAG, "Unhandled " + msg);
return NOT_HANDLED;
@ -217,11 +217,30 @@ public class NsdService extends INsdManager.Stub {
}
}
private boolean requestLimitReached(ClientInfo clientInfo) {
if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
return true;
}
return false;
}
private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
clientInfo.mClientIds.put(clientId, globalId);
mIdToClientInfoMap.put(globalId, clientInfo);
}
private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
clientInfo.mClientIds.remove(clientId);
mIdToClientInfoMap.remove(globalId);
}
@Override
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 @@ public class NsdService extends INsdManager.Stub {
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;
}
removeRequestMap(msg.arg2, id, clientInfo);
if (unregisterService(id)) {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
} else {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.UPDATE_SERVICE:
if (DBG) Slog.d(TAG, "Update service");
//TODO: implement
mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
break;
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 class NsdService extends INsdManager.Stub {
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.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;
clientInfo.mResolvedService.setServiceName(name);
clientInfo.mResolvedService.setServiceType(type);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
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 @@ public class NsdService extends INsdManager.Stub {
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 @@ public class NsdService extends INsdManager.Stub {
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 @@ public class NsdService extends INsdManager.Stub {
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 @@ public class NsdService extends INsdManager.Stub {
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();
}