1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.android_scripting.facade.net.nsd; 18 19 import android.content.Context; 20 import android.net.nsd.NsdManager; 21 import android.net.nsd.NsdServiceInfo; 22 23 import com.googlecode.android_scripting.facade.EventFacade; 24 import com.googlecode.android_scripting.facade.FacadeManager; 25 import com.googlecode.android_scripting.jsonrpc.JsonSerializable; 26 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 27 import com.googlecode.android_scripting.rpc.Rpc; 28 import com.googlecode.android_scripting.rpc.RpcParameter; 29 30 import org.json.JSONException; 31 import org.json.JSONObject; 32 33 import java.net.InetAddress; 34 import java.net.UnknownHostException; 35 import java.util.HashMap; 36 import java.util.Map; 37 38 /** 39 * Facade for NsdManager - exposing mDNS utilities. 40 */ 41 public class NsdManagerFacade extends RpcReceiver { 42 /** 43 * NsdServiceInfo JSON constants 44 */ 45 private static final String NSD_SERVICE_INFO_HOST = "serviceInfoHost"; 46 private static final String NSD_SERVICE_INFO_PORT = "serviceInfoPort"; 47 private static final String NSD_SERVICE_INFO_SERVICE_NAME = "serviceInfoServiceName"; 48 private static final String NSD_SERVICE_INFO_SERVICE_TYPE = "serviceInfoServiceType"; 49 50 /** 51 * RegisterationListener events/data 52 */ 53 private static final String REG_LISTENER_EVENT = "NsdRegistrationListener"; 54 55 private static final String REG_LISTENER_EVENT_ON_REG_FAILED = "OnRegistrationFailed"; 56 private static final String REG_LISTENER_EVENT_ON_SERVICE_REGISTERED = "OnServiceRegistered"; 57 private static final String REG_LISTENER_EVENT_ON_SERVICE_UNREG = "OnServiceUnregistered"; 58 private static final String REG_LISTENER_EVENT_ON_UNREG_FAILED = "OnUnregistrationFailed"; 59 60 private static final String REG_LISTENER_DATA_ID = "id"; 61 private static final String REG_LISTENER_CALLBACK = "callback"; 62 private static final String REG_LISTENER_ERROR_CODE = "error_code"; 63 64 /** 65 * DiscoveryListener events/data 66 */ 67 private static final String DISCOVERY_LISTENER_EVENT = "NsdDiscoveryListener"; 68 69 private static final String DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STARTED = 70 "OnDiscoveryStarted"; 71 private static final String DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STOPPED = 72 "OnDiscoveryStopped"; 73 private static final String DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND = "OnServiceFound"; 74 private static final String DISCOVERY_LISTENER_EVENT_ON_SERVICE_LOST = "OnServiceLost"; 75 private static final String DISCOVERY_LISTENER_EVENT_ON_START_DISCOVERY_FAILED = 76 "OnStartDiscoveryFailed"; 77 private static final String DISCOVERY_LISTENER_EVENT_ON_STOP_DISCOVERY_FAILED = 78 "OnStopDiscoveryFailed"; 79 80 private static final String DISCOVERY_LISTENER_DATA_ID = "id"; 81 private static final String DISCOVERY_LISTENER_DATA_CALLBACK = "callback"; 82 private static final String DISCOVERY_LISTENER_DATA_SERVICE_TYPE = "service_type"; 83 private static final String DISCOVERY_LISTENER_DATA_ERROR_CODE = "error_code"; 84 85 /** 86 * ResolveListener events/data 87 */ 88 private static final String RESOLVE_LISTENER_EVENT = "NsdResolveListener"; 89 90 private static final String RESOLVE_LISTENER_EVENT_ON_RESOLVE_FAIL = "OnResolveFail"; 91 private static final String RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED = "OnServiceResolved"; 92 93 private static final String RESOLVE_LISTENER_DATA_ID = "id"; 94 private static final String RESOLVE_LISTENER_DATA_CALLBACK = "callback"; 95 private static final String RESOLVE_LISTENER_DATA_ERROR_CODE = "error_code"; 96 97 // facade class data 98 99 private final EventFacade mEventFacade; 100 101 private final NsdManager mNsdManager; 102 private final Map<String, RegistrationListener> mRegistrationListeners = new HashMap<>(); 103 private final Map<String, DiscoveryListener> mDiscoveryListeners = new HashMap<>(); 104 private final Map<String, ResolveListener> mResolveListener = new HashMap<>(); 105 106 private static NsdServiceInfo getNsdServiceInfo(JSONObject j) 107 throws JSONException, UnknownHostException { 108 if (j == null) { 109 return null; 110 } 111 112 NsdServiceInfo nsi = new NsdServiceInfo(); 113 114 if (j.has(NSD_SERVICE_INFO_SERVICE_NAME)) { 115 nsi.setServiceName(j.getString(NSD_SERVICE_INFO_SERVICE_NAME)); 116 } 117 if (j.has(NSD_SERVICE_INFO_SERVICE_TYPE)) { 118 nsi.setServiceType(j.getString(NSD_SERVICE_INFO_SERVICE_TYPE)); 119 } 120 if (j.has(NSD_SERVICE_INFO_PORT)) { 121 nsi.setPort(j.getInt(NSD_SERVICE_INFO_PORT)); 122 } 123 if (j.has(NSD_SERVICE_INFO_HOST)) { 124 nsi.setHost(InetAddress.getByName(j.getString(NSD_SERVICE_INFO_HOST))); 125 } 126 127 return nsi; 128 } 129 130 private static void addNsdServiceInfo(JSONObject j, NsdServiceInfo nsi) { 131 try { 132 j.put(NSD_SERVICE_INFO_SERVICE_NAME, nsi.getServiceName()); 133 j.put(NSD_SERVICE_INFO_SERVICE_TYPE, nsi.getServiceType()); 134 j.put(NSD_SERVICE_INFO_HOST, nsi.getHost()); 135 j.put(NSD_SERVICE_INFO_PORT, nsi.getPort()); 136 } catch (JSONException e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 public NsdManagerFacade(FacadeManager manager) { 142 super(manager); 143 mNsdManager = (NsdManager) manager.getService().getSystemService(Context.NSD_SERVICE); 144 mEventFacade = manager.getReceiver(EventFacade.class); 145 } 146 147 @Override 148 public void shutdown() { 149 if (mNsdManager != null) { 150 for (RegistrationListener listener : mRegistrationListeners.values()) { 151 mNsdManager.unregisterService(listener); 152 } 153 mRegistrationListeners.clear(); 154 155 for (NsdManager.DiscoveryListener listener : mDiscoveryListeners.values()) { 156 mNsdManager.stopServiceDiscovery(listener); 157 } 158 mDiscoveryListeners.clear(); 159 160 mResolveListener.clear(); 161 } 162 } 163 164 /** 165 * Facade for NsdManager.registerService(). 166 */ 167 @Rpc(description = "register a service (starts advertising)") 168 public String nsdRegisterService( 169 @RpcParameter(name = "service information") JSONObject serviceInfo) 170 throws JSONException, UnknownHostException { 171 NsdServiceInfo nsi = getNsdServiceInfo(serviceInfo); 172 RegistrationListener listener = new RegistrationListener(); 173 mRegistrationListeners.put(listener.mListenerId, listener); 174 mNsdManager.registerService(nsi, NsdManager.PROTOCOL_DNS_SD, listener); 175 return listener.mListenerId; 176 } 177 178 /** 179 * Facade for NsdManager.unregisterService(). 180 */ 181 @Rpc(description = "unregister a service (stops advertising)") 182 public Boolean nsdUnregisterService( 183 @RpcParameter(name = "service registration callback ID") String id) { 184 RegistrationListener listener = mRegistrationListeners.get(id); 185 if (listener != null) { 186 mNsdManager.unregisterService(listener); 187 return true; 188 } else { 189 return false; 190 } 191 } 192 193 /** 194 * Facade for NsdManager.discoverServices(). 195 */ 196 @Rpc(description = "start service discovery") 197 public String nsdDiscoverServices(@RpcParameter(name = "service type") String serviceType) { 198 DiscoveryListener listener = new DiscoveryListener(); 199 mDiscoveryListeners.put(listener.mListenerId, listener); 200 mNsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, listener); 201 return listener.mListenerId; 202 } 203 204 /** 205 * Facade for NsdManager.stopServiceDiscovery(). 206 */ 207 @Rpc(description = "stop service discovery") 208 public Boolean nsdStopServiceDiscovery( 209 @RpcParameter(name = "service discovery callback ID") String id) { 210 DiscoveryListener listener = mDiscoveryListeners.get(id); 211 if (listener != null) { 212 mNsdManager.stopServiceDiscovery(listener); 213 return true; 214 } else { 215 return false; 216 } 217 } 218 219 /** 220 * Facade for NsdManager.resolveService(). 221 */ 222 @Rpc(description = "resolved discovered service info") 223 public String nsdResolveService( 224 @RpcParameter(name = "service information") JSONObject serviceInfo) 225 throws JSONException, UnknownHostException { 226 ResolveListener listener = new ResolveListener(); 227 mResolveListener.put(listener.mListenerId, listener); 228 mNsdManager.resolveService(getNsdServiceInfo(serviceInfo), listener); 229 return listener.mListenerId; 230 } 231 232 private class RegistrationListener implements NsdManager.RegistrationListener { 233 final String mListenerId; 234 235 RegistrationListener() { 236 mListenerId = "com.googlecode.android_scripting.facade.net.nsd.RegistrationListener: " 237 + System.identityHashCode(this); 238 } 239 240 @Override 241 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 242 mEventFacade.postEvent( 243 REG_LISTENER_EVENT, 244 new RegistrationListenerEvent(REG_LISTENER_EVENT_ON_REG_FAILED, serviceInfo, 245 errorCode)); 246 } 247 248 @Override 249 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 250 mEventFacade.postEvent( 251 REG_LISTENER_EVENT, 252 new RegistrationListenerEvent(REG_LISTENER_EVENT_ON_UNREG_FAILED, serviceInfo, 253 errorCode)); 254 } 255 256 @Override 257 public void onServiceRegistered(NsdServiceInfo serviceInfo) { 258 mEventFacade.postEvent( 259 REG_LISTENER_EVENT, 260 new RegistrationListenerEvent(REG_LISTENER_EVENT_ON_SERVICE_REGISTERED, 261 serviceInfo, null)); 262 } 263 264 @Override 265 public void onServiceUnregistered(NsdServiceInfo serviceInfo) { 266 mEventFacade.postEvent( 267 REG_LISTENER_EVENT, 268 new RegistrationListenerEvent(REG_LISTENER_EVENT_ON_SERVICE_UNREG, serviceInfo, 269 null)); 270 } 271 272 private class RegistrationListenerEvent implements JsonSerializable { 273 private final String mCallback; 274 private final NsdServiceInfo mServiceInfo; 275 private final Integer mErrorCode; 276 277 RegistrationListenerEvent(String callback, NsdServiceInfo serviceInfo, 278 Integer errorCode) { 279 mCallback = callback; 280 mServiceInfo = serviceInfo; 281 mErrorCode = errorCode; 282 } 283 284 @Override 285 public JSONObject toJSON() throws JSONException { 286 JSONObject j = new JSONObject(); 287 288 j.put(REG_LISTENER_DATA_ID, mListenerId); 289 j.put(REG_LISTENER_CALLBACK, mCallback); 290 addNsdServiceInfo(j, mServiceInfo); 291 if (mErrorCode != null) { 292 j.put(REG_LISTENER_ERROR_CODE, mErrorCode.intValue()); 293 } 294 295 return j; 296 } 297 } 298 } 299 300 private class DiscoveryListener implements NsdManager.DiscoveryListener { 301 final String mListenerId; 302 303 DiscoveryListener() { 304 mListenerId = "com.googlecode.android_scripting.facade.net.nsd.DiscoveryListener: " 305 + System.identityHashCode(this); 306 } 307 308 @Override 309 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 310 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 311 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_START_DISCOVERY_FAILED, 312 serviceType, null, errorCode)); 313 } 314 315 @Override 316 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 317 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 318 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_STOP_DISCOVERY_FAILED, 319 serviceType, null, errorCode)); 320 } 321 322 @Override 323 public void onDiscoveryStarted(String serviceType) { 324 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 325 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STARTED, 326 serviceType, null, null)); 327 } 328 329 @Override 330 public void onDiscoveryStopped(String serviceType) { 331 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 332 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STOPPED, 333 serviceType, null, null)); 334 } 335 336 @Override 337 public void onServiceFound(NsdServiceInfo serviceInfo) { 338 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 339 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND, null, 340 serviceInfo, null)); 341 } 342 343 @Override 344 public void onServiceLost(NsdServiceInfo serviceInfo) { 345 mEventFacade.postEvent(DISCOVERY_LISTENER_EVENT, 346 new DiscoveryListenerEvent(DISCOVERY_LISTENER_EVENT_ON_SERVICE_LOST, null, 347 serviceInfo, null)); 348 } 349 350 private class DiscoveryListenerEvent implements JsonSerializable { 351 private final String mCallback; 352 private final String mServiceType; 353 private final NsdServiceInfo mServiceInfo; 354 private final Integer mErrorCode; 355 356 DiscoveryListenerEvent(String callback, String serviceType, NsdServiceInfo serviceInfo, 357 Integer errorCode) { 358 mCallback = callback; 359 mServiceType = serviceType; 360 mServiceInfo = serviceInfo; 361 mErrorCode = errorCode; 362 } 363 364 @Override 365 public JSONObject toJSON() throws JSONException { 366 JSONObject j = new JSONObject(); 367 368 j.put(DISCOVERY_LISTENER_DATA_ID, mListenerId); 369 j.put(DISCOVERY_LISTENER_DATA_CALLBACK, mCallback); 370 if (mServiceType != null) { 371 j.put(DISCOVERY_LISTENER_DATA_SERVICE_TYPE, mServiceType); 372 } 373 if (mServiceInfo != null) { 374 addNsdServiceInfo(j, mServiceInfo); 375 } 376 if (mErrorCode != null) { 377 j.put(DISCOVERY_LISTENER_DATA_ERROR_CODE, mErrorCode.intValue()); 378 } 379 380 return j; 381 } 382 } 383 } 384 385 private class ResolveListener implements NsdManager.ResolveListener { 386 final String mListenerId; 387 388 ResolveListener() { 389 mListenerId = "com.googlecode.android_scripting.facade.net.nsd.ResolveListener: " 390 + System.identityHashCode(this); 391 } 392 393 @Override 394 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { 395 mEventFacade.postEvent(RESOLVE_LISTENER_EVENT, 396 new ResolveListenerEvent(RESOLVE_LISTENER_EVENT_ON_RESOLVE_FAIL, serviceInfo, 397 errorCode)); 398 } 399 400 @Override 401 public void onServiceResolved(NsdServiceInfo serviceInfo) { 402 mEventFacade.postEvent(RESOLVE_LISTENER_EVENT, 403 new ResolveListenerEvent(RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED, 404 serviceInfo, null)); 405 } 406 407 private class ResolveListenerEvent implements JsonSerializable { 408 private final String mCallback; 409 private final NsdServiceInfo mServiceInfo; 410 private final Integer mErrorCode; 411 412 ResolveListenerEvent(String callback, NsdServiceInfo serviceInfo, 413 Integer errorCode) { 414 mCallback = callback; 415 mServiceInfo = serviceInfo; 416 mErrorCode = errorCode; 417 } 418 419 @Override 420 public JSONObject toJSON() throws JSONException { 421 JSONObject j = new JSONObject(); 422 423 j.put(RESOLVE_LISTENER_DATA_ID, mListenerId); 424 j.put(RESOLVE_LISTENER_DATA_CALLBACK, mCallback); 425 addNsdServiceInfo(j, mServiceInfo); 426 if (mErrorCode != null) { 427 j.put(RESOLVE_LISTENER_DATA_ERROR_CODE, mErrorCode.intValue()); 428 } 429 430 return j; 431 } 432 } 433 } 434 } 435