service_discovery_host_client.cc revision f2477e01787aa58f445919b809d89e252beef54f
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#include "chrome/common/local_discovery/local_discovery_messages.h"
8#include "content/public/browser/browser_thread.h"
9#include "content/public/browser/utility_process_host.h"
10#include "net/socket/socket_descriptor.h"
11
12#if defined(OS_POSIX)
13#include "base/file_descriptor_posix.h"
14#endif  // OS_POSIX
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 " << id_;
48    DCHECK(started_);
49    host_->Send(new LocalDiscoveryMsg_DiscoverServices(id_, force_update));
50  }
51
52  virtual std::string GetServiceType() const OVERRIDE {
53    return service_type_;
54  }
55
56 private:
57  scoped_refptr<ServiceDiscoveryHostClient> host_;
58  const std::string service_type_;
59  const uint64 id_;
60  bool started_;
61};
62
63class ServiceDiscoveryHostClient::ServiceResolverProxy
64    : public ServiceResolver {
65 public:
66  ServiceResolverProxy(ServiceDiscoveryHostClient* host,
67                       const std::string& service_name,
68                       const ServiceResolver::ResolveCompleteCallback& callback)
69      : host_(host),
70        service_name_(service_name),
71        id_(host->RegisterResolverCallback(callback)),
72        started_(false) {
73  }
74
75  virtual ~ServiceResolverProxy() {
76    DVLOG(1) << "~ServiceResolverProxy with id " << id_;
77    host_->UnregisterResolverCallback(id_);
78    if (started_)
79      host_->Send(new LocalDiscoveryMsg_DestroyResolver(id_));
80  }
81
82  virtual void StartResolving() OVERRIDE {
83    DVLOG(1) << "ServiceResolverProxy::StartResolving with id " << id_;
84    DCHECK(!started_);
85    host_->Send(new LocalDiscoveryMsg_ResolveService(id_, service_name_));
86    started_ = true;
87  }
88
89  virtual std::string GetName() const OVERRIDE {
90    return service_name_;
91  }
92
93 private:
94  scoped_refptr<ServiceDiscoveryHostClient> host_;
95  const std::string service_name_;
96  const uint64 id_;
97  bool started_;
98};
99
100class ServiceDiscoveryHostClient::LocalDomainResolverProxy
101    : public LocalDomainResolver {
102 public:
103  LocalDomainResolverProxy(ServiceDiscoveryHostClient* host,
104                       const std::string& domain,
105                       net::AddressFamily address_family,
106                       const LocalDomainResolver::IPAddressCallback& callback)
107      : host_(host),
108        domain_(domain),
109        address_family_(address_family),
110        id_(host->RegisterLocalDomainResolverCallback(callback)),
111        started_(false) {
112  }
113
114  virtual ~LocalDomainResolverProxy() {
115    DVLOG(1) << "~LocalDomainResolverProxy with id " << id_;
116    host_->UnregisterLocalDomainResolverCallback(id_);
117    if (started_)
118      host_->Send(new LocalDiscoveryMsg_DestroyLocalDomainResolver(id_));
119  }
120
121  virtual void Start() OVERRIDE {
122    DVLOG(1) << "LocalDomainResolverProxy::Start with id " << id_;
123    DCHECK(!started_);
124    host_->Send(new LocalDiscoveryMsg_ResolveLocalDomain(id_, domain_,
125                                                         address_family_));
126    started_ = true;
127  }
128
129 private:
130  scoped_refptr<ServiceDiscoveryHostClient> host_;
131  std::string domain_;
132  net::AddressFamily address_family_;
133  const uint64 id_;
134  bool started_;
135};
136
137ServiceDiscoveryHostClient::ServiceDiscoveryHostClient() : current_id_(0) {
138  callback_runner_ = base::MessageLoop::current()->message_loop_proxy();
139  io_runner_ = BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
140}
141
142ServiceDiscoveryHostClient::~ServiceDiscoveryHostClient() {
143  DCHECK(service_watcher_callbacks_.empty());
144  DCHECK(service_resolver_callbacks_.empty());
145  DCHECK(domain_resolver_callbacks_.empty());
146}
147
148scoped_ptr<ServiceWatcher> ServiceDiscoveryHostClient::CreateServiceWatcher(
149    const std::string& service_type,
150    const ServiceWatcher::UpdatedCallback& callback) {
151  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152  return scoped_ptr<ServiceWatcher>(
153      new ServiceWatcherProxy(this, service_type, callback));
154}
155
156scoped_ptr<ServiceResolver> ServiceDiscoveryHostClient::CreateServiceResolver(
157    const std::string& service_name,
158    const ServiceResolver::ResolveCompleteCallback& callback) {
159  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
160  return scoped_ptr<ServiceResolver>(
161      new ServiceResolverProxy(this, service_name, callback));
162}
163
164scoped_ptr<LocalDomainResolver>
165ServiceDiscoveryHostClient::CreateLocalDomainResolver(
166    const std::string& domain,
167    net::AddressFamily address_family,
168    const LocalDomainResolver::IPAddressCallback& callback) {
169  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
170  return scoped_ptr<LocalDomainResolver>(new LocalDomainResolverProxy(
171      this, domain, address_family, callback));
172}
173
174uint64 ServiceDiscoveryHostClient::RegisterWatcherCallback(
175    const ServiceWatcher::UpdatedCallback& callback) {
176  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177  DCHECK(!ContainsKey(service_watcher_callbacks_, current_id_ + 1));
178  service_watcher_callbacks_[++current_id_] = callback;
179  return current_id_;
180}
181
182uint64 ServiceDiscoveryHostClient::RegisterResolverCallback(
183    const ServiceResolver::ResolveCompleteCallback& callback) {
184  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185  DCHECK(!ContainsKey(service_resolver_callbacks_, current_id_ + 1));
186  service_resolver_callbacks_[++current_id_] = callback;
187  return current_id_;
188}
189
190uint64 ServiceDiscoveryHostClient::RegisterLocalDomainResolverCallback(
191    const LocalDomainResolver::IPAddressCallback& callback) {
192  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193  DCHECK(!ContainsKey(domain_resolver_callbacks_, current_id_ + 1));
194  domain_resolver_callbacks_[++current_id_] = callback;
195  return current_id_;
196}
197
198void ServiceDiscoveryHostClient::UnregisterWatcherCallback(uint64 id) {
199  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200  service_watcher_callbacks_.erase(id);
201}
202
203void ServiceDiscoveryHostClient::UnregisterResolverCallback(uint64 id) {
204  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205  service_resolver_callbacks_.erase(id);
206}
207
208void ServiceDiscoveryHostClient::UnregisterLocalDomainResolverCallback(
209    uint64 id) {
210  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211  domain_resolver_callbacks_.erase(id);
212}
213
214void ServiceDiscoveryHostClient::Start(
215    const base::Closure& error_callback) {
216  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
217  DCHECK(!utility_host_);
218  DCHECK(error_callback_.is_null());
219  error_callback_ = error_callback;
220  io_runner_->PostTask(
221      FROM_HERE,
222      base::Bind(&ServiceDiscoveryHostClient::StartOnIOThread, this));
223}
224
225void ServiceDiscoveryHostClient::Shutdown() {
226  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227  io_runner_->PostTask(
228      FROM_HERE,
229      base::Bind(&ServiceDiscoveryHostClient::ShutdownOnIOThread, this));
230}
231
232void ServiceDiscoveryHostClient::StartOnIOThread() {
233  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
234  DCHECK(!utility_host_);
235  utility_host_ = UtilityProcessHost::Create(
236      this, base::MessageLoopProxy::current().get())->AsWeakPtr();
237  if (utility_host_) {
238    utility_host_->EnableMDns();
239    utility_host_->StartBatchMode();
240
241#if defined(OS_POSIX)
242    base::FileDescriptor v4(net::CreatePlatformSocket(AF_INET, SOCK_DGRAM, 0),
243                            true);
244    base::FileDescriptor v6(net::CreatePlatformSocket(AF_INET6, SOCK_DGRAM, 0),
245                            true);
246    LOG_IF(ERROR, v4.fd == net::kInvalidSocket) << "Can't create IPv4 socket.";
247    LOG_IF(ERROR, v6.fd == net::kInvalidSocket) << "Can't create IPv6 socket.";
248    if (v4.fd == net::kInvalidSocket &&
249        v6.fd == net::kInvalidSocket) {
250      ShutdownOnIOThread();
251    } else {
252      utility_host_->Send(new LocalDiscoveryMsg_SetSockets(v4, v6));
253    }
254#endif  // OS_POSIX
255  }
256}
257
258void ServiceDiscoveryHostClient::ShutdownOnIOThread() {
259  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
260  if (utility_host_) {
261    utility_host_->Send(new LocalDiscoveryMsg_ShutdownLocalDiscovery);
262    utility_host_->EndBatchMode();
263    utility_host_.reset();
264  }
265  error_callback_ = base::Closure();
266}
267
268void ServiceDiscoveryHostClient::Send(IPC::Message* msg) {
269  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270  io_runner_->PostTask(
271      FROM_HERE,
272      base::Bind(&ServiceDiscoveryHostClient::SendOnIOThread, this, msg));
273}
274
275void ServiceDiscoveryHostClient::SendOnIOThread(IPC::Message* msg) {
276  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
277  if (utility_host_) {
278    utility_host_->Send(msg);
279  } else {
280    delete msg;
281  }
282}
283
284void ServiceDiscoveryHostClient::OnProcessCrashed(int exit_code) {
285  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
286  DCHECK(!utility_host_);
287  OnError();
288}
289
290bool ServiceDiscoveryHostClient::OnMessageReceived(
291    const IPC::Message& message) {
292  bool handled = true;
293  IPC_BEGIN_MESSAGE_MAP(ServiceDiscoveryHostClient, message)
294    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_Error, OnError)
295    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_WatcherCallback,
296                        OnWatcherCallback)
297    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_ResolverCallback,
298                        OnResolverCallback)
299    IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_LocalDomainResolverCallback,
300                        OnLocalDomainResolverCallback)
301    IPC_MESSAGE_UNHANDLED(handled = false)
302  IPC_END_MESSAGE_MAP()
303  return handled;
304}
305
306void ServiceDiscoveryHostClient::InvalidateWatchers() {
307  WatcherCallbacks service_watcher_callbacks;
308  service_watcher_callbacks_.swap(service_watcher_callbacks);
309  service_resolver_callbacks_.clear();
310  domain_resolver_callbacks_.clear();
311
312  for (WatcherCallbacks::iterator i = service_watcher_callbacks.begin();
313       i != service_watcher_callbacks.end(); i++) {
314    if (!i->second.is_null()) {
315      i->second.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
316    }
317  }
318}
319
320void ServiceDiscoveryHostClient::OnError() {
321  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
322  if (!error_callback_.is_null())
323    callback_runner_->PostTask(FROM_HERE, error_callback_);
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
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
391}  // namespace local_discovery
392