dns_sd_registry.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/extensions/api/mdns/dns_sd_registry.h"
6
7#include "base/stl_util.h"
8#include "chrome/browser/extensions/api/mdns/dns_sd_device_lister.h"
9#include "chrome/browser/local_discovery/service_discovery_shared_client.h"
10
11using local_discovery::ServiceDiscoveryClient;
12using local_discovery::ServiceDiscoverySharedClient;
13
14namespace extensions {
15
16namespace {
17// Predicate to test if two discovered services have the same service_name.
18class IsSameServiceName {
19 public:
20  explicit IsSameServiceName(const DnsSdService& service) : service_(service) {}
21  bool operator()(const DnsSdService& other) const {
22    return service_.service_name == other.service_name;
23  }
24
25 private:
26  const DnsSdService& service_;
27};
28}  // namespace
29
30DnsSdRegistry::ServiceTypeData::ServiceTypeData(
31    scoped_ptr<DnsSdDeviceLister> lister)
32    : ref_count(1), lister_(lister.Pass()) {}
33
34DnsSdRegistry::ServiceTypeData::~ServiceTypeData() {}
35
36void DnsSdRegistry::ServiceTypeData::ListenerAdded() {
37  ref_count++;
38};
39
40bool DnsSdRegistry::ServiceTypeData::ListenerRemoved() {
41  return --ref_count == 0;
42};
43
44int DnsSdRegistry::ServiceTypeData::GetListenerCount() {
45  return ref_count;
46}
47
48bool DnsSdRegistry::ServiceTypeData::UpdateService(
49      bool added, const DnsSdService& service) {
50  DnsSdRegistry::DnsSdServiceList::iterator it =
51      std::find_if(service_list_.begin(),
52                   service_list_.end(),
53                   IsSameServiceName(service));
54  // Set to true when a service is updated in or added to the registry.
55  bool updated_or_added = added;
56  bool known = (it != service_list_.end());
57  if (known) {
58    // If added == true, but we still found the service in our cache, then just
59    // update the existing entry, but this should not happen!
60    DCHECK(!added);
61    if (*it != service) {
62      *it = service;
63      updated_or_added = true;
64    }
65  } else if (added) {
66    service_list_.push_back(service);
67  }
68
69  VLOG(1) << "UpdateService: " << service.service_name
70          << ", added: " << added
71          << ", known: " << known
72          << ", updated or added: " << updated_or_added;
73  return updated_or_added;
74};
75
76bool DnsSdRegistry::ServiceTypeData::RemoveService(
77    const std::string& service_name) {
78  for (DnsSdRegistry::DnsSdServiceList::iterator it = service_list_.begin();
79       it != service_list_.end(); ++it) {
80    if ((*it).service_name == service_name) {
81      service_list_.erase(it);
82      return true;
83    }
84  }
85  return false;
86};
87
88bool DnsSdRegistry::ServiceTypeData::ClearServices() {
89  if (service_list_.empty())
90    return false;
91
92  service_list_.clear();
93  return true;
94}
95
96const DnsSdRegistry::DnsSdServiceList&
97DnsSdRegistry::ServiceTypeData::GetServiceList() {
98  return service_list_;
99}
100
101DnsSdRegistry::DnsSdRegistry() {
102#if defined(ENABLE_MDNS) || defined(OS_MACOSX)
103  service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
104#endif
105}
106
107DnsSdRegistry::DnsSdRegistry(ServiceDiscoverySharedClient* client) {
108  service_discovery_client_ = client;
109}
110
111DnsSdRegistry::~DnsSdRegistry() {}
112
113void DnsSdRegistry::AddObserver(DnsSdObserver* observer) {
114  observers_.AddObserver(observer);
115}
116
117void DnsSdRegistry::RemoveObserver(DnsSdObserver* observer) {
118  observers_.RemoveObserver(observer);
119}
120
121DnsSdDeviceLister* DnsSdRegistry::CreateDnsSdDeviceLister(
122    DnsSdDelegate* delegate,
123    const std::string& service_type,
124    local_discovery::ServiceDiscoverySharedClient* discovery_client) {
125  return new DnsSdDeviceLister(discovery_client, delegate, service_type);
126}
127
128void DnsSdRegistry::RegisterDnsSdListener(std::string service_type) {
129  VLOG(1) << "RegisterDnsSdListener: " << service_type
130          << ", registered: " << IsRegistered(service_type);
131  if (service_type.empty())
132    return;
133
134  if (IsRegistered(service_type)) {
135    service_data_map_[service_type]->ListenerAdded();
136    DispatchApiEvent(service_type);
137    return;
138  }
139
140  scoped_ptr<DnsSdDeviceLister> dns_sd_device_lister(CreateDnsSdDeviceLister(
141      this, service_type, service_discovery_client_));
142  dns_sd_device_lister->Discover(false);
143  linked_ptr<ServiceTypeData> service_type_data(
144      new ServiceTypeData(dns_sd_device_lister.Pass()));
145  service_data_map_[service_type] = service_type_data;
146  DispatchApiEvent(service_type);
147}
148
149void DnsSdRegistry::UnregisterDnsSdListener(std::string service_type) {
150  VLOG(1) << "UnregisterDnsSdListener: " << service_type;
151  DnsSdRegistry::DnsSdServiceTypeDataMap::iterator it =
152      service_data_map_.find(service_type);
153  if (it == service_data_map_.end())
154    return;
155
156  if (service_data_map_[service_type]->ListenerRemoved())
157    service_data_map_.erase(it);
158}
159
160void DnsSdRegistry::ServiceChanged(const std::string& service_type,
161                                   bool added,
162                                   const DnsSdService& service) {
163  VLOG(1) << "ServiceChanged: service_type: " << service_type
164          << ", known: " << IsRegistered(service_type)
165          << ", service: " << service.service_name
166          << ", added: " << added;
167  if (!IsRegistered(service_type)) {
168    return;
169  }
170
171  bool is_updated =
172      service_data_map_[service_type]->UpdateService(added, service);
173  VLOG(1) << "ServiceChanged: is_updated: " << is_updated;
174
175  if (is_updated) {
176    DispatchApiEvent(service_type);
177  }
178}
179
180void DnsSdRegistry::ServiceRemoved(const std::string& service_type,
181                                   const std::string& service_name) {
182  VLOG(1) << "ServiceRemoved: service_type: " << service_type
183          << ", known: " << IsRegistered(service_type)
184          << ", service: " << service_name;
185  if (!IsRegistered(service_type)) {
186    return;
187  }
188
189  bool is_removed =
190      service_data_map_[service_type]->RemoveService(service_name);
191  VLOG(1) << "ServiceRemoved: is_removed: " << is_removed;
192
193  if (is_removed)
194    DispatchApiEvent(service_type);
195}
196
197void DnsSdRegistry::ServicesFlushed(const std::string& service_type) {
198  VLOG(1) << "ServicesFlushed: service_type: " << service_type
199          << ", known: " << IsRegistered(service_type);
200  if (!IsRegistered(service_type)) {
201    return;
202  }
203
204  bool is_cleared = service_data_map_[service_type]->ClearServices();
205  VLOG(1) << "ServicesFlushed: is_cleared: " << is_cleared;
206
207  if (is_cleared)
208    DispatchApiEvent(service_type);
209}
210
211void DnsSdRegistry::DispatchApiEvent(const std::string& service_type) {
212  // TODO(justinlin): Make this MaybeDispatchApiEvent instead and dispatch if a
213  // dirty bit is set.
214  VLOG(1) << "DispatchApiEvent: service_type: " << service_type;
215  FOR_EACH_OBSERVER(DnsSdObserver, observers_, OnDnsSdEvent(
216      service_type, service_data_map_[service_type]->GetServiceList()));
217}
218
219bool DnsSdRegistry::IsRegistered(const std::string& service_type) {
220  return service_data_map_.find(service_type) != service_data_map_.end();
221}
222
223}  // namespace extensions
224