service_discovery_host_client.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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
3// found in the LICENSE file.
4
5#include "chrome/browser/local_discovery/service_discovery_host_client.h"
6
7#if defined(OS_POSIX)
8#include "base/file_descriptor_posix.h"
9#endif  // OS_POSIX
10
11#include "chrome/common/local_discovery/local_discovery_messages.h"
12#include "content/public/browser/browser_thread.h"
13#include "content/public/browser/utility_process_host.h"
14#include "net/socket/socket_descriptor.h"
15
16namespace local_discovery {
17
18using content::BrowserThread;
19using content::UtilityProcessHost;
20
21class ServiceDiscoveryHostClient::ServiceWatcherProxy : public ServiceWatcher {
22 public:
23  ServiceWatcherProxy(ServiceDiscoveryHostClient* host,
24                      const std::string& service_type,
25                      const ServiceWatcher::UpdatedCallback& callback)
26      : host_(host),
27        service_type_(service_type),
28        id_(host_->RegisterWatcherCallback(callback)),
29        started_(false) {
30  }
31
32  virtual ~ServiceWatcherProxy() {
33    DVLOG(1) << "~ServiceWatcherProxy with id " << id_;
34    host_->UnregisterWatcherCallback(id_);
35    if (started_)
36      host_->Send(new LocalDiscoveryMsg_DestroyWatcher(id_));
37  }
38
39  virtual void Start() OVERRIDE {
40    DVLOG(1) << "ServiceWatcher::Start with id " << id_;
41    DCHECK(!started_);
42    host_->Send(new LocalDiscoveryMsg_StartWatcher(id_, service_type_));
43    started_ = true;
44  }
45
46  virtual void DiscoverNewServices(bool force_update) OVERRIDE {
47    DVLOG(1) << "ServiceWatcher::DiscoverNewServices with id "
48            << id_;
49    DCHECK(started_);
50    host_->Send(new LocalDiscoveryMsg_DiscoverServices(id_, force_update));
51  }
52
53  virtual std::string GetServiceType() const OVERRIDE {
54    return service_type_;
55  }
56
57 private:
58  scoped_refptr<ServiceDiscoveryHostClient> host_;
59  const std::string service_type_;
60  const uint64 id_;
61  bool started_;
62};
63
64class ServiceDiscoveryHostClient::ServiceResolverProxy
65    : public ServiceResolver {
66 public:
67  ServiceResolverProxy(ServiceDiscoveryHostClient* host,
68                       const std::string& service_name,
69                       const ServiceResolver::ResolveCompleteCallback& callback)
70      : host_(host),
71        service_name_(service_name),
72        id_(host->RegisterResolverCallback(callback)),
73        started_(false) {
74  }
75
76  virtual ~ServiceResolverProxy() {
77    DVLOG(1) << "~ServiceResolverProxy with id " << id_;
78    host_->UnregisterResolverCallback(id_);
79    if (started_)
80      host_->Send(new LocalDiscoveryMsg_DestroyResolver(id_));
81  }
82
83  virtual void StartResolving() OVERRIDE {
84    DVLOG(1)
85        << "ServiceResolverProxy::StartResolving with id "
86        << id_;
87    DCHECK(!started_);
88    host_->Send(new LocalDiscoveryMsg_ResolveService(id_, service_name_));
89    started_ = true;
90  }
91
92  virtual std::string GetName() const OVERRIDE {
93    return service_name_;
94  }
95
96 private:
97  scoped_refptr<ServiceDiscoveryHostClient> host_;
98  const std::string service_name_;
99  const uint64 id_;
100  bool started_;
101};
102
103class ServiceDiscoveryHostClient::LocalDomainResolverProxy
104    : public LocalDomainResolver {
105 public:
106  LocalDomainResolverProxy(ServiceDiscoveryHostClient* host,
107                       const std::string& domain,
108                       net::AddressFamily address_family,
109                       const LocalDomainResolver::IPAddressCallback& callback)
110      : host_(host),
111        domain_(domain),
112        address_family_(address_family),
113        id_(host->RegisterLocalDomainResolverCallback(callback)),
114        started_(false) {
115  }
116
117  virtual ~LocalDomainResolverProxy() {
118    DVLOG(1) << "~LocalDomainResolverProxy with id "
119            << id_;
120    host_->UnregisterLocalDomainResolverCallback(id_);
121    if (started_)
122      host_->Send(new LocalDiscoveryMsg_DestroyLocalDomainResolver(id_));
123  }
124
125  virtual void Start() OVERRIDE {
126    DVLOG(1) << "LocalDomainResolverProxy::Start with id "
127            << id_;
128    DCHECK(!started_);
129    host_->Send(new LocalDiscoveryMsg_ResolveLocalDomain(id_, domain_,
130                                                         address_family_));
131    started_ = true;
132  }
133
134 private:
135  scoped_refptr<ServiceDiscoveryHostClient> host_;
136  std::string domain_;
137  net::AddressFamily address_family_;
138  const uint64 id_;
139  bool started_;
140};
141
142ServiceDiscoveryHostClient::ServiceDiscoveryHostClient() : current_id_(0) {
143  callback_runner_ = base::MessageLoop::current()->message_loop_proxy();
144}
145
146ServiceDiscoveryHostClient::~ServiceDiscoveryHostClient() {
147  // The ServiceDiscoveryHostClient may be destroyed from the IO thread or the
148  // owning thread.
149  DetachFromThread();
150  DCHECK(service_watcher_callbacks_.empty());
151  DCHECK(service_resolver_callbacks_.empty());
152  DCHECK(domain_resolver_callbacks_.empty());
153}
154
155scoped_ptr<ServiceWatcher> ServiceDiscoveryHostClient::CreateServiceWatcher(
156    const std::string& service_type,
157    const ServiceWatcher::UpdatedCallback& callback) {
158  DCHECK(CalledOnValidThread());
159  return scoped_ptr<ServiceWatcher>(
160      new ServiceWatcherProxy(this, service_type, callback));
161}
162
163scoped_ptr<ServiceResolver> ServiceDiscoveryHostClient::CreateServiceResolver(
164    const std::string& service_name,
165    const ServiceResolver::ResolveCompleteCallback& callback) {
166  DCHECK(CalledOnValidThread());
167  return scoped_ptr<ServiceResolver>(
168      new ServiceResolverProxy(this, service_name, callback));
169}
170
171scoped_ptr<LocalDomainResolver>
172ServiceDiscoveryHostClient::CreateLocalDomainResolver(
173    const std::string& domain,
174    net::AddressFamily address_family,
175    const LocalDomainResolver::IPAddressCallback& callback) {
176  DCHECK(CalledOnValidThread());
177  return scoped_ptr<LocalDomainResolver>(new LocalDomainResolverProxy(
178      this, domain, address_family, callback));
179}
180
181uint64 ServiceDiscoveryHostClient::RegisterWatcherCallback(
182    const ServiceWatcher::UpdatedCallback& callback) {
183  DCHECK(CalledOnValidThread());
184  DCHECK(!ContainsKey(service_watcher_callbacks_, current_id_ + 1));
185  service_watcher_callbacks_[++current_id_] = callback;
186  return current_id_;
187}
188
189uint64 ServiceDiscoveryHostClient::RegisterResolverCallback(
190    const ServiceResolver::ResolveCompleteCallback& callback) {
191  DCHECK(CalledOnValidThread());
192  DCHECK(!ContainsKey(service_resolver_callbacks_, current_id_ + 1));
193  service_resolver_callbacks_[++current_id_] = callback;
194  return current_id_;
195}
196
197uint64 ServiceDiscoveryHostClient::RegisterLocalDomainResolverCallback(
198    const LocalDomainResolver::IPAddressCallback& callback) {
199  DCHECK(CalledOnValidThread());
200  DCHECK(!ContainsKey(domain_resolver_callbacks_, current_id_ + 1));
201  domain_resolver_callbacks_[++current_id_] = callback;
202  return current_id_;
203}
204
205void ServiceDiscoveryHostClient::UnregisterWatcherCallback(uint64 id) {
206  DCHECK(CalledOnValidThread());
207  service_watcher_callbacks_.erase(id);
208}
209
210void ServiceDiscoveryHostClient::UnregisterResolverCallback(uint64 id) {
211  DCHECK(CalledOnValidThread());
212  service_resolver_callbacks_.erase(id);
213}
214
215void ServiceDiscoveryHostClient::UnregisterLocalDomainResolverCallback(
216    uint64 id) {
217  DCHECK(CalledOnValidThread());
218  domain_resolver_callbacks_.erase(id);
219}
220
221void ServiceDiscoveryHostClient::Start() {
222  DCHECK(CalledOnValidThread());
223  net::NetworkChangeNotifier::AddIPAddressObserver(this);
224  BrowserThread::PostTask(
225      BrowserThread::IO,
226      FROM_HERE,
227      base::Bind(&ServiceDiscoveryHostClient::StartOnIOThread, this));
228}
229
230void ServiceDiscoveryHostClient::Shutdown() {
231  net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
232  DCHECK(CalledOnValidThread());
233  BrowserThread::PostTask(
234      BrowserThread::IO,
235      FROM_HERE,
236      base::Bind(&ServiceDiscoveryHostClient::ShutdownOnIOThread, this));
237}
238
239void ServiceDiscoveryHostClient::StartOnIOThread() {
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241  utility_host_ = UtilityProcessHost::Create(
242      this, base::MessageLoopProxy::current().get())->AsWeakPtr();
243  if (utility_host_) {
244    utility_host_->EnableZygote();
245    utility_host_->EnableMDns();
246    utility_host_->StartBatchMode();
247
248#if defined(OS_POSIX)
249    base::FileDescriptor v4(net::CreatePlatformSocket(AF_INET, SOCK_DGRAM, 0),
250                            true);
251    base::FileDescriptor v6(net::CreatePlatformSocket(AF_INET6, SOCK_DGRAM, 0),
252                            true);
253    LOG_IF(ERROR, v4.fd == net::kInvalidSocket) << "Can't create IPv4 socket.";
254    LOG_IF(ERROR, v6.fd == net::kInvalidSocket) << "Can't create IPv6 socket.";
255    if (v4.fd == net::kInvalidSocket &&
256        v6.fd == net::kInvalidSocket) {
257      ShutdownOnIOThread();
258    } else {
259      utility_host_->Send(new LocalDiscoveryMsg_SetSockets(v4, v6));
260    }
261#endif  // OS_POSIX
262  }
263}
264
265void ServiceDiscoveryHostClient::ShutdownOnIOThread() {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
267  if (utility_host_) {
268    utility_host_->Send(new LocalDiscoveryMsg_ShutdownLocalDiscovery);
269    utility_host_->EndBatchMode();
270  }
271}
272
273void ServiceDiscoveryHostClient::RestartOnIOThread() {
274  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
275
276  ShutdownOnIOThread();
277  StartOnIOThread();
278}
279
280void ServiceDiscoveryHostClient::Send(IPC::Message* msg) {
281  DCHECK(CalledOnValidThread());
282  BrowserThread::PostTask(
283      BrowserThread::IO,
284      FROM_HERE,
285      base::Bind(&ServiceDiscoveryHostClient::SendOnIOThread, this, msg));
286}
287
288void ServiceDiscoveryHostClient::SendOnIOThread(IPC::Message* msg) {
289  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
290  if (utility_host_)
291    utility_host_->Send(msg);
292}
293
294void ServiceDiscoveryHostClient::OnIPAddressChanged() {
295  BrowserThread::PostTask(
296      BrowserThread::IO,
297      FROM_HERE,
298      base::Bind(&ServiceDiscoveryHostClient::RestartOnIOThread, this));
299
300  WatcherCallbacks service_watcher_callbacks;
301  service_watcher_callbacks_.swap(service_watcher_callbacks);
302
303  for (WatcherCallbacks::iterator i = service_watcher_callbacks.begin();
304       i != service_watcher_callbacks.end(); i++) {
305    if (!i->second.is_null()) {
306      i->second.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
307    }
308  }
309}
310
311bool ServiceDiscoveryHostClient::OnMessageReceived(
312    const IPC::Message& message) {
313  bool handled = true;
314  IPC_BEGIN_MESSAGE_MAP(ServiceDiscoveryHostClient, message)
315    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_WatcherCallback,
316                        OnWatcherCallback)
317    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_ResolverCallback,
318                        OnResolverCallback)
319    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_LocalDomainResolverCallback,
320                        OnLocalDomainResolverCallback)
321    IPC_MESSAGE_UNHANDLED(handled = false)
322  IPC_END_MESSAGE_MAP()
323  return handled;
324}
325
326void ServiceDiscoveryHostClient::OnWatcherCallback(
327    uint64 id,
328    ServiceWatcher::UpdateType update,
329    const std::string& service_name) {
330  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
331  callback_runner_->PostTask(
332      FROM_HERE,
333      base::Bind(&ServiceDiscoveryHostClient::RunWatcherCallback, this, id,
334                 update, service_name));
335}
336
337void ServiceDiscoveryHostClient::OnResolverCallback(
338    uint64 id,
339    ServiceResolver::RequestStatus status,
340    const ServiceDescription& description) {
341  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
342  callback_runner_->PostTask(
343      FROM_HERE,
344      base::Bind(&ServiceDiscoveryHostClient::RunResolverCallback, this, id,
345                 status, description));
346}
347
348void ServiceDiscoveryHostClient::OnLocalDomainResolverCallback(
349    uint64 id,
350    bool success,
351    const net::IPAddressNumber& ip_address_ipv4,
352    const net::IPAddressNumber& ip_address_ipv6) {
353  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
354  callback_runner_->PostTask(
355      FROM_HERE,
356      base::Bind(&ServiceDiscoveryHostClient::RunLocalDomainResolverCallback,
357                 this, id, success, ip_address_ipv4, ip_address_ipv6));
358}
359
360void ServiceDiscoveryHostClient::RunWatcherCallback(
361    uint64 id,
362    ServiceWatcher::UpdateType update,
363    const std::string& service_name) {
364  DCHECK(CalledOnValidThread());
365  WatcherCallbacks::iterator it = service_watcher_callbacks_.find(id);
366  if (it != service_watcher_callbacks_.end() && !it->second.is_null())
367    it->second.Run(update, service_name);
368}
369
370void ServiceDiscoveryHostClient::RunResolverCallback(
371    uint64 id,
372    ServiceResolver::RequestStatus status,
373    const ServiceDescription& description) {
374  DCHECK(CalledOnValidThread());
375  ResolverCallbacks::iterator it = service_resolver_callbacks_.find(id);
376  if (it != service_resolver_callbacks_.end() && !it->second.is_null())
377    it->second.Run(status, description);
378}
379
380void ServiceDiscoveryHostClient::RunLocalDomainResolverCallback(
381    uint64 id,
382    bool success,
383    const net::IPAddressNumber& ip_address_ipv4,
384    const net::IPAddressNumber& ip_address_ipv6) {
385  DCHECK(CalledOnValidThread());
386  DomainResolverCallbacks::iterator it = domain_resolver_callbacks_.find(id);
387  if (it != domain_resolver_callbacks_.end() && !it->second.is_null())
388    it->second.Run(success, ip_address_ipv4, ip_address_ipv6);
389}
390
391ServiceDiscoveryHostClientFactory::ServiceDiscoveryHostClientFactory()
392    : instance_(NULL), references_(0) {
393}
394
395ServiceDiscoveryHostClientFactory::~ServiceDiscoveryHostClientFactory() {
396}
397
398// static
399ServiceDiscoveryHostClient* ServiceDiscoveryHostClientFactory::GetClient() {
400  return GetInstance()->GetClientInternal();
401}
402
403// static
404void ServiceDiscoveryHostClientFactory::ReleaseClient() {
405  GetInstance()->ReleaseClientInternal();
406}
407
408// static
409ServiceDiscoveryHostClientFactory*
410ServiceDiscoveryHostClientFactory::GetInstance() {
411  return Singleton<ServiceDiscoveryHostClientFactory>::get();
412}
413
414ServiceDiscoveryHostClient*
415ServiceDiscoveryHostClientFactory::GetClientInternal() {
416  DCHECK(CalledOnValidThread());
417  if (references_ == 0) {
418    instance_ = new ServiceDiscoveryHostClient;
419    instance_->Start();
420  }
421
422  references_++;
423  return instance_.get();
424}
425
426void ServiceDiscoveryHostClientFactory::ReleaseClientInternal() {
427  DCHECK(CalledOnValidThread());
428  references_--;
429  if (references_ == 0) {
430    instance_->Shutdown();
431    instance_ = NULL;
432  }
433}
434
435}  // namespace local_discovery
436