1 // Copyright (c) 2012 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/extensions/api/bluetooth/bluetooth_event_router.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/stl_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/api/bluetooth/bluetooth_api_pairing_delegate.h"
20 #include "chrome/browser/extensions/api/bluetooth/bluetooth_api_utils.h"
21 #include "chrome/browser/extensions/api/bluetooth/bluetooth_private_api.h"
22 #include "chrome/common/extensions/api/bluetooth.h"
23 #include "chrome/common/extensions/api/bluetooth_private.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "device/bluetooth/bluetooth_adapter.h"
27 #include "device/bluetooth/bluetooth_adapter_factory.h"
28 #include "device/bluetooth/bluetooth_device.h"
29 #include "device/bluetooth/bluetooth_discovery_session.h"
30 #include "device/bluetooth/bluetooth_profile.h"
31 #include "device/bluetooth/bluetooth_socket.h"
32 #include "extensions/browser/event_router.h"
33 #include "extensions/browser/extension_host.h"
35 namespace extensions
{
37 namespace bluetooth
= api::bluetooth
;
38 namespace bt_private
= api::bluetooth_private
;
40 // A struct storing a Bluetooth profile and the extension that added it.
41 struct BluetoothEventRouter::ExtensionBluetoothProfileRecord
{
42 std::string extension_id
;
43 device::BluetoothProfile
* profile
;
46 BluetoothEventRouter::BluetoothEventRouter(content::BrowserContext
* context
)
47 : browser_context_(context
),
49 num_event_listeners_(0),
50 weak_ptr_factory_(this) {
51 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
52 DCHECK(browser_context_
);
54 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
55 content::Source
<content::BrowserContext
>(browser_context_
));
57 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
58 content::Source
<content::BrowserContext
>(browser_context_
));
61 BluetoothEventRouter::~BluetoothEventRouter() {
62 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
64 adapter_
->RemoveObserver(this);
67 CleanUpAllExtensions();
70 bool BluetoothEventRouter::IsBluetoothSupported() const {
71 return adapter_
.get() ||
72 device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable();
75 void BluetoothEventRouter::GetAdapter(
76 const device::BluetoothAdapterFactory::AdapterCallback
& callback
) {
78 callback
.Run(scoped_refptr
<device::BluetoothAdapter
>(adapter_
));
82 device::BluetoothAdapterFactory::GetAdapter(callback
);
85 void BluetoothEventRouter::AddProfile(
86 const device::BluetoothUUID
& uuid
,
87 const std::string
& extension_id
,
88 device::BluetoothProfile
* bluetooth_profile
) {
89 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
90 DCHECK(!HasProfile(uuid
));
91 ExtensionBluetoothProfileRecord record
= { extension_id
, bluetooth_profile
};
92 bluetooth_profile_map_
[uuid
] = record
;
95 void BluetoothEventRouter::RemoveProfile(const device::BluetoothUUID
& uuid
) {
96 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
97 BluetoothProfileMap::iterator iter
= bluetooth_profile_map_
.find(uuid
);
98 if (iter
!= bluetooth_profile_map_
.end()) {
99 device::BluetoothProfile
* bluetooth_profile
= iter
->second
.profile
;
100 bluetooth_profile_map_
.erase(iter
);
101 bluetooth_profile
->Unregister();
105 bool BluetoothEventRouter::HasProfile(const device::BluetoothUUID
& uuid
) const {
106 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
107 return bluetooth_profile_map_
.find(uuid
) != bluetooth_profile_map_
.end();
110 void BluetoothEventRouter::StartDiscoverySession(
111 device::BluetoothAdapter
* adapter
,
112 const std::string
& extension_id
,
113 const base::Closure
& callback
,
114 const base::Closure
& error_callback
) {
115 if (adapter
!= adapter_
.get()) {
116 error_callback
.Run();
119 DiscoverySessionMap::iterator iter
=
120 discovery_session_map_
.find(extension_id
);
121 if (iter
!= discovery_session_map_
.end() && iter
->second
->IsActive()) {
122 DVLOG(1) << "An active discovery session exists for extension.";
123 error_callback
.Run();
126 adapter
->StartDiscoverySession(
127 base::Bind(&BluetoothEventRouter::OnStartDiscoverySession
,
128 weak_ptr_factory_
.GetWeakPtr(),
134 void BluetoothEventRouter::StopDiscoverySession(
135 device::BluetoothAdapter
* adapter
,
136 const std::string
& extension_id
,
137 const base::Closure
& callback
,
138 const base::Closure
& error_callback
) {
139 if (adapter
!= adapter_
.get()) {
140 error_callback
.Run();
143 DiscoverySessionMap::iterator iter
=
144 discovery_session_map_
.find(extension_id
);
145 if (iter
== discovery_session_map_
.end() || !iter
->second
->IsActive()) {
146 DVLOG(1) << "No active discovery session exists for extension.";
147 error_callback
.Run();
150 device::BluetoothDiscoverySession
* session
= iter
->second
;
151 session
->Stop(callback
, error_callback
);
154 device::BluetoothProfile
* BluetoothEventRouter::GetProfile(
155 const device::BluetoothUUID
& uuid
) const {
156 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
157 BluetoothProfileMap::const_iterator iter
= bluetooth_profile_map_
.find(uuid
);
158 if (iter
!= bluetooth_profile_map_
.end())
159 return iter
->second
.profile
;
164 BluetoothApiPairingDelegate
* BluetoothEventRouter::GetPairingDelegate(
165 const std::string
& extension_id
) {
166 return ContainsKey(pairing_delegate_map_
, extension_id
)
167 ? pairing_delegate_map_
[extension_id
]
171 void BluetoothEventRouter::OnAdapterInitialized(
172 const base::Closure
& callback
,
173 scoped_refptr
<device::BluetoothAdapter
> adapter
) {
174 if (!adapter_
.get()) {
176 adapter_
->AddObserver(this);
182 void BluetoothEventRouter::MaybeReleaseAdapter() {
183 if (adapter_
.get() && num_event_listeners_
== 0 &&
184 pairing_delegate_map_
.empty()) {
185 adapter_
->RemoveObserver(this);
190 void BluetoothEventRouter::AddPairingDelegate(const std::string
& extension_id
) {
191 if (!adapter_
.get()) {
192 base::Closure self_callback
=
193 base::Bind(&BluetoothEventRouter::AddPairingDelegate
,
194 weak_ptr_factory_
.GetWeakPtr(),
196 GetAdapter(base::Bind(&BluetoothEventRouter::OnAdapterInitialized
,
197 weak_ptr_factory_
.GetWeakPtr(),
202 if (!ContainsKey(pairing_delegate_map_
, extension_id
)) {
203 BluetoothApiPairingDelegate
* delegate
=
204 new BluetoothApiPairingDelegate(extension_id
, browser_context_
);
205 DCHECK(adapter_
.get());
206 adapter_
->AddPairingDelegate(
207 delegate
, device::BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH
);
208 pairing_delegate_map_
[extension_id
] = delegate
;
210 LOG(ERROR
) << "Pairing delegate already exists for extension. "
211 << "There should be at most one onPairing listener.";
216 void BluetoothEventRouter::RemovePairingDelegate(
217 const std::string
& extension_id
) {
218 if (ContainsKey(pairing_delegate_map_
, extension_id
)) {
219 BluetoothApiPairingDelegate
* delegate
= pairing_delegate_map_
[extension_id
];
221 adapter_
->RemovePairingDelegate(delegate
);
222 pairing_delegate_map_
.erase(extension_id
);
224 MaybeReleaseAdapter();
228 void BluetoothEventRouter::AdapterPresentChanged(
229 device::BluetoothAdapter
* adapter
,
231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
232 if (adapter
!= adapter_
.get()) {
233 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
236 DispatchAdapterStateEvent();
239 void BluetoothEventRouter::AdapterPoweredChanged(
240 device::BluetoothAdapter
* adapter
,
242 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
243 if (adapter
!= adapter_
.get()) {
244 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
247 DispatchAdapterStateEvent();
250 void BluetoothEventRouter::AdapterDiscoveringChanged(
251 device::BluetoothAdapter
* adapter
,
253 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
254 if (adapter
!= adapter_
.get()) {
255 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
260 // If any discovery sessions are inactive, clean them up.
261 DiscoverySessionMap active_session_map
;
262 for (DiscoverySessionMap::iterator iter
= discovery_session_map_
.begin();
263 iter
!= discovery_session_map_
.end();
265 device::BluetoothDiscoverySession
* session
= iter
->second
;
266 if (session
->IsActive()) {
267 active_session_map
[iter
->first
] = session
;
272 discovery_session_map_
.swap(active_session_map
);
273 MaybeReleaseAdapter();
276 DispatchAdapterStateEvent();
279 void BluetoothEventRouter::DeviceAdded(device::BluetoothAdapter
* adapter
,
280 device::BluetoothDevice
* device
) {
281 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
282 if (adapter
!= adapter_
.get()) {
283 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
287 DispatchDeviceEvent(bluetooth::OnDeviceAdded::kEventName
, device
);
290 void BluetoothEventRouter::DeviceChanged(device::BluetoothAdapter
* adapter
,
291 device::BluetoothDevice
* device
) {
292 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
293 if (adapter
!= adapter_
.get()) {
294 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
298 DispatchDeviceEvent(bluetooth::OnDeviceChanged::kEventName
, device
);
301 void BluetoothEventRouter::DeviceRemoved(device::BluetoothAdapter
* adapter
,
302 device::BluetoothDevice
* device
) {
303 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
304 if (adapter
!= adapter_
.get()) {
305 DVLOG(1) << "Ignoring event for adapter " << adapter
->GetAddress();
309 DispatchDeviceEvent(bluetooth::OnDeviceRemoved::kEventName
, device
);
312 void BluetoothEventRouter::OnListenerAdded() {
313 num_event_listeners_
++;
314 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
315 if (!adapter_
.get()) {
316 GetAdapter(base::Bind(&BluetoothEventRouter::OnAdapterInitialized
,
317 weak_ptr_factory_
.GetWeakPtr(),
318 base::Bind(&base::DoNothing
)));
322 void BluetoothEventRouter::OnListenerRemoved() {
323 if (num_event_listeners_
> 0)
324 num_event_listeners_
--;
325 MaybeReleaseAdapter();
328 void BluetoothEventRouter::DispatchAdapterStateEvent() {
329 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
330 api::bluetooth::AdapterState state
;
331 PopulateAdapterState(*adapter_
.get(), &state
);
333 scoped_ptr
<base::ListValue
> args
=
334 bluetooth::OnAdapterStateChanged::Create(state
);
335 scoped_ptr
<Event
> event(new Event(
336 bluetooth::OnAdapterStateChanged::kEventName
,
338 EventRouter::Get(browser_context_
)->BroadcastEvent(event
.Pass());
341 void BluetoothEventRouter::DispatchDeviceEvent(
342 const std::string
& event_name
,
343 device::BluetoothDevice
* device
) {
344 bluetooth::Device extension_device
;
345 bluetooth::BluetoothDeviceToApiDevice(*device
, &extension_device
);
347 scoped_ptr
<base::ListValue
> args
=
348 bluetooth::OnDeviceAdded::Create(extension_device
);
349 scoped_ptr
<Event
> event(new Event(event_name
, args
.Pass()));
350 EventRouter::Get(browser_context_
)->BroadcastEvent(event
.Pass());
353 void BluetoothEventRouter::CleanUpForExtension(
354 const std::string
& extension_id
) {
355 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
356 RemovePairingDelegate(extension_id
);
358 // Remove all profiles added by the extension.
359 BluetoothProfileMap::iterator profile_iter
= bluetooth_profile_map_
.begin();
360 while (profile_iter
!= bluetooth_profile_map_
.end()) {
361 ExtensionBluetoothProfileRecord record
= profile_iter
->second
;
362 if (record
.extension_id
== extension_id
) {
363 bluetooth_profile_map_
.erase(profile_iter
++);
364 record
.profile
->Unregister();
370 // Remove any discovery session initiated by the extension.
371 DiscoverySessionMap::iterator session_iter
=
372 discovery_session_map_
.find(extension_id
);
373 if (session_iter
== discovery_session_map_
.end())
375 delete session_iter
->second
;
376 discovery_session_map_
.erase(session_iter
);
379 void BluetoothEventRouter::CleanUpAllExtensions() {
380 for (BluetoothProfileMap::iterator it
= bluetooth_profile_map_
.begin();
381 it
!= bluetooth_profile_map_
.end();
383 it
->second
.profile
->Unregister();
385 bluetooth_profile_map_
.clear();
387 for (DiscoverySessionMap::iterator it
= discovery_session_map_
.begin();
388 it
!= discovery_session_map_
.end();
392 discovery_session_map_
.clear();
394 PairingDelegateMap::iterator pairing_iter
= pairing_delegate_map_
.begin();
395 while (pairing_iter
!= pairing_delegate_map_
.end())
396 RemovePairingDelegate(pairing_iter
++->first
);
399 void BluetoothEventRouter::OnStartDiscoverySession(
400 const std::string
& extension_id
,
401 const base::Closure
& callback
,
402 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
403 // Clean up any existing session instance for the extension.
404 DiscoverySessionMap::iterator iter
=
405 discovery_session_map_
.find(extension_id
);
406 if (iter
!= discovery_session_map_
.end())
408 discovery_session_map_
[extension_id
] = discovery_session
.release();
412 void BluetoothEventRouter::Observe(
414 const content::NotificationSource
& source
,
415 const content::NotificationDetails
& details
) {
416 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
418 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
: {
419 extensions::UnloadedExtensionInfo
* info
=
420 content::Details
<extensions::UnloadedExtensionInfo
>(details
).ptr();
421 CleanUpForExtension(info
->extension
->id());
424 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED
: {
425 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
426 CleanUpForExtension(host
->extension_id());
432 } // namespace extensions