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/memory/scoped_vector.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/common/local_discovery/service_discovery_client_impl.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "net/dns/mdns_client.h"
12 #include "net/udp/datagram_server_socket.h"
14 namespace local_discovery {
16 using content::BrowserThread;
18 // Base class for objects returned by ServiceDiscoveryClient implementation.
19 // Handles interaction of client code on UI thread end net code on mdns thread.
20 class ServiceDiscoveryClientMdns::Proxy {
22 typedef base::WeakPtr<Proxy> WeakPtr;
24 explicit Proxy(ServiceDiscoveryClientMdns* client)
26 weak_ptr_factory_(this) {
27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
28 client_->proxies_.AddObserver(this);
32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
33 client_->proxies_.RemoveObserver(this);
36 // Returns true if object is not yet shutdown.
37 virtual bool IsValid() = 0;
39 // Notifies proxies that mDNS layer is going to be destroyed.
40 virtual void OnMdnsDestroy() = 0;
42 // Notifies proxies that new mDNS instance is ready.
43 virtual void OnNewMdnsReady() {
44 DCHECK(!client_->need_dalay_mdns_tasks_);
46 for (size_t i = 0; i < delayed_tasks_.size(); ++i)
47 client_->mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]);
49 delayed_tasks_.clear();
52 // Runs callback using this method to abort callback if instance of |Proxy|
54 void RunCallback(const base::Closure& callback) {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 void PostToMdnsThread(const base::Closure& task) {
62 // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
63 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
64 // running on FILE thread, so |PostToMdnsThread| could be called to post
65 // task for |mdns_| that is not initialized yet.
66 if (!client_->need_dalay_mdns_tasks_) {
67 client_->mdns_runner_->PostTask(FROM_HERE, task);
70 delayed_tasks_.push_back(task);
73 static bool PostToUIThread(const base::Closure& task) {
74 return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
77 ServiceDiscoveryClient* client() {
78 return client_->client_.get();
81 WeakPtr GetWeakPtr() {
82 return weak_ptr_factory_.GetWeakPtr();
86 void DeleteOnMdnsThread(T* t) {
89 if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
94 scoped_refptr<ServiceDiscoveryClientMdns> client_;
95 base::WeakPtrFactory<Proxy> weak_ptr_factory_;
96 // Delayed |mdns_runner_| tasks.
97 std::vector<base::Closure> delayed_tasks_;
98 DISALLOW_COPY_AND_ASSIGN(Proxy);
103 const int kMaxRestartAttempts = 10;
104 const int kRestartDelayOnNetworkChangeSeconds = 3;
106 typedef base::Callback<void(bool)> MdnsInitCallback;
108 class SocketFactory : public net::MDnsSocketFactory {
110 explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
111 : interfaces_(interfaces) {}
113 // net::MDnsSocketFactory implementation:
115 ScopedVector<net::DatagramServerSocket>* sockets) override {
116 for (size_t i = 0; i < interfaces_.size(); ++i) {
117 DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 ||
118 interfaces_[i].second == net::ADDRESS_FAMILY_IPV6);
119 scoped_ptr<net::DatagramServerSocket> socket(
120 CreateAndBindMDnsSocket(interfaces_[i].second, interfaces_[i].first));
122 sockets->push_back(socket.release());
127 net::InterfaceIndexFamilyList interfaces_;
130 void InitMdns(const MdnsInitCallback& on_initialized,
131 const net::InterfaceIndexFamilyList& interfaces,
132 net::MDnsClient* mdns) {
133 SocketFactory socket_factory(interfaces);
134 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
135 base::Bind(on_initialized,
136 mdns->StartListening(&socket_factory)));
140 class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
142 typedef ProxyBase<T> Base;
144 explicit ProxyBase(ServiceDiscoveryClientMdns* client)
148 virtual ~ProxyBase() {
149 DeleteOnMdnsThread(implementation_.release());
152 virtual bool IsValid() override {
153 return !!implementation();
156 virtual void OnMdnsDestroy() override {
157 DeleteOnMdnsThread(implementation_.release());
161 void set_implementation(scoped_ptr<T> implementation) {
162 implementation_ = implementation.Pass();
165 T* implementation() const {
166 return implementation_.get();
170 scoped_ptr<T> implementation_;
171 DISALLOW_COPY_AND_ASSIGN(ProxyBase);
174 class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
176 ServiceWatcherProxy(ServiceDiscoveryClientMdns* client_mdns,
177 const std::string& service_type,
178 const ServiceWatcher::UpdatedCallback& callback)
179 : ProxyBase(client_mdns),
180 service_type_(service_type),
181 callback_(callback) {
182 // It's safe to call |CreateServiceWatcher| on UI thread, because
183 // |MDnsClient| is not used there. It's simplify implementation.
184 set_implementation(client()->CreateServiceWatcher(
186 base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback)));
189 // ServiceWatcher methods.
190 void Start() override {
191 if (implementation()) {
192 PostToMdnsThread(base::Bind(&ServiceWatcher::Start,
193 base::Unretained(implementation())));
197 void DiscoverNewServices(bool force_update) override {
198 if (implementation()) {
199 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices,
200 base::Unretained(implementation()),
205 void SetActivelyRefreshServices(bool actively_refresh_services) override {
206 if (implementation()) {
207 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices,
208 base::Unretained(implementation()),
209 actively_refresh_services));
213 std::string GetServiceType() const override { return service_type_; }
215 void OnNewMdnsReady() override {
216 ProxyBase<ServiceWatcher>::OnNewMdnsReady();
217 if (!implementation())
218 callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
222 static void OnCallback(const WeakPtr& proxy,
223 const ServiceWatcher::UpdatedCallback& callback,
225 const std::string& a2) {
226 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
227 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
228 base::Bind(callback, a1, a2)));
230 std::string service_type_;
231 ServiceWatcher::UpdatedCallback callback_;
232 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy);
235 class ServiceResolverProxy : public ProxyBase<ServiceResolver> {
237 ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns,
238 const std::string& service_name,
239 const ServiceResolver::ResolveCompleteCallback& callback)
240 : ProxyBase(client_mdns),
241 service_name_(service_name) {
242 // It's safe to call |CreateServiceResolver| on UI thread, because
243 // |MDnsClient| is not used there. It's simplify implementation.
244 set_implementation(client()->CreateServiceResolver(
246 base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback)));
249 // ServiceResolver methods.
250 void StartResolving() override {
251 if (implementation()) {
252 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving,
253 base::Unretained(implementation())));
257 std::string GetName() const override { return service_name_; }
260 static void OnCallback(
261 const WeakPtr& proxy,
262 const ServiceResolver::ResolveCompleteCallback& callback,
264 const ServiceDescription& a2) {
265 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
266 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
267 base::Bind(callback, a1, a2)));
270 std::string service_name_;
271 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy);
274 class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> {
276 LocalDomainResolverProxy(
277 ServiceDiscoveryClientMdns* client_mdns,
278 const std::string& domain,
279 net::AddressFamily address_family,
280 const LocalDomainResolver::IPAddressCallback& callback)
281 : ProxyBase(client_mdns) {
282 // It's safe to call |CreateLocalDomainResolver| on UI thread, because
283 // |MDnsClient| is not used there. It's simplify implementation.
284 set_implementation(client()->CreateLocalDomainResolver(
288 &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback)));
291 // LocalDomainResolver methods.
292 void Start() override {
293 if (implementation()) {
294 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start,
295 base::Unretained(implementation())));
300 static void OnCallback(const WeakPtr& proxy,
301 const LocalDomainResolver::IPAddressCallback& callback,
303 const net::IPAddressNumber& a2,
304 const net::IPAddressNumber& a3) {
305 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
306 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
307 base::Bind(callback, a1, a2, a3)));
310 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy);
315 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
317 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)),
318 restart_attempts_(0),
319 need_dalay_mdns_tasks_(true),
320 weak_ptr_factory_(this) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
326 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher(
327 const std::string& service_type,
328 const ServiceWatcher::UpdatedCallback& callback) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330 return scoped_ptr<ServiceWatcher>(
331 new ServiceWatcherProxy(this, service_type, callback));
334 scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver(
335 const std::string& service_name,
336 const ServiceResolver::ResolveCompleteCallback& callback) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338 return scoped_ptr<ServiceResolver>(
339 new ServiceResolverProxy(this, service_name, callback));
342 scoped_ptr<LocalDomainResolver>
343 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
344 const std::string& domain,
345 net::AddressFamily address_family,
346 const LocalDomainResolver::IPAddressCallback& callback) {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348 return scoped_ptr<LocalDomainResolver>(
349 new LocalDomainResolverProxy(this, domain, address_family, callback));
352 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
358 void ServiceDiscoveryClientMdns::OnNetworkChanged(
359 net::NetworkChangeNotifier::ConnectionType type) {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361 // Only network changes resets counter.
362 restart_attempts_ = 0;
363 ScheduleStartNewClient();
366 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
368 OnBeforeMdnsDestroy();
369 if (restart_attempts_ < kMaxRestartAttempts) {
370 base::MessageLoop::current()->PostDelayedTask(
372 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
373 weak_ptr_factory_.GetWeakPtr()),
374 base::TimeDelta::FromSeconds(
375 kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_)));
381 void ServiceDiscoveryClientMdns::StartNewClient() {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
385 mdns_.reset(net::MDnsClient::CreateDefault().release());
386 client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
387 BrowserThread::PostTaskAndReplyWithResult(
390 base::Bind(&net::GetMDnsInterfacesToBind),
391 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady,
392 weak_ptr_factory_.GetWeakPtr()));
395 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
396 const net::InterfaceIndexFamilyList& interfaces) {
397 mdns_runner_->PostTask(
399 base::Bind(&InitMdns,
400 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
401 weak_ptr_factory_.GetWeakPtr()),
403 base::Unretained(mdns_.get())));
406 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
409 ScheduleStartNewClient();
414 // Initialization is done, no need to delay tasks.
415 need_dalay_mdns_tasks_ = false;
416 FOR_EACH_OBSERVER(Proxy, proxies_, OnNewMdnsReady());
419 void ServiceDiscoveryClientMdns::ReportSuccess() {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
425 void ServiceDiscoveryClientMdns::OnBeforeMdnsDestroy() {
426 need_dalay_mdns_tasks_ = true;
427 weak_ptr_factory_.InvalidateWeakPtrs();
428 FOR_EACH_OBSERVER(Proxy, proxies_, OnMdnsDestroy());
431 void ServiceDiscoveryClientMdns::DestroyMdns() {
432 OnBeforeMdnsDestroy();
433 // After calling |Proxy::OnMdnsDestroy| all references to client_ and mdns_
434 // should be destroyed.
436 mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
438 mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
441 } // namespace local_discovery