什么是NSD?
NSD全称为: Network Service Discovery.也就是网络服务发现的意思。(可以在局域网内发现同样使用nsd注册了的应用设备的网络信息)
NSD应用于哪?
通常应用于局域网内不同应用设备的互联
- 发现配置打印机
- 应用小游戏等的互联
NSD简介
NSD(NsdManager)是Android SDK中自带的类库,可以集成直接使用。
使用 NSD服务需要(android4.1及以上) minSdkVersion >16
NSD主要包含两个功能:
1. NSD 注册功能:
进行NSD注册:自定义服务名、端口号,IP地址注册到NSD服务中
2. NSD 扫描功能:
扫描到当前局域网内所有已通过NSD注册了的应用设备的网络信息(服务名、端口号、IP地址)
NSD基本原理
实现了网络发现服务NsdService,其基于苹果的Bonjour服务发现协议
Bonjour协议主要包括:
- 服务的发现
- 服务名称与地址的转换
Bonjour协议流程和DNS流程近似,包括:
1. 服务登记过程
2. 服务发现过程
3. 服务地址解析过程
4. 建立连接等过程
服务发现采用的协议也和DNS类似,不过与DNS协议采用的单播方式不同的是采用了组播方式,因此被称为mDNS。
什么是mDNS?
mDNS multicast DNS (组播Dns)
首先,在 IP 协议里规定了一些保留地址,其中有一个是 224.0.0.251,对应的 IPv6 地址是 [FF02::FB]。
mDNS 协议规定了一个端口,5353。
mDNS 基于 UDP 协议。
mDNS注册扫描流程:A主机进入局域网,开启了 mDNS 服务,并向 mDNS 服务注册以下信息:我的服务名是AiXue,我的IP是 192.168.1.101,端口是 21。当B主机进入局域网,并向 mDNS 服务进行扫描请求,扫描到mDNS服务中所有已注册的主机后,从中过滤出服务名是AiXue的主机,并解析获得到它的网络信息为IP地址为 192.168.1.101,端口号是 21 。
ANDROID借助第三方开源工程mDNSResponder实现了Bonjour协议。
ANDROID对网络服务发现的实现架构包括四层:
- NSD应用层
- 服务发现服务框架层(对应NsdService)
- MDns后台监听层(对应运行在netd本地服务进程的MDnsSdListener类 )
- MDns后台服务(对应mdnsd本地服务进程)。
架构的每层作为其上一层的服务端对上一层提供服务,四层分别运行在不同的进程,采用相应的跨进程通讯方式进行交互,上层通过connect与下层服务建立连接。其中NsdService 和NSD应用层采用JAVA语言实现 ,MDns后台监视采用C++实现,而MDns后台服务为采用C语言的开源代码。
NSD应用层通过NsdService层提供的NsdManager类,对NSD进行注册、扫描、接收响应等操作。
NsdService处于整个层次的承上启下层,其通过NsdManager对应用层提供调用和回调服务,NsdManager和NsdService服务之间采用AsyncChannel异步通道进行消息交互。NsdService服务对下在其NativeDaemonConnector线程对象中使用UNIX SOCKET接口与MDnsSdListener建立跨进程连接。
在MDnsSdListener类中调用mDNSResponder开源工程提供的客户端桩接口与MDns后台服务建立本地SOCKET通讯,并采用Monitor对象来启动MDns后台服务,实现MDns后台服务的事件监听和事件回调处理等工作。MDnsSdListener及Monitor对象与MDns后台服务的交互也是采用UNIX SOCKET机制进行跨进程交互。
MDns后台服务的整个实现代码及客户端的桩实现由第三方工程mDNSResponder提供,代码位于 external目录下 的mdnsresponder中,包括mDNSCore(包括MDNS核心协议引擎代码)、mDNSShared多个平台共享的非核心引擎代码、mDNSPosix Posix平台相关代码、Clients包括如何使用后台服务提供的API的客户端例子代码等四个目录,整个工程编译生成一个mdnsd后台服务和一个MDns监视层使用的库libmdnssd,而Clients中的代码生成一个dnssd执行文件用于测试。
NSD 注册功能开发
1.注册NSD
private NsdManager mNsdManager;
//NSD注册
private void registerService(Context context, String serviceName, int port) {
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName("AiXue");
serviceInfo.setPort(21);
serviceInfo.setServiceType("_http._tcp.");//扫描是需要对应的这个Type字符串
mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}
到这里如果其他设备马上进行扫描就能看到注册了NSD的服务器网络信息
2.注销NSD
public void stopNSDServer() {
mNsdManager.unregisterService(mRegistrationListener);
}
可以取消掉注册NSD服务器,就是让别人扫描不到你的NSD服务器
3.注册监听器
private NsdManager.RegistrationListener mRegistrationListener;
//实例化注册监听器
private void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
@Override
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceRegistered: " + serviceInfo);
}
@Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "NsdServiceInfo onRegistrationFailed");
}
@Override
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);
}
};
}
NSD扫描功能开发
开始NSD扫描
private var mNsdManager: NsdManager? = null
/**
* 启动nsd扫描
* @param mServiceName 服务名与注册者保持一致
* @param mIDiscoverState 扫描状态回调
*/
fun startNsdClient() {
mNsdManager = mContext.getSystemService(Context.NSD_SERVICE) as NsdManager
mNsdManager?.discoverServices("_http._tcp.", NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener)
}
进行发现服务操作后,会在扫描监听器对应的方法得到数据。
停止NSD扫描
fun stopNsdServer() {
mNsdManager?.stopServiceDiscovery(mDiscoveryListener)
}
注册扫描监听器
private fun initializeDiscoveryListener() {
mDiscoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(serviceType: String) {}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
mNsdManager?.stopServiceDiscovery(this)
}
override fun onDiscoveryStopped(serviceType: String) {}
override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
mNsdManager?.stopServiceDiscovery(this)
}
override fun onServiceFound(serviceInfo: NsdServiceInfo) {
if (serviceInfo.serviceType == "_http._tcp." && serviceInfo.serviceName == "AiXue") {
// 解析
mNsdManager?.resolveService(serviceInfo, object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {"onResolveFailed")
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
val port = serviceInfo.port
val host = serviceInfo.host
}
})
}
}
override fun onServiceLost(serviceInfo: NsdServiceInfo) {
LiveLocalLog.e("onServiceLost: serviceInfo=$serviceInfo")
mIDiscoverState?.onDiscoverFail(100, "onServiceLost")
}
}
}
注册流程源码分析
-
NsdManager的实例化
应用通过调用Context.getSystemService(Context.NSD_SERVICE)获得NsdManager的实例。在NsdManager的实例化过程中对使用到的资源进行实例化,包括调用NsdService的getMessenger函数获得服务的Messenger对象用作客户端消息的发送目标,实例化和启动事件处理线程HandlerThread及实例化事件接收处理对象ServiceHandler,AsyncChannel对象的实例化并且调用AsyncChannel对象的connec函数与NsdService建立连接。
在NsdService服务接收到连接消息后,实例化一个服务端的AsyncChannel对象,并根据消息的源和服务端的AsyncChannel对象实例化一个ClientInfo对象放入mClients HashMap数组中。
public NsdManager(Context context, INsdManager service) {
mService = service;
mContext = context;
init();
}
private void init() {
final Messenger messenger = getMessenger();
if (messenger == null) {
fatal("Failed to obtain service Messenger");
}
HandlerThread t = new HandlerThread("NsdManager");
t.start();
mHandler = new ServiceHandler(t.getLooper());
mAsyncChannel.connect(mContext, mHandler, messenger);
try {
mConnected.await();
} catch (InterruptedException e) {
fatal("Interrupted wait at init");
}
}
-
NsdManager的注册
应用调用NsdManager实例的registerService接口,registerService接口参数中包含一个NsdServiceInfo参数(指示要登记的服务信息)、一个protocolType参数(指定协议类型)以及一个监听对象listener,用来接收响应事件回调。
在registerService接口中调用putListener函数分别把NsdServiceInfo参数和监听对象listener保存到mServiceMap和mListenerMap的映射数组中,并返回数组的键值key;然后registerService通过NsdManager的AsyncChannel对象向目标发送REGISTER_SERVICE消息,发送的消息参数包括putListener函数返回的key以及NsdServiceInfo信息。
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
}
private int putListener(Object listener, NsdServiceInfo s) {
checkListener(listener);
final int key;
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
checkArgument(valueIndex == -1, "listener already in use");
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
}
return key;
}
-
NsdService注册及监听
NsdService服务收到REGISTER_SERVICE消息后,首先根据消息源从mClients数组中获得clientInfo对象,然后调用getUniqueId获得一个UniqueId作为登记请求ID;接着调用服务端的registerService函数,registerService的参数为UniqueId和消息传进来的NsdServiceInfo信息。
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
clientInfo = mClients.get(msg.replyTo);
if (requestLimitReached(clientInfo)) {
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_MAX_LIMIT);
break;
}
id = getUniqueId();
if (registerService(id, (NsdServiceInfo) msg.obj)) {
if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
storeRequestMap(msg.arg2, id, clientInfo, msg.what);
// Return success after mDns reports success
} else {
unregisterService(id);
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
NsdService的服务注册:在registerService函数中调用NativeDaemonConnector对象的execute函数,execute函数的命令参数为”mdnssd”,其它参数包括登记命令名称标示"register"、登记ID、从NsdServiceInfo中获得的ServiceName、ServiceType和port等参数。
NativeDaemonConnector对象在NsdService服务实例化时实例化, NativeDaemonConnector对象实例化mSocket参数为"mdns",mCallbacks参数指向NsdService服务内部NativeCallbackReceiver对象。NativeDaemonConnector对象本身是一个派生自Runnable的线程对象,因此其线程函数run也在实例化后启动。
private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) {
Slog.d(TAG, "registerService: " + regId + " " + service);
}
String name = service.getServiceName();
String type = service.getServiceType();
int port = service.getPort();
byte[] textRecord = service.getTxtRecord();
String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
return mDaemon.execute("register", regId, name, type, port, record);
}
public static NsdService create(Context context) throws InterruptedException {
NsdSettings settings = NsdSettings.makeDefault(context);
HandlerThread thread = new HandlerThread(TAG);
thread.start();
Handler handler = new Handler(thread.getLooper());
NsdService service = new NsdService(context, settings, handler, DaemonConnection::new);
service.mDaemonCallback.awaitConnection();
return service;
}
public static class DaemonConnection {
final NativeDaemonConnector mNativeConnector;
DaemonConnection(NativeCallbackReceiver callback) {
mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
new Thread(mNativeConnector, MDNS_TAG).start();
}
public boolean execute(Object... args) {
if (DBG) {
Slog.d(TAG, "mdnssd " + Arrays.toString(args));
}
try {
mNativeConnector.execute("mdnssd", args);
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
return false;
}
return true;
}
}
Socket读操作来监听mDnsListener层发来的消息:listenToSocket首先实例化一个本地socket对象,LocalSocket对象的LocalSocketAddress地址的 Socket名称为已初始化的mSocket,并使用该地址调用connect函数,从init.rc 可以看到名称为"mdns"的Socket对应的本地服务为netd,因此NativeCallbackReceiver对象与netd服务建立了连接;然后listenToSocket函数调用socket的getInputStream和getOutputStream函数获得输入和输出流对象;最后listenToSocket函数进入while循环不断从输入流读取事件进行分析。解析后的事件发给HandlerThread线程的Handler函数进行处理,在Handler函数中调用mCallbacks的onEvent回调函数,即NsdService服务内部NativeCallbackReceiver对象的onEvent回调函数。
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
if (isShuttingDown()) break;
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
if (isShuttingDown()) break;
SystemClock.sleep(5000);
}
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
mCallbacks.onDaemonConnected();
FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();
// Add our starting point to the count and reset the start.
count += start;
start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
if (start == 0) {
log("RCV incomplete");
}
// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}
-
NsdServer通过Socket往mDnsListener层写操作
在NativeDaemonConnector对象的execute函数中首先根据传进的参数调用makeCommand函数生成一个字符串类型的命令,然后调用本地socket的输出流对象 mOutputStream的write函数来发送命令。
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
...
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
final String rawCmd = rawBuilder.toString();
final String logCmd = logBuilder.toString();
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
...
}
static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber,
String cmd, Object... args) {
if (cmd.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected command: " + cmd);
}
if (cmd.indexOf(' ') >= 0) {
throw new IllegalArgumentException("Arguments must be separate from command");
}
rawBuilder.append(sequenceNumber).append(' ').append(cmd);
logBuilder.append(sequenceNumber).append(' ').append(cmd);
for (Object arg : args) {
final String argString = String.valueOf(arg);
if (argString.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected argument: " + arg);
}
rawBuilder.append(' ');
logBuilder.append(' ');
appendEscaped(rawBuilder, argString);
if (arg instanceof SensitiveArg) {
logBuilder.append("[scrubbed]");
} else {
appendEscaped(logBuilder, argString);
}
}
rawBuilder.append('\0');
}
在本地服务netd的进程中调用其MDnsSdListener对象的startListener函数启动命令的监听。
MDnsSdListener对象通过FrameworkListener间接派生自SocketListener,在MDnsSdListener对象实例化时其成员mSocketName初始化 为"mdns",因此对应的socket通道和NativeCallbackReceiver对象中的socket通道相同。MDnsSdListener实例化时还初始化一个Monitor对象和一个FrameworkCommand类型的Handler对象。
Handler对象初始化时其mCommand属性赋值为"mdnssd",用来和发送来的命令匹配,Handler对象也保存到FrameworkCommand命令列表对象中mCommands。
Monitor对象实例化时调用socketpair函数建立一个Socket组mCtrlSocketPair,还创建一个监听线程,线程中调用Monitor对象的run函数。startListener函数首先调用android_get_control_socket函数根据mSocketName名称获得其SOCKET fd;然后调用listen函数监听socket通道;
然后创建一个线程,在线程中执行runListener函数,在runListener函数循环调用accept接收客户端连接。当有客户端连接后,根据accept返回的socket fd实例化一个SocketClient对象保存到SocketClient对象列表中mClients,并调用onDataAvailable函数。
onDataAvailable函数调用read函数读取客户端发送的命令,并调用dispatchCommand函数提交命令。
在dispatchCommand函数中解析命令参数,并与mCommands命令对象列表进行命令匹配,并调用匹配后命令对象的runCommand函数,这里即调用MDnsSdListener对象中的Handler对象的runCommand函数。在Handler对象的runCommand函数中进行命令参数的匹配,这里匹配的是"register",因此在获得命令参数后调用serviceRegister函数,serviceRegister函数参数包括匹配的SocketClient对象以及命令参数信息。
在serviceRegister函数中,首先调用mMonitor的allocateServiceRef函数根据请求ID实例化一个Element对象放入链表中,并返回Element对象的DNSServiceRef指针,DNSServiceRef指向_DNSServiceRef_t结构,其成员包括DNS操作或应答类型,接收消息回调接口、客户端回调和上下文、客户端与服务端连接socket等参数。
然后调用DNSServiceRegister函数,DNSServiceRegister函数用来向本地MDns后台服务发起连接和消息请求,DNSServiceRegister函数的参数包括allocateServiceRef函数返回的DNSServiceRef指针变量以及serviceRegister传进来的命令请求参数,以及事件接收回调函数MDnsSdListenerRegisterCallback。DNSServiceRegister函数为mDNSResponder开源工程提供的客户端调用API接口,用来与MDns后台服务建立连接,并向其提交请求。
在DNSServiceRegister函数中首先通过ConnectToServer函数与MDns后台服务建立连接。
在ConnectToServer函数首先实例和初始化一个_DNSServiceRef_t类型DNSServiceOp变量,然后创建一个本地socket,且新建socket的文件句柄赋值给DNSServiceOp对象的sockfd。
然后调用connect与MDns后台服务建立连接,最后把实例化后的DNSServiceOp对象通过DNSServiceRef参数带回。
ConnectToServer函数返回后接着调用create_hdr函数为实例化一个ipc_msg_hdr类型的请求消息,并对请求消息赋值后连同ConnectToServer函数带回的DNSServiceRef参数一同传给deliver_request函数,通过deliver_request函数提交请求。在Monitor对象的run函数中循环对mPollFds进行poll操作。
在startMonitoring函数通过向mCtrlSocketPair[1]写入RESCAN命令后,由于mPollFds[0].fd指向mCtrlSocketPair[0],因此mMonitor的run函数在mPollFds[0]通道读取到RESCAN命令并调用RESCAN函数,在RESCAN函数中根据已建立的与服务器的连接为mPollFds的其它通道赋值,这些mPollFds通道的文件句柄位赋值为服务器已建立连接的socket 的句柄。
在服务端的响应事件到来时在这些通道poll到事件,然后调用DNSServiceProcessResult函数,参数为DNSServiceRef。在DNSServiceProcessResult函数中读取响应事件和数据,并调用DNSServiceRef参数的事件回调ProcessReply函数,即对于服务登记请求对应的是MDnsSdListenerRegisterCallback函数。
在MDnsSdListenerRegisterCallback中向Handler对象的监听对象的sendBroadcast函数发送ResponseCode::ServiceRegistrationSucceeded应答消息,Handler对象的监听对象为MDnsSdListener对象本身,因此这里调用SocketListener的sendBroadcast函数。
在sendBroadcast函数中遍历mClients对象的成员对象,并调用其调用sendMsg函数,即调用SocketClient的sendMsg函数。
在sendMsg函数中通过与客户端(即NsdService服务的NativeDaemonConnector对象)建立的SOCKET向客户端发送应答消息。在NsdService的NativeDaemonConnector对象的listenToSocket函数 收到服务端的应答消息后,调用NsdService服务内部NativeCallbackReceiver对象的onEvent回调函数。
在onEvent回调函数中向NsdService服务的状态机发送NsdManager.NATIVE_DAEMON_EVENT事件,假如这时NsdService服务处于EnabledState状态,状态机收到NsdManager.NATIVE_DAEMON_EVENT事件后调用handleNativeEvent函数。
class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
...
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
// 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, cooked);
mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
return true;
}
}
handleNativeEvent函数首先根据响应消息的请求ID从mIdToClientInfoMap中获得先前应用层建立连接时保存的clientInfo对象及从clientInfo对象获得clientId,然后执行响应事件代码为NativeResponseCode.SERVICE_REGISTERED的事件处理,事件处理先根据返回的响应事件实例化一个NsdServiceInfo对象,然后通过clientInfo中的AsyncChannel对象成员向NsdService服务的应用层发送NsdManager.REGISTER_SERVICE_SUCCEEDED响应事件。
private boolean handleNativeEvent(int code, String raw, String[] cooked) {
NsdServiceInfo servInfo;
int id = Integer.parseInt(cooked[1]);
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
if (clientInfo == null) {
String name = NativeResponseCode.nameOf(code);
Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
return false;
}
/* This goes in response as msg.arg2 */
int clientId = clientInfo.getClientId(id);
if (clientId < 0) {
// This can happen because of race conditions. For example,
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
// and we may get in this situation.
String name = NativeResponseCode.nameOf(code);
Slog.d(TAG, String.format(
"Notification %s for listener id %d that is no longer active",
name, id));
return false;
}
if (DBG) {
String name = NativeResponseCode.nameOf(code);
Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
}
switch (code) {
...
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
servInfo = new NsdServiceInfo(cooked[2], null);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
id, clientId, servInfo);
break;
...
}
return true;
}
- NsdManager的事件接收对象ServiceHandler接收到NsdManager.REGISTER_SERVICE_SUCCEEDED响应事件,在其handleMessage函数中调用其监听对象(NSD应用层)的onServiceRegistered回调。到此整个服务登记流程结束。