| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/utility/local_discovery/service_discovery_message_handler.h" | 5 #include "chrome/utility/local_discovery/service_discovery_message_handler.h" |
| 6 | 6 |
| 7 #include <stddef.h> |
| 8 |
| 7 #include <algorithm> | 9 #include <algorithm> |
| 8 #include <vector> | 10 #include <vector> |
| 9 | 11 |
| 10 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
| 11 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/macros.h" |
| 12 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
| 17 #include "build/build_config.h" |
| 14 #include "chrome/common/local_discovery/local_discovery_messages.h" | 18 #include "chrome/common/local_discovery/local_discovery_messages.h" |
| 15 #include "chrome/common/local_discovery/service_discovery_client_impl.h" | 19 #include "chrome/common/local_discovery/service_discovery_client_impl.h" |
| 16 #include "content/public/utility/utility_thread.h" | 20 #include "content/public/utility/utility_thread.h" |
| 17 #include "net/base/net_util.h" | 21 #include "net/base/net_util.h" |
| 18 #include "net/socket/socket_descriptor.h" | 22 #include "net/socket/socket_descriptor.h" |
| 19 #include "net/udp/datagram_server_socket.h" | 23 #include "net/udp/datagram_server_socket.h" |
| 20 | 24 |
| 21 namespace local_discovery { | 25 namespace local_discovery { |
| 22 | 26 |
| 23 namespace { | 27 namespace { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 50 } | 54 } |
| 51 | 55 |
| 52 private: | 56 private: |
| 53 net::SocketDescriptor socket_; | 57 net::SocketDescriptor socket_; |
| 54 DISALLOW_COPY_AND_ASSIGN(ScopedSocketFactory); | 58 DISALLOW_COPY_AND_ASSIGN(ScopedSocketFactory); |
| 55 }; | 59 }; |
| 56 | 60 |
| 57 struct SocketInfo { | 61 struct SocketInfo { |
| 58 SocketInfo(net::SocketDescriptor socket, | 62 SocketInfo(net::SocketDescriptor socket, |
| 59 net::AddressFamily address_family, | 63 net::AddressFamily address_family, |
| 60 uint32 interface_index) | 64 uint32_t interface_index) |
| 61 : socket(socket), | 65 : socket(socket), |
| 62 address_family(address_family), | 66 address_family(address_family), |
| 63 interface_index(interface_index) { | 67 interface_index(interface_index) {} |
| 64 } | |
| 65 net::SocketDescriptor socket; | 68 net::SocketDescriptor socket; |
| 66 net::AddressFamily address_family; | 69 net::AddressFamily address_family; |
| 67 uint32 interface_index; | 70 uint32_t interface_index; |
| 68 }; | 71 }; |
| 69 | 72 |
| 70 // Returns list of sockets preallocated before. | 73 // Returns list of sockets preallocated before. |
| 71 class PreCreatedMDnsSocketFactory : public net::MDnsSocketFactory { | 74 class PreCreatedMDnsSocketFactory : public net::MDnsSocketFactory { |
| 72 public: | 75 public: |
| 73 PreCreatedMDnsSocketFactory() {} | 76 PreCreatedMDnsSocketFactory() {} |
| 74 ~PreCreatedMDnsSocketFactory() override { | 77 ~PreCreatedMDnsSocketFactory() override { |
| 75 // Not empty if process exits too fast, before starting mDns code. If | 78 // Not empty if process exits too fast, before starting mDns code. If |
| 76 // happened, destructors may crash accessing destroyed global objects. | 79 // happened, destructors may crash accessing destroyed global objects. |
| 77 // TODO This sounds memory leak, check and do better if possible | 80 // TODO This sounds memory leak, check and do better if possible |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 260 const std::vector<LocalDiscoveryMsg_SocketInfo>& sockets) { | 263 const std::vector<LocalDiscoveryMsg_SocketInfo>& sockets) { |
| 261 for (size_t i = 0; i < sockets.size(); ++i) { | 264 for (size_t i = 0; i < sockets.size(); ++i) { |
| 262 g_local_discovery_socket_factory.Get().AddSocket( | 265 g_local_discovery_socket_factory.Get().AddSocket( |
| 263 SocketInfo(sockets[i].descriptor.fd, sockets[i].address_family, | 266 SocketInfo(sockets[i].descriptor.fd, sockets[i].address_family, |
| 264 sockets[i].interface_index)); | 267 sockets[i].interface_index)); |
| 265 } | 268 } |
| 266 } | 269 } |
| 267 #endif // OS_POSIX | 270 #endif // OS_POSIX |
| 268 | 271 |
| 269 void ServiceDiscoveryMessageHandler::OnStartWatcher( | 272 void ServiceDiscoveryMessageHandler::OnStartWatcher( |
| 270 uint64 id, | 273 uint64_t id, |
| 271 const std::string& service_type) { | 274 const std::string& service_type) { |
| 272 PostTask(FROM_HERE, | 275 PostTask(FROM_HERE, |
| 273 base::Bind(&ServiceDiscoveryMessageHandler::StartWatcher, | 276 base::Bind(&ServiceDiscoveryMessageHandler::StartWatcher, |
| 274 base::Unretained(this), id, service_type)); | 277 base::Unretained(this), id, service_type)); |
| 275 } | 278 } |
| 276 | 279 |
| 277 void ServiceDiscoveryMessageHandler::OnDiscoverServices(uint64 id, | 280 void ServiceDiscoveryMessageHandler::OnDiscoverServices(uint64_t id, |
| 278 bool force_update) { | 281 bool force_update) { |
| 279 PostTask(FROM_HERE, | 282 PostTask(FROM_HERE, |
| 280 base::Bind(&ServiceDiscoveryMessageHandler::DiscoverServices, | 283 base::Bind(&ServiceDiscoveryMessageHandler::DiscoverServices, |
| 281 base::Unretained(this), id, force_update)); | 284 base::Unretained(this), id, force_update)); |
| 282 } | 285 } |
| 283 | 286 |
| 284 void ServiceDiscoveryMessageHandler::OnSetActivelyRefreshServices( | 287 void ServiceDiscoveryMessageHandler::OnSetActivelyRefreshServices( |
| 285 uint64 id, bool actively_refresh_services) { | 288 uint64_t id, |
| 289 bool actively_refresh_services) { |
| 286 PostTask(FROM_HERE, | 290 PostTask(FROM_HERE, |
| 287 base::Bind( | 291 base::Bind( |
| 288 &ServiceDiscoveryMessageHandler::SetActivelyRefreshServices, | 292 &ServiceDiscoveryMessageHandler::SetActivelyRefreshServices, |
| 289 base::Unretained(this), id, actively_refresh_services)); | 293 base::Unretained(this), id, actively_refresh_services)); |
| 290 } | 294 } |
| 291 | 295 |
| 292 void ServiceDiscoveryMessageHandler::OnDestroyWatcher(uint64 id) { | 296 void ServiceDiscoveryMessageHandler::OnDestroyWatcher(uint64_t id) { |
| 293 PostTask(FROM_HERE, | 297 PostTask(FROM_HERE, |
| 294 base::Bind(&ServiceDiscoveryMessageHandler::DestroyWatcher, | 298 base::Bind(&ServiceDiscoveryMessageHandler::DestroyWatcher, |
| 295 base::Unretained(this), id)); | 299 base::Unretained(this), id)); |
| 296 } | 300 } |
| 297 | 301 |
| 298 void ServiceDiscoveryMessageHandler::OnResolveService( | 302 void ServiceDiscoveryMessageHandler::OnResolveService( |
| 299 uint64 id, | 303 uint64_t id, |
| 300 const std::string& service_name) { | 304 const std::string& service_name) { |
| 301 PostTask(FROM_HERE, | 305 PostTask(FROM_HERE, |
| 302 base::Bind(&ServiceDiscoveryMessageHandler::ResolveService, | 306 base::Bind(&ServiceDiscoveryMessageHandler::ResolveService, |
| 303 base::Unretained(this), id, service_name)); | 307 base::Unretained(this), id, service_name)); |
| 304 } | 308 } |
| 305 | 309 |
| 306 void ServiceDiscoveryMessageHandler::OnDestroyResolver(uint64 id) { | 310 void ServiceDiscoveryMessageHandler::OnDestroyResolver(uint64_t id) { |
| 307 PostTask(FROM_HERE, | 311 PostTask(FROM_HERE, |
| 308 base::Bind(&ServiceDiscoveryMessageHandler::DestroyResolver, | 312 base::Bind(&ServiceDiscoveryMessageHandler::DestroyResolver, |
| 309 base::Unretained(this), id)); | 313 base::Unretained(this), id)); |
| 310 } | 314 } |
| 311 | 315 |
| 312 void ServiceDiscoveryMessageHandler::OnResolveLocalDomain( | 316 void ServiceDiscoveryMessageHandler::OnResolveLocalDomain( |
| 313 uint64 id, const std::string& domain, | 317 uint64_t id, |
| 318 const std::string& domain, |
| 314 net::AddressFamily address_family) { | 319 net::AddressFamily address_family) { |
| 315 PostTask(FROM_HERE, | 320 PostTask(FROM_HERE, |
| 316 base::Bind(&ServiceDiscoveryMessageHandler::ResolveLocalDomain, | 321 base::Bind(&ServiceDiscoveryMessageHandler::ResolveLocalDomain, |
| 317 base::Unretained(this), id, domain, address_family)); | 322 base::Unretained(this), id, domain, address_family)); |
| 318 } | 323 } |
| 319 | 324 |
| 320 void ServiceDiscoveryMessageHandler::OnDestroyLocalDomainResolver(uint64 id) { | 325 void ServiceDiscoveryMessageHandler::OnDestroyLocalDomainResolver(uint64_t id) { |
| 321 PostTask(FROM_HERE, | 326 PostTask(FROM_HERE, |
| 322 base::Bind( | 327 base::Bind( |
| 323 &ServiceDiscoveryMessageHandler::DestroyLocalDomainResolver, | 328 &ServiceDiscoveryMessageHandler::DestroyLocalDomainResolver, |
| 324 base::Unretained(this), id)); | 329 base::Unretained(this), id)); |
| 325 } | 330 } |
| 326 | 331 |
| 327 void ServiceDiscoveryMessageHandler::StartWatcher( | 332 void ServiceDiscoveryMessageHandler::StartWatcher( |
| 328 uint64 id, | 333 uint64_t id, |
| 329 const std::string& service_type) { | 334 const std::string& service_type) { |
| 330 VLOG(1) << "StartWatcher, id=" << id << ", type=" << service_type; | 335 VLOG(1) << "StartWatcher, id=" << id << ", type=" << service_type; |
| 331 if (!service_discovery_client_) | 336 if (!service_discovery_client_) |
| 332 return; | 337 return; |
| 333 DCHECK(!ContainsKey(service_watchers_, id)); | 338 DCHECK(!ContainsKey(service_watchers_, id)); |
| 334 scoped_ptr<ServiceWatcher> watcher( | 339 scoped_ptr<ServiceWatcher> watcher( |
| 335 service_discovery_client_->CreateServiceWatcher( | 340 service_discovery_client_->CreateServiceWatcher( |
| 336 service_type, | 341 service_type, |
| 337 base::Bind(&ServiceDiscoveryMessageHandler::OnServiceUpdated, | 342 base::Bind(&ServiceDiscoveryMessageHandler::OnServiceUpdated, |
| 338 base::Unretained(this), id))); | 343 base::Unretained(this), id))); |
| 339 watcher->Start(); | 344 watcher->Start(); |
| 340 service_watchers_[id].reset(watcher.release()); | 345 service_watchers_[id].reset(watcher.release()); |
| 341 } | 346 } |
| 342 | 347 |
| 343 void ServiceDiscoveryMessageHandler::DiscoverServices(uint64 id, | 348 void ServiceDiscoveryMessageHandler::DiscoverServices(uint64_t id, |
| 344 bool force_update) { | 349 bool force_update) { |
| 345 VLOG(1) << "DiscoverServices, id=" << id; | 350 VLOG(1) << "DiscoverServices, id=" << id; |
| 346 if (!service_discovery_client_) | 351 if (!service_discovery_client_) |
| 347 return; | 352 return; |
| 348 DCHECK(ContainsKey(service_watchers_, id)); | 353 DCHECK(ContainsKey(service_watchers_, id)); |
| 349 service_watchers_[id]->DiscoverNewServices(force_update); | 354 service_watchers_[id]->DiscoverNewServices(force_update); |
| 350 } | 355 } |
| 351 | 356 |
| 352 void ServiceDiscoveryMessageHandler::SetActivelyRefreshServices( | 357 void ServiceDiscoveryMessageHandler::SetActivelyRefreshServices( |
| 353 uint64 id, | 358 uint64_t id, |
| 354 bool actively_refresh_services) { | 359 bool actively_refresh_services) { |
| 355 VLOG(1) << "ActivelyRefreshServices, id=" << id; | 360 VLOG(1) << "ActivelyRefreshServices, id=" << id; |
| 356 if (!service_discovery_client_) | 361 if (!service_discovery_client_) |
| 357 return; | 362 return; |
| 358 DCHECK(ContainsKey(service_watchers_, id)); | 363 DCHECK(ContainsKey(service_watchers_, id)); |
| 359 service_watchers_[id]->SetActivelyRefreshServices(actively_refresh_services); | 364 service_watchers_[id]->SetActivelyRefreshServices(actively_refresh_services); |
| 360 } | 365 } |
| 361 | 366 |
| 362 void ServiceDiscoveryMessageHandler::DestroyWatcher(uint64 id) { | 367 void ServiceDiscoveryMessageHandler::DestroyWatcher(uint64_t id) { |
| 363 VLOG(1) << "DestoryWatcher, id=" << id; | 368 VLOG(1) << "DestoryWatcher, id=" << id; |
| 364 if (!service_discovery_client_) | 369 if (!service_discovery_client_) |
| 365 return; | 370 return; |
| 366 service_watchers_.erase(id); | 371 service_watchers_.erase(id); |
| 367 } | 372 } |
| 368 | 373 |
| 369 void ServiceDiscoveryMessageHandler::ResolveService( | 374 void ServiceDiscoveryMessageHandler::ResolveService( |
| 370 uint64 id, | 375 uint64_t id, |
| 371 const std::string& service_name) { | 376 const std::string& service_name) { |
| 372 VLOG(1) << "ResolveService, id=" << id << ", name=" << service_name; | 377 VLOG(1) << "ResolveService, id=" << id << ", name=" << service_name; |
| 373 if (!service_discovery_client_) | 378 if (!service_discovery_client_) |
| 374 return; | 379 return; |
| 375 DCHECK(!ContainsKey(service_resolvers_, id)); | 380 DCHECK(!ContainsKey(service_resolvers_, id)); |
| 376 scoped_ptr<ServiceResolver> resolver( | 381 scoped_ptr<ServiceResolver> resolver( |
| 377 service_discovery_client_->CreateServiceResolver( | 382 service_discovery_client_->CreateServiceResolver( |
| 378 service_name, | 383 service_name, |
| 379 base::Bind(&ServiceDiscoveryMessageHandler::OnServiceResolved, | 384 base::Bind(&ServiceDiscoveryMessageHandler::OnServiceResolved, |
| 380 base::Unretained(this), id))); | 385 base::Unretained(this), id))); |
| 381 resolver->StartResolving(); | 386 resolver->StartResolving(); |
| 382 service_resolvers_[id].reset(resolver.release()); | 387 service_resolvers_[id].reset(resolver.release()); |
| 383 } | 388 } |
| 384 | 389 |
| 385 void ServiceDiscoveryMessageHandler::DestroyResolver(uint64 id) { | 390 void ServiceDiscoveryMessageHandler::DestroyResolver(uint64_t id) { |
| 386 VLOG(1) << "DestroyResolver, id=" << id; | 391 VLOG(1) << "DestroyResolver, id=" << id; |
| 387 if (!service_discovery_client_) | 392 if (!service_discovery_client_) |
| 388 return; | 393 return; |
| 389 service_resolvers_.erase(id); | 394 service_resolvers_.erase(id); |
| 390 } | 395 } |
| 391 | 396 |
| 392 void ServiceDiscoveryMessageHandler::ResolveLocalDomain( | 397 void ServiceDiscoveryMessageHandler::ResolveLocalDomain( |
| 393 uint64 id, | 398 uint64_t id, |
| 394 const std::string& domain, | 399 const std::string& domain, |
| 395 net::AddressFamily address_family) { | 400 net::AddressFamily address_family) { |
| 396 VLOG(1) << "ResolveLocalDomain, id=" << id << ", domain=" << domain; | 401 VLOG(1) << "ResolveLocalDomain, id=" << id << ", domain=" << domain; |
| 397 if (!service_discovery_client_) | 402 if (!service_discovery_client_) |
| 398 return; | 403 return; |
| 399 DCHECK(!ContainsKey(local_domain_resolvers_, id)); | 404 DCHECK(!ContainsKey(local_domain_resolvers_, id)); |
| 400 scoped_ptr<LocalDomainResolver> resolver( | 405 scoped_ptr<LocalDomainResolver> resolver( |
| 401 service_discovery_client_->CreateLocalDomainResolver( | 406 service_discovery_client_->CreateLocalDomainResolver( |
| 402 domain, address_family, | 407 domain, address_family, |
| 403 base::Bind(&ServiceDiscoveryMessageHandler::OnLocalDomainResolved, | 408 base::Bind(&ServiceDiscoveryMessageHandler::OnLocalDomainResolved, |
| 404 base::Unretained(this), id))); | 409 base::Unretained(this), id))); |
| 405 resolver->Start(); | 410 resolver->Start(); |
| 406 local_domain_resolvers_[id].reset(resolver.release()); | 411 local_domain_resolvers_[id].reset(resolver.release()); |
| 407 } | 412 } |
| 408 | 413 |
| 409 void ServiceDiscoveryMessageHandler::DestroyLocalDomainResolver(uint64 id) { | 414 void ServiceDiscoveryMessageHandler::DestroyLocalDomainResolver(uint64_t id) { |
| 410 VLOG(1) << "DestroyLocalDomainResolver, id=" << id; | 415 VLOG(1) << "DestroyLocalDomainResolver, id=" << id; |
| 411 if (!service_discovery_client_) | 416 if (!service_discovery_client_) |
| 412 return; | 417 return; |
| 413 local_domain_resolvers_.erase(id); | 418 local_domain_resolvers_.erase(id); |
| 414 } | 419 } |
| 415 | 420 |
| 416 void ServiceDiscoveryMessageHandler::ShutdownLocalDiscovery() { | 421 void ServiceDiscoveryMessageHandler::ShutdownLocalDiscovery() { |
| 417 if (!discovery_task_runner_.get()) | 422 if (!discovery_task_runner_.get()) |
| 418 return; | 423 return; |
| 419 | 424 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 430 void ServiceDiscoveryMessageHandler::ShutdownOnIOThread() { | 435 void ServiceDiscoveryMessageHandler::ShutdownOnIOThread() { |
| 431 VLOG(1) << "ShutdownLocalDiscovery"; | 436 VLOG(1) << "ShutdownLocalDiscovery"; |
| 432 service_watchers_.clear(); | 437 service_watchers_.clear(); |
| 433 service_resolvers_.clear(); | 438 service_resolvers_.clear(); |
| 434 local_domain_resolvers_.clear(); | 439 local_domain_resolvers_.clear(); |
| 435 service_discovery_client_.reset(); | 440 service_discovery_client_.reset(); |
| 436 mdns_client_.reset(); | 441 mdns_client_.reset(); |
| 437 } | 442 } |
| 438 | 443 |
| 439 void ServiceDiscoveryMessageHandler::OnServiceUpdated( | 444 void ServiceDiscoveryMessageHandler::OnServiceUpdated( |
| 440 uint64 id, | 445 uint64_t id, |
| 441 ServiceWatcher::UpdateType update, | 446 ServiceWatcher::UpdateType update, |
| 442 const std::string& name) { | 447 const std::string& name) { |
| 443 VLOG(1) << "OnServiceUpdated, id=" << id | 448 VLOG(1) << "OnServiceUpdated, id=" << id |
| 444 << ", status=" << WatcherUpdateToString(update) << ", name=" << name; | 449 << ", status=" << WatcherUpdateToString(update) << ", name=" << name; |
| 445 DCHECK(service_discovery_client_); | 450 DCHECK(service_discovery_client_); |
| 446 | 451 |
| 447 Send(new LocalDiscoveryHostMsg_WatcherCallback(id, update, name)); | 452 Send(new LocalDiscoveryHostMsg_WatcherCallback(id, update, name)); |
| 448 } | 453 } |
| 449 | 454 |
| 450 void ServiceDiscoveryMessageHandler::OnServiceResolved( | 455 void ServiceDiscoveryMessageHandler::OnServiceResolved( |
| 451 uint64 id, | 456 uint64_t id, |
| 452 ServiceResolver::RequestStatus status, | 457 ServiceResolver::RequestStatus status, |
| 453 const ServiceDescription& description) { | 458 const ServiceDescription& description) { |
| 454 VLOG(1) << "OnServiceResolved, id=" << id | 459 VLOG(1) << "OnServiceResolved, id=" << id |
| 455 << ", status=" << ResolverStatusToString(status) | 460 << ", status=" << ResolverStatusToString(status) |
| 456 << ", name=" << description.service_name; | 461 << ", name=" << description.service_name; |
| 457 | 462 |
| 458 DCHECK(service_discovery_client_); | 463 DCHECK(service_discovery_client_); |
| 459 Send(new LocalDiscoveryHostMsg_ResolverCallback(id, status, description)); | 464 Send(new LocalDiscoveryHostMsg_ResolverCallback(id, status, description)); |
| 460 } | 465 } |
| 461 | 466 |
| 462 void ServiceDiscoveryMessageHandler::OnLocalDomainResolved( | 467 void ServiceDiscoveryMessageHandler::OnLocalDomainResolved( |
| 463 uint64 id, | 468 uint64_t id, |
| 464 bool success, | 469 bool success, |
| 465 const net::IPAddressNumber& address_ipv4, | 470 const net::IPAddressNumber& address_ipv4, |
| 466 const net::IPAddressNumber& address_ipv6) { | 471 const net::IPAddressNumber& address_ipv6) { |
| 467 VLOG(1) << "OnLocalDomainResolved, id=" << id | 472 VLOG(1) << "OnLocalDomainResolved, id=" << id |
| 468 << ", IPv4=" << (address_ipv4.empty() ? "" : | 473 << ", IPv4=" << (address_ipv4.empty() ? "" : |
| 469 net::IPAddressToString(address_ipv4)) | 474 net::IPAddressToString(address_ipv4)) |
| 470 << ", IPv6=" << (address_ipv6.empty() ? "" : | 475 << ", IPv6=" << (address_ipv6.empty() ? "" : |
| 471 net::IPAddressToString(address_ipv6)); | 476 net::IPAddressToString(address_ipv6)); |
| 472 | 477 |
| 473 DCHECK(service_discovery_client_); | 478 DCHECK(service_discovery_client_); |
| 474 Send(new LocalDiscoveryHostMsg_LocalDomainResolverCallback( | 479 Send(new LocalDiscoveryHostMsg_LocalDomainResolverCallback( |
| 475 id, success, address_ipv4, address_ipv6)); | 480 id, success, address_ipv4, address_ipv6)); |
| 476 } | 481 } |
| 477 | 482 |
| 478 void ServiceDiscoveryMessageHandler::Send(IPC::Message* msg) { | 483 void ServiceDiscoveryMessageHandler::Send(IPC::Message* msg) { |
| 479 utility_task_runner_->PostTask(FROM_HERE, | 484 utility_task_runner_->PostTask(FROM_HERE, |
| 480 base::Bind(&SendHostMessageOnUtilityThread, | 485 base::Bind(&SendHostMessageOnUtilityThread, |
| 481 msg)); | 486 msg)); |
| 482 } | 487 } |
| 483 | 488 |
| 484 } // namespace local_discovery | 489 } // namespace local_discovery |
| OLD | NEW |