blob: 6c2048f18fb572bc8fe2c0d10539c7a8abe25b33 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/api/bluetooth/bluetooth_event_router.h"
#include <map>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/device_event_log/device_event_log.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "extensions/browser/api/bluetooth/bluetooth_api_pairing_delegate.h"
#include "extensions/browser/api/bluetooth/bluetooth_api_utils.h"
#include "extensions/browser/api/bluetooth/bluetooth_private_api.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/notification_types.h"
#include "extensions/common/api/bluetooth.h"
#include "extensions/common/api/bluetooth_private.h"
namespace extensions {
namespace {
void IgnoreAdapterResult(scoped_refptr<device::BluetoothAdapter> adapter) {}
void IgnoreAdapterResultAndThen(
const base::Closure& callback,
scoped_refptr<device::BluetoothAdapter> adapter) {
callback.Run();
}
std::string GetListenerId(const extensions::EventListenerInfo& details) {
return !details.extension_id.empty() ? details.extension_id
: details.listener_url.host();
}
} // namespace
namespace bluetooth = api::bluetooth;
namespace bt_private = api::bluetooth_private;
BluetoothEventRouter::BluetoothEventRouter(content::BrowserContext* context)
: browser_context_(context),
adapter_(nullptr),
extension_registry_observer_(this),
weak_ptr_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
BLUETOOTH_LOG(USER) << "BluetoothEventRouter()";
DCHECK(browser_context_);
registrar_.Add(this, extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
content::Source<content::BrowserContext>(browser_context_));
extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
}
BluetoothEventRouter::~BluetoothEventRouter() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
BLUETOOTH_LOG(USER) << "~BluetoothEventRouter()";
if (adapter_.get()) {
adapter_->RemoveObserver(this);
adapter_ = nullptr;
}
CleanUpAllExtensions();
}
bool BluetoothEventRouter::IsBluetoothSupported() const {
return device::BluetoothAdapterFactory::IsBluetoothSupported();
}
void BluetoothEventRouter::GetAdapter(
const device::BluetoothAdapterFactory::AdapterCallback& callback) {
if (adapter_.get()) {
callback.Run(scoped_refptr<device::BluetoothAdapter>(adapter_));
return;
}
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothEventRouter::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr(), callback));
}
void BluetoothEventRouter::StartDiscoverySession(
device::BluetoothAdapter* adapter,
const std::string& extension_id,
const base::Closure& callback,
const base::Closure& error_callback) {
if (!adapter_.get() && IsBluetoothSupported()) {
// If |adapter_| isn't set yet, call GetAdapter() which will synchronously
// invoke the callback (StartDiscoverySessionImpl).
GetAdapter(base::Bind(
&IgnoreAdapterResultAndThen,
base::Bind(&BluetoothEventRouter::StartDiscoverySessionImpl,
weak_ptr_factory_.GetWeakPtr(), base::RetainedRef(adapter),
extension_id, callback, error_callback)));
return;
}
StartDiscoverySessionImpl(adapter, extension_id, callback, error_callback);
}
void BluetoothEventRouter::StartDiscoverySessionImpl(
device::BluetoothAdapter* adapter,
const std::string& extension_id,
const base::Closure& callback,
const base::Closure& error_callback) {
if (!adapter_.get()) {
BLUETOOTH_LOG(ERROR) << "Unable to get Bluetooth adapter.";
error_callback.Run();
return;
}
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(ERROR) << "Bluetooth adapter mismatch.";
error_callback.Run();
return;
}
DiscoverySessionMap::iterator iter =
discovery_session_map_.find(extension_id);
if (iter != discovery_session_map_.end() && iter->second->IsActive()) {
BLUETOOTH_LOG(DEBUG) << "An active discovery session exists for extension: "
<< extension_id;
error_callback.Run();
return;
}
BLUETOOTH_LOG(USER) << "StartDiscoverySession: " << extension_id;
// Check whether user pre set discovery filter by calling SetDiscoveryFilter
// before. If the user has set a discovery filter then start a filtered
// discovery session, otherwise start a regular session
PreSetFilterMap::iterator pre_set_iter =
pre_set_filter_map_.find(extension_id);
if (pre_set_iter != pre_set_filter_map_.end()) {
adapter->StartDiscoverySessionWithFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter>(pre_set_iter->second),
base::Bind(&BluetoothEventRouter::OnStartDiscoverySession,
weak_ptr_factory_.GetWeakPtr(), extension_id, callback),
error_callback);
pre_set_filter_map_.erase(pre_set_iter);
return;
}
adapter->StartDiscoverySession(
base::Bind(&BluetoothEventRouter::OnStartDiscoverySession,
weak_ptr_factory_.GetWeakPtr(), extension_id, callback),
error_callback);
}
void BluetoothEventRouter::StopDiscoverySession(
device::BluetoothAdapter* adapter,
const std::string& extension_id,
const base::Closure& callback,
const base::Closure& error_callback) {
if (adapter != adapter_.get()) {
error_callback.Run();
return;
}
DiscoverySessionMap::iterator iter =
discovery_session_map_.find(extension_id);
if (iter == discovery_session_map_.end() || !iter->second->IsActive()) {
BLUETOOTH_LOG(DEBUG) << "No active discovery session exists for extension.";
error_callback.Run();
return;
}
BLUETOOTH_LOG(USER) << "StopDiscoverySession: " << extension_id;
device::BluetoothDiscoverySession* session = iter->second;
session->Stop(callback, error_callback);
}
void BluetoothEventRouter::SetDiscoveryFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
device::BluetoothAdapter* adapter,
const std::string& extension_id,
const base::Closure& callback,
const base::Closure& error_callback) {
BLUETOOTH_LOG(USER) << "SetDiscoveryFilter";
if (adapter != adapter_.get()) {
error_callback.Run();
return;
}
DiscoverySessionMap::iterator iter =
discovery_session_map_.find(extension_id);
if (iter == discovery_session_map_.end() || !iter->second->IsActive()) {
BLUETOOTH_LOG(DEBUG) << "No active discovery session exists for extension, "
<< "so caching filter for later use.";
pre_set_filter_map_[extension_id] = discovery_filter.release();
callback.Run();
return;
}
// extension is already running discovery, update it's discovery filter
iter->second->SetDiscoveryFilter(std::move(discovery_filter), callback,
error_callback);
}
BluetoothApiPairingDelegate* BluetoothEventRouter::GetPairingDelegate(
const std::string& extension_id) {
return base::ContainsKey(pairing_delegate_map_, extension_id)
? pairing_delegate_map_[extension_id]
: nullptr;
}
void BluetoothEventRouter::OnAdapterInitialized(
const device::BluetoothAdapterFactory::AdapterCallback& callback,
scoped_refptr<device::BluetoothAdapter> adapter) {
if (!adapter_.get()) {
adapter_ = adapter;
adapter_->AddObserver(this);
}
callback.Run(adapter);
}
void BluetoothEventRouter::MaybeReleaseAdapter() {
if (adapter_.get() && event_listener_count_.empty() &&
pairing_delegate_map_.empty()) {
BLUETOOTH_LOG(EVENT) << "Releasing Adapter.";
adapter_->RemoveObserver(this);
adapter_ = nullptr;
}
}
void BluetoothEventRouter::AddPairingDelegate(const std::string& extension_id) {
if (!adapter_.get() && IsBluetoothSupported()) {
GetAdapter(
base::Bind(&IgnoreAdapterResultAndThen,
base::Bind(&BluetoothEventRouter::AddPairingDelegateImpl,
weak_ptr_factory_.GetWeakPtr(), extension_id)));
return;
}
AddPairingDelegateImpl(extension_id);
}
void BluetoothEventRouter::AddPairingDelegateImpl(
const std::string& extension_id) {
if (!adapter_.get()) {
LOG(ERROR) << "Unable to get adapter for extension_id: " << extension_id;
return;
}
if (base::ContainsKey(pairing_delegate_map_, extension_id)) {
// For WebUI there may be more than one page open to the same url
// (e.g. chrome://settings). These will share the same pairing delegate.
BLUETOOTH_LOG(EVENT) << "Pairing delegate already exists for extension_id: "
<< extension_id;
return;
}
BluetoothApiPairingDelegate* delegate =
new BluetoothApiPairingDelegate(browser_context_);
DCHECK(adapter_.get());
adapter_->AddPairingDelegate(
delegate, device::BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
pairing_delegate_map_[extension_id] = delegate;
}
void BluetoothEventRouter::RemovePairingDelegate(
const std::string& extension_id) {
if (base::ContainsKey(pairing_delegate_map_, extension_id)) {
BluetoothApiPairingDelegate* delegate = pairing_delegate_map_[extension_id];
if (adapter_.get())
adapter_->RemovePairingDelegate(delegate);
pairing_delegate_map_.erase(extension_id);
delete delegate;
MaybeReleaseAdapter();
}
}
void BluetoothEventRouter::AdapterPresentChanged(
device::BluetoothAdapter* adapter,
bool present) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
DispatchAdapterStateEvent();
}
void BluetoothEventRouter::AdapterPoweredChanged(
device::BluetoothAdapter* adapter,
bool has_power) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
DispatchAdapterStateEvent();
}
void BluetoothEventRouter::AdapterDiscoveringChanged(
device::BluetoothAdapter* adapter,
bool discovering) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
if (!discovering) {
// If any discovery sessions are inactive, clean them up.
DiscoverySessionMap active_session_map;
for (DiscoverySessionMap::iterator iter = discovery_session_map_.begin();
iter != discovery_session_map_.end(); ++iter) {
device::BluetoothDiscoverySession* session = iter->second;
if (session->IsActive()) {
active_session_map[iter->first] = session;
continue;
}
delete session;
}
discovery_session_map_.swap(active_session_map);
}
DispatchAdapterStateEvent();
// Release the adapter after dispatching the event.
if (!discovering) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BluetoothEventRouter::MaybeReleaseAdapter,
weak_ptr_factory_.GetWeakPtr()));
}
}
void BluetoothEventRouter::DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
DispatchDeviceEvent(events::BLUETOOTH_ON_DEVICE_ADDED,
bluetooth::OnDeviceAdded::kEventName, device);
}
void BluetoothEventRouter::DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
DispatchDeviceEvent(events::BLUETOOTH_ON_DEVICE_CHANGED,
bluetooth::OnDeviceChanged::kEventName, device);
}
void BluetoothEventRouter::DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (adapter != adapter_.get()) {
BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter "
<< adapter->GetAddress();
return;
}
DispatchDeviceEvent(events::BLUETOOTH_ON_DEVICE_REMOVED,
bluetooth::OnDeviceRemoved::kEventName, device);
}
void BluetoothEventRouter::OnListenerAdded(const EventListenerInfo& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::string id = GetListenerId(details);
int count = ++event_listener_count_[id];
BLUETOOTH_LOG(EVENT) << "Event Listener Added: " << id << " Count: " << count;
if (!adapter_.get())
GetAdapter(base::Bind(&IgnoreAdapterResult));
}
void BluetoothEventRouter::OnListenerRemoved(const EventListenerInfo& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::string id = GetListenerId(details);
auto iter = event_listener_count_.find(id);
CHECK(iter != event_listener_count_.end());
int count = --(iter->second);
BLUETOOTH_LOG(EVENT) << "Event Listener Removed: " << id
<< " Count: " << count;
if (count == 0) {
event_listener_count_.erase(iter);
// When all listeners for a listener id have been removed, remove any
// pairing delegate or discovery session and filters.
CleanUpForExtension(id);
}
MaybeReleaseAdapter();
}
void BluetoothEventRouter::DispatchAdapterStateEvent() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
api::bluetooth::AdapterState state;
CHECK(adapter_.get());
PopulateAdapterState(*adapter_, &state);
std::unique_ptr<base::ListValue> args =
bluetooth::OnAdapterStateChanged::Create(state);
std::unique_ptr<Event> event(
new Event(events::BLUETOOTH_ON_ADAPTER_STATE_CHANGED,
bluetooth::OnAdapterStateChanged::kEventName, std::move(args)));
EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event));
}
void BluetoothEventRouter::DispatchDeviceEvent(
events::HistogramValue histogram_value,
const std::string& event_name,
device::BluetoothDevice* device) {
bluetooth::Device extension_device;
CHECK(device);
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
std::unique_ptr<base::ListValue> args =
bluetooth::OnDeviceAdded::Create(extension_device);
std::unique_ptr<Event> event(
new Event(histogram_value, event_name, std::move(args)));
EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event));
}
void BluetoothEventRouter::CleanUpForExtension(
const std::string& extension_id) {
BLUETOOTH_LOG(DEBUG) << "CleanUpForExtension: " << extension_id;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
RemovePairingDelegate(extension_id);
PreSetFilterMap::iterator pre_set_iter =
pre_set_filter_map_.find(extension_id);
if (pre_set_iter != pre_set_filter_map_.end()) {
delete pre_set_iter->second;
pre_set_filter_map_.erase(pre_set_iter);
}
// Remove any discovery session initiated by the extension.
DiscoverySessionMap::iterator session_iter =
discovery_session_map_.find(extension_id);
if (session_iter == discovery_session_map_.end())
return;
// discovery_session_map.erase() should happen before
// delete session_iter->second, because deleting the
// BluetoothDiscoverySession object may trigger a chain reaction
// (see http://crbug.com/711484#c9) which will modify
// discovery_session_map_ itself.
device::BluetoothDiscoverySession* discovery_session = session_iter->second;
discovery_session_map_.erase(session_iter);
delete discovery_session;
}
void BluetoothEventRouter::CleanUpAllExtensions() {
BLUETOOTH_LOG(DEBUG) << "CleanUpAllExtensions";
for (auto& it : pre_set_filter_map_)
delete it.second;
pre_set_filter_map_.clear();
for (auto& it : discovery_session_map_) {
BLUETOOTH_LOG(DEBUG) << "Clean up Discovery Session: " << it.first;
delete it.second;
}
discovery_session_map_.clear();
PairingDelegateMap::iterator pairing_iter = pairing_delegate_map_.begin();
while (pairing_iter != pairing_delegate_map_.end())
RemovePairingDelegate(pairing_iter++->first);
}
void BluetoothEventRouter::OnStartDiscoverySession(
const std::string& extension_id,
const base::Closure& callback,
std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
BLUETOOTH_LOG(EVENT) << "OnStartDiscoverySession: " << extension_id;
// Clean up any existing session instance for the extension.
DiscoverySessionMap::iterator iter =
discovery_session_map_.find(extension_id);
if (iter != discovery_session_map_.end())
delete iter->second;
discovery_session_map_[extension_id] = discovery_session.release();
callback.Run();
}
void BluetoothEventRouter::OnSetDiscoveryFilter(const std::string& extension_id,
const base::Closure& callback) {
BLUETOOTH_LOG(DEBUG) << "Successfully set DiscoveryFilter.";
callback.Run();
}
void BluetoothEventRouter::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, type);
ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
BLUETOOTH_LOG(DEBUG) << "Host Destroyed: " << host->extension_id();
CleanUpForExtension(host->extension_id());
}
void BluetoothEventRouter::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) {
CleanUpForExtension(extension->id());
}
} // namespace extensions