1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/local_discovery/service_discovery_client_mdns.h"
7 #include "base/location.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/metrics/histogram.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "chrome/common/local_discovery/service_discovery_client_impl.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/dns/mdns_client.h"
15 #include "net/udp/datagram_server_socket.h"
17 namespace local_discovery
{
19 using content::BrowserThread
;
21 // Base class for objects returned by ServiceDiscoveryClient implementation.
22 // Handles interaction of client code on UI thread end net code on mdns thread.
23 class ServiceDiscoveryClientMdns::Proxy
{
25 typedef base::WeakPtr
<Proxy
> WeakPtr
;
27 explicit Proxy(ServiceDiscoveryClientMdns
* client
)
29 weak_ptr_factory_(this) {
30 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
31 client_
->proxies_
.AddObserver(this);
35 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
36 client_
->proxies_
.RemoveObserver(this);
39 // Returns true if object is not yet shutdown.
40 virtual bool IsValid() = 0;
42 // Notifies proxies that mDNS layer is going to be destroyed.
43 virtual void OnMdnsDestroy() = 0;
45 // Notifies proxies that new mDNS instance is ready.
46 virtual void OnNewMdnsReady() {
47 DCHECK(!client_
->need_dalay_mdns_tasks_
);
49 for (size_t i
= 0; i
< delayed_tasks_
.size(); ++i
)
50 client_
->mdns_runner_
->PostTask(FROM_HERE
, delayed_tasks_
[i
]);
52 delayed_tasks_
.clear();
55 // Runs callback using this method to abort callback if instance of |Proxy|
57 void RunCallback(const base::Closure
& callback
) {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
63 void PostToMdnsThread(const base::Closure
& task
) {
65 // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
66 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
67 // running on FILE thread, so |PostToMdnsThread| could be called to post
68 // task for |mdns_| that is not initialized yet.
69 if (!client_
->need_dalay_mdns_tasks_
) {
70 client_
->mdns_runner_
->PostTask(FROM_HERE
, task
);
73 delayed_tasks_
.push_back(task
);
76 static bool PostToUIThread(const base::Closure
& task
) {
77 return BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, task
);
80 ServiceDiscoveryClient
* client() {
81 return client_
->client_
.get();
84 WeakPtr
GetWeakPtr() {
85 return weak_ptr_factory_
.GetWeakPtr();
89 void DeleteOnMdnsThread(T
* t
) {
92 if (!client_
->mdns_runner_
->DeleteSoon(FROM_HERE
, t
))
97 scoped_refptr
<ServiceDiscoveryClientMdns
> client_
;
98 // Delayed |mdns_runner_| tasks.
99 std::vector
<base::Closure
> delayed_tasks_
;
100 base::WeakPtrFactory
<Proxy
> weak_ptr_factory_
;
101 DISALLOW_COPY_AND_ASSIGN(Proxy
);
106 const int kMaxRestartAttempts
= 10;
107 const int kRestartDelayOnNetworkChangeSeconds
= 3;
109 typedef base::Callback
<void(bool)> MdnsInitCallback
;
111 class SocketFactory
: public net::MDnsSocketFactory
{
113 explicit SocketFactory(const net::InterfaceIndexFamilyList
& interfaces
)
114 : interfaces_(interfaces
) {}
116 // net::MDnsSocketFactory implementation:
118 ScopedVector
<net::DatagramServerSocket
>* sockets
) override
{
119 for (size_t i
= 0; i
< interfaces_
.size(); ++i
) {
120 DCHECK(interfaces_
[i
].second
== net::ADDRESS_FAMILY_IPV4
||
121 interfaces_
[i
].second
== net::ADDRESS_FAMILY_IPV6
);
122 scoped_ptr
<net::DatagramServerSocket
> socket(
123 CreateAndBindMDnsSocket(interfaces_
[i
].second
, interfaces_
[i
].first
));
125 sockets
->push_back(socket
.release());
130 net::InterfaceIndexFamilyList interfaces_
;
133 void InitMdns(const MdnsInitCallback
& on_initialized
,
134 const net::InterfaceIndexFamilyList
& interfaces
,
135 net::MDnsClient
* mdns
) {
136 SocketFactory
socket_factory(interfaces
);
137 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
138 base::Bind(on_initialized
,
139 mdns
->StartListening(&socket_factory
)));
143 class ProxyBase
: public ServiceDiscoveryClientMdns::Proxy
, public T
{
145 typedef ProxyBase
<T
> Base
;
147 explicit ProxyBase(ServiceDiscoveryClientMdns
* client
)
151 ~ProxyBase() override
{
152 DeleteOnMdnsThread(implementation_
.release());
155 bool IsValid() override
{
156 return !!implementation();
159 void OnMdnsDestroy() override
{
160 DeleteOnMdnsThread(implementation_
.release());
164 void set_implementation(scoped_ptr
<T
> implementation
) {
165 implementation_
= implementation
.Pass();
168 T
* implementation() const {
169 return implementation_
.get();
173 scoped_ptr
<T
> implementation_
;
174 DISALLOW_COPY_AND_ASSIGN(ProxyBase
);
177 class ServiceWatcherProxy
: public ProxyBase
<ServiceWatcher
> {
179 ServiceWatcherProxy(ServiceDiscoveryClientMdns
* client_mdns
,
180 const std::string
& service_type
,
181 const ServiceWatcher::UpdatedCallback
& callback
)
182 : ProxyBase(client_mdns
),
183 service_type_(service_type
),
184 callback_(callback
) {
185 // It's safe to call |CreateServiceWatcher| on UI thread, because
186 // |MDnsClient| is not used there. It's simplify implementation.
187 set_implementation(client()->CreateServiceWatcher(
189 base::Bind(&ServiceWatcherProxy::OnCallback
, GetWeakPtr(), callback
)));
192 // ServiceWatcher methods.
193 void Start() override
{
194 if (implementation()) {
195 PostToMdnsThread(base::Bind(&ServiceWatcher::Start
,
196 base::Unretained(implementation())));
200 void DiscoverNewServices(bool force_update
) override
{
201 if (implementation()) {
202 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices
,
203 base::Unretained(implementation()),
208 void SetActivelyRefreshServices(bool actively_refresh_services
) override
{
209 if (implementation()) {
210 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices
,
211 base::Unretained(implementation()),
212 actively_refresh_services
));
216 std::string
GetServiceType() const override
{ return service_type_
; }
218 void OnNewMdnsReady() override
{
219 ProxyBase
<ServiceWatcher
>::OnNewMdnsReady();
220 if (!implementation())
221 callback_
.Run(ServiceWatcher::UPDATE_INVALIDATED
, "");
225 static void OnCallback(const WeakPtr
& proxy
,
226 const ServiceWatcher::UpdatedCallback
& callback
,
228 const std::string
& a2
) {
229 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
230 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
231 base::Bind(callback
, a1
, a2
)));
233 std::string service_type_
;
234 ServiceWatcher::UpdatedCallback callback_
;
235 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy
);
238 class ServiceResolverProxy
: public ProxyBase
<ServiceResolver
> {
240 ServiceResolverProxy(ServiceDiscoveryClientMdns
* client_mdns
,
241 const std::string
& service_name
,
242 const ServiceResolver::ResolveCompleteCallback
& callback
)
243 : ProxyBase(client_mdns
),
244 service_name_(service_name
) {
245 // It's safe to call |CreateServiceResolver| on UI thread, because
246 // |MDnsClient| is not used there. It's simplify implementation.
247 set_implementation(client()->CreateServiceResolver(
249 base::Bind(&ServiceResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
252 // ServiceResolver methods.
253 void StartResolving() override
{
254 if (implementation()) {
255 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving
,
256 base::Unretained(implementation())));
260 std::string
GetName() const override
{ return service_name_
; }
263 static void OnCallback(
264 const WeakPtr
& proxy
,
265 const ServiceResolver::ResolveCompleteCallback
& callback
,
267 const ServiceDescription
& a2
) {
268 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
269 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
270 base::Bind(callback
, a1
, a2
)));
273 std::string service_name_
;
274 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy
);
277 class LocalDomainResolverProxy
: public ProxyBase
<LocalDomainResolver
> {
279 LocalDomainResolverProxy(
280 ServiceDiscoveryClientMdns
* client_mdns
,
281 const std::string
& domain
,
282 net::AddressFamily address_family
,
283 const LocalDomainResolver::IPAddressCallback
& callback
)
284 : ProxyBase(client_mdns
) {
285 // It's safe to call |CreateLocalDomainResolver| on UI thread, because
286 // |MDnsClient| is not used there. It's simplify implementation.
287 set_implementation(client()->CreateLocalDomainResolver(
291 &LocalDomainResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
294 // LocalDomainResolver methods.
295 void Start() override
{
296 if (implementation()) {
297 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start
,
298 base::Unretained(implementation())));
303 static void OnCallback(const WeakPtr
& proxy
,
304 const LocalDomainResolver::IPAddressCallback
& callback
,
306 const net::IPAddressNumber
& a2
,
307 const net::IPAddressNumber
& a3
) {
308 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
309 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
310 base::Bind(callback
, a1
, a2
, a3
)));
313 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy
);
318 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
320 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
)),
321 restart_attempts_(0),
322 need_dalay_mdns_tasks_(true),
323 weak_ptr_factory_(this) {
324 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
325 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
329 scoped_ptr
<ServiceWatcher
> ServiceDiscoveryClientMdns::CreateServiceWatcher(
330 const std::string
& service_type
,
331 const ServiceWatcher::UpdatedCallback
& callback
) {
332 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
333 return scoped_ptr
<ServiceWatcher
>(
334 new ServiceWatcherProxy(this, service_type
, callback
));
337 scoped_ptr
<ServiceResolver
> ServiceDiscoveryClientMdns::CreateServiceResolver(
338 const std::string
& service_name
,
339 const ServiceResolver::ResolveCompleteCallback
& callback
) {
340 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
341 return scoped_ptr
<ServiceResolver
>(
342 new ServiceResolverProxy(this, service_name
, callback
));
345 scoped_ptr
<LocalDomainResolver
>
346 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
347 const std::string
& domain
,
348 net::AddressFamily address_family
,
349 const LocalDomainResolver::IPAddressCallback
& callback
) {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
351 return scoped_ptr
<LocalDomainResolver
>(
352 new LocalDomainResolverProxy(this, domain
, address_family
, callback
));
355 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
356 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
357 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
361 void ServiceDiscoveryClientMdns::OnNetworkChanged(
362 net::NetworkChangeNotifier::ConnectionType type
) {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
364 // Only network changes resets counter.
365 restart_attempts_
= 0;
366 ScheduleStartNewClient();
369 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
371 OnBeforeMdnsDestroy();
372 if (restart_attempts_
< kMaxRestartAttempts
) {
373 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
374 FROM_HERE
, base::Bind(&ServiceDiscoveryClientMdns::StartNewClient
,
375 weak_ptr_factory_
.GetWeakPtr()),
376 base::TimeDelta::FromSeconds(kRestartDelayOnNetworkChangeSeconds
*
377 (1 << restart_attempts_
)));
383 void ServiceDiscoveryClientMdns::StartNewClient() {
384 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
387 mdns_
.reset(net::MDnsClient::CreateDefault().release());
388 client_
.reset(new ServiceDiscoveryClientImpl(mdns_
.get()));
389 BrowserThread::PostTaskAndReplyWithResult(
392 base::Bind(&net::GetMDnsInterfacesToBind
),
393 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady
,
394 weak_ptr_factory_
.GetWeakPtr()));
397 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
398 const net::InterfaceIndexFamilyList
& interfaces
) {
399 mdns_runner_
->PostTask(
401 base::Bind(&InitMdns
,
402 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized
,
403 weak_ptr_factory_
.GetWeakPtr()),
405 base::Unretained(mdns_
.get())));
408 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success
) {
409 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
411 ScheduleStartNewClient();
416 // Initialization is done, no need to delay tasks.
417 need_dalay_mdns_tasks_
= false;
418 FOR_EACH_OBSERVER(Proxy
, proxies_
, OnNewMdnsReady());
421 void ServiceDiscoveryClientMdns::ReportSuccess() {
422 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
423 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
427 void ServiceDiscoveryClientMdns::OnBeforeMdnsDestroy() {
428 need_dalay_mdns_tasks_
= true;
429 weak_ptr_factory_
.InvalidateWeakPtrs();
430 FOR_EACH_OBSERVER(Proxy
, proxies_
, OnMdnsDestroy());
433 void ServiceDiscoveryClientMdns::DestroyMdns() {
434 OnBeforeMdnsDestroy();
435 // After calling |Proxy::OnMdnsDestroy| all references to client_ and mdns_
436 // should be destroyed.
438 mdns_runner_
->DeleteSoon(FROM_HERE
, client_
.release());
440 mdns_runner_
->DeleteSoon(FROM_HERE
, mdns_
.release());
443 } // namespace local_discovery