Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / local_discovery / service_discovery_client_mdns.cc
blobfbd077f91ca88f0dcb33ce184e42a0ff3191f93a
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 {
21 public:
22 typedef base::WeakPtr<Proxy> WeakPtr;
24 explicit Proxy(ServiceDiscoveryClientMdns* client)
25 : client_(client),
26 weak_ptr_factory_(this) {
27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
28 client_->proxies_.AddObserver(this);
31 virtual ~Proxy() {
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_);
45 if (IsValid()) {
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|
53 // is deleted.
54 void RunCallback(const base::Closure& callback) {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
56 callback.Run();
59 protected:
60 void PostToMdnsThread(const base::Closure& task) {
61 DCHECK(IsValid());
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);
68 return;
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();
85 template<class T>
86 void DeleteOnMdnsThread(T* t) {
87 if (!t)
88 return;
89 if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
90 delete t;
93 private:
94 scoped_refptr<ServiceDiscoveryClientMdns> client_;
95 // Delayed |mdns_runner_| tasks.
96 std::vector<base::Closure> delayed_tasks_;
97 base::WeakPtrFactory<Proxy> weak_ptr_factory_;
98 DISALLOW_COPY_AND_ASSIGN(Proxy);
101 namespace {
103 const int kMaxRestartAttempts = 10;
104 const int kRestartDelayOnNetworkChangeSeconds = 3;
106 typedef base::Callback<void(bool)> MdnsInitCallback;
108 class SocketFactory : public net::MDnsSocketFactory {
109 public:
110 explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
111 : interfaces_(interfaces) {}
113 // net::MDnsSocketFactory implementation:
114 void CreateSockets(
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));
121 if (socket)
122 sockets->push_back(socket.release());
126 private:
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)));
139 template<class T>
140 class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
141 public:
142 typedef ProxyBase<T> Base;
144 explicit ProxyBase(ServiceDiscoveryClientMdns* client)
145 : Proxy(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());
160 protected:
161 void set_implementation(scoped_ptr<T> implementation) {
162 implementation_ = implementation.Pass();
165 T* implementation() const {
166 return implementation_.get();
169 private:
170 scoped_ptr<T> implementation_;
171 DISALLOW_COPY_AND_ASSIGN(ProxyBase);
174 class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
175 public:
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(
185 service_type,
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()),
201 force_update));
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, "");
221 private:
222 static void OnCallback(const WeakPtr& proxy,
223 const ServiceWatcher::UpdatedCallback& callback,
224 UpdateType a1,
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> {
236 public:
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(
245 service_name,
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_; }
259 private:
260 static void OnCallback(
261 const WeakPtr& proxy,
262 const ServiceResolver::ResolveCompleteCallback& callback,
263 RequestStatus a1,
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> {
275 public:
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(
285 domain,
286 address_family,
287 base::Bind(
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())));
299 private:
300 static void OnCallback(const WeakPtr& proxy,
301 const LocalDomainResolver::IPAddressCallback& callback,
302 bool a1,
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);
313 } // namespace
315 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
316 : mdns_runner_(
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);
323 StartNewClient();
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);
355 DestroyMdns();
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(
371 FROM_HERE,
372 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
373 weak_ptr_factory_.GetWeakPtr()),
374 base::TimeDelta::FromSeconds(
375 kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_)));
376 } else {
377 ReportSuccess();
381 void ServiceDiscoveryClientMdns::StartNewClient() {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 ++restart_attempts_;
384 DestroyMdns();
385 mdns_.reset(net::MDnsClient::CreateDefault().release());
386 client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
387 BrowserThread::PostTaskAndReplyWithResult(
388 BrowserThread::FILE,
389 FROM_HERE,
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(
398 FROM_HERE,
399 base::Bind(&InitMdns,
400 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
401 weak_ptr_factory_.GetWeakPtr()),
402 interfaces,
403 base::Unretained(mdns_.get())));
406 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
408 if (!success) {
409 ScheduleStartNewClient();
410 return;
412 ReportSuccess();
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",
422 restart_attempts_);
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.
435 if (client_)
436 mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
437 if (mdns_)
438 mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
441 } // namespace local_discovery