|  | // Copyright 2012 The Chromium Authors | 
|  | // 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/containers/contains.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/functional/callback_helpers.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "build/build_config.h" | 
|  | #include "components/device_event_log/device_event_log.h" | 
|  | #include "content/public/browser/browser_thread.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/common/api/bluetooth.h" | 
|  | #include "extensions/common/api/bluetooth_private.h" | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr char kScanClientName[] = "Chrome Extension"; | 
|  |  | 
|  | void IgnoreAdapterResult(scoped_refptr<device::BluetoothAdapter> adapter) {} | 
|  |  | 
|  | void IgnoreAdapterResultAndThen( | 
|  | base::OnceClosure callback, | 
|  | scoped_refptr<device::BluetoothAdapter> adapter) { | 
|  | std::move(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) { | 
|  | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
|  | BLUETOOTH_LOG(USER) << "BluetoothEventRouter()"; | 
|  | DCHECK(browser_context_); | 
|  | extension_registry_observation_.Observe( | 
|  | ExtensionRegistry::Get(browser_context_)); | 
|  | extension_host_registry_observation_.Observe( | 
|  | ExtensionHostRegistry::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( | 
|  | device::BluetoothAdapterFactory::AdapterCallback callback) { | 
|  | if (adapter_.get()) { | 
|  | std::move(callback).Run(adapter_); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Note: On ChromeOS this will return an adapter that also supports Bluetooth | 
|  | // Low Energy. | 
|  | device::BluetoothAdapterFactory::Get()->GetClassicAdapter( | 
|  | base::BindOnce(&BluetoothEventRouter::OnAdapterInitialized, | 
|  | weak_ptr_factory_.GetWeakPtr(), std::move(callback))); | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::StartDiscoverySession( | 
|  | device::BluetoothAdapter* adapter, | 
|  | const std::string& extension_id, | 
|  | base::OnceClosure callback, | 
|  | base::OnceClosure error_callback) { | 
|  | if (!adapter_.get() && IsBluetoothSupported()) { | 
|  | // If |adapter_| isn't set yet, call GetAdapter() which will synchronously | 
|  | // invoke the callback (StartDiscoverySessionImpl). | 
|  | GetAdapter(base::BindOnce( | 
|  | &IgnoreAdapterResultAndThen, | 
|  | base::BindOnce(&BluetoothEventRouter::StartDiscoverySessionImpl, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | base::RetainedRef(adapter), extension_id, | 
|  | std::move(callback), std::move(error_callback)))); | 
|  | return; | 
|  | } | 
|  | StartDiscoverySessionImpl(adapter, extension_id, std::move(callback), | 
|  | std::move(error_callback)); | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::StartDiscoverySessionImpl( | 
|  | device::BluetoothAdapter* adapter, | 
|  | const std::string& extension_id, | 
|  | base::OnceClosure callback, | 
|  | base::OnceClosure error_callback) { | 
|  | if (!adapter_.get()) { | 
|  | BLUETOOTH_LOG(ERROR) << "Unable to get Bluetooth adapter."; | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  | if (adapter != adapter_.get()) { | 
|  | BLUETOOTH_LOG(ERROR) << "Bluetooth adapter mismatch."; | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  | auto 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; | 
|  | std::move(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 | 
|  | auto 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), | 
|  | kScanClientName, | 
|  | base::BindOnce(&BluetoothEventRouter::OnStartDiscoverySession, | 
|  | weak_ptr_factory_.GetWeakPtr(), extension_id, | 
|  | std::move(callback)), | 
|  | std::move(error_callback)); | 
|  | pre_set_filter_map_.erase(pre_set_iter); | 
|  | return; | 
|  | } | 
|  | adapter->StartDiscoverySession( | 
|  | kScanClientName, | 
|  | base::BindOnce(&BluetoothEventRouter::OnStartDiscoverySession, | 
|  | weak_ptr_factory_.GetWeakPtr(), extension_id, | 
|  | std::move(callback)), | 
|  | std::move(error_callback)); | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::StopDiscoverySession( | 
|  | device::BluetoothAdapter* adapter, | 
|  | const std::string& extension_id, | 
|  | base::OnceClosure callback, | 
|  | base::OnceClosure error_callback) { | 
|  | if (adapter != adapter_.get()) { | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  | auto 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."; | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  | BLUETOOTH_LOG(USER) << "StopDiscoverySession: " << extension_id; | 
|  | device::BluetoothDiscoverySession* session = iter->second; | 
|  | session->Stop(); | 
|  | std::move(callback).Run(); | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::SetDiscoveryFilter( | 
|  | std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter, | 
|  | device::BluetoothAdapter* adapter, | 
|  | const std::string& extension_id, | 
|  | base::OnceClosure callback, | 
|  | base::OnceClosure error_callback) { | 
|  | BLUETOOTH_LOG(USER) << "SetDiscoveryFilter"; | 
|  | if (!adapter_.get()) { | 
|  | BLUETOOTH_LOG(ERROR) << "Unable to get Bluetooth adapter."; | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  | if (adapter != adapter_.get()) { | 
|  | BLUETOOTH_LOG(ERROR) << "Bluetooth adapter mismatch."; | 
|  | std::move(error_callback).Run(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto 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(); | 
|  | std::move(callback).Run(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If the session has already started simply start a new one. The callback | 
|  | // will automatically delete the old session and put the new session (with its | 
|  | // new filter) in as this extension's session | 
|  | adapter->StartDiscoverySessionWithFilter( | 
|  | std::move(discovery_filter), kScanClientName, | 
|  | base::BindOnce(&BluetoothEventRouter::OnStartDiscoverySession, | 
|  | weak_ptr_factory_.GetWeakPtr(), extension_id, | 
|  | std::move(callback)), | 
|  | std::move(error_callback)); | 
|  | } | 
|  |  | 
|  | BluetoothApiPairingDelegate* BluetoothEventRouter::GetPairingDelegate( | 
|  | const std::string& extension_id) { | 
|  | return base::Contains(pairing_delegate_map_, extension_id) | 
|  | ? pairing_delegate_map_[extension_id] | 
|  | : nullptr; | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::OnAdapterInitialized( | 
|  | device::BluetoothAdapterFactory::AdapterCallback callback, | 
|  | scoped_refptr<device::BluetoothAdapter> adapter) { | 
|  | if (!adapter_.get()) { | 
|  | adapter_ = adapter; | 
|  | adapter_->AddObserver(this); | 
|  | } | 
|  |  | 
|  | std::move(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::BindOnce( | 
|  | &IgnoreAdapterResultAndThen, | 
|  | base::BindOnce(&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::Contains(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::Contains(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 (auto 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::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&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::DeviceAddressChanged( | 
|  | device::BluetoothAdapter* adapter, | 
|  | device::BluetoothDevice* device, | 
|  | const std::string& old_address) { | 
|  | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
|  | if (adapter != adapter_.get()) { | 
|  | BLUETOOTH_LOG(DEBUG) << "Ignoring event for adapter " | 
|  | << adapter->GetAddress(); | 
|  | return; | 
|  | } | 
|  | DCHECK(device); | 
|  |  | 
|  | bluetooth::Device extension_device; | 
|  | bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device); | 
|  |  | 
|  | auto args = | 
|  | bt_private::OnDeviceAddressChanged::Create(extension_device, old_address); | 
|  | auto event = std::make_unique<Event>( | 
|  | events::BLUETOOTH_PRIVATE_ON_DEVICE_ADDRESS_CHANGED, | 
|  | bt_private::OnDeviceAddressChanged::kEventName, std::move(args)); | 
|  | EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event)); | 
|  | } | 
|  |  | 
|  | 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::BindOnce(&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); | 
|  |  | 
|  | auto 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); | 
|  |  | 
|  | auto 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); | 
|  |  | 
|  | auto 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. | 
|  | auto 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(); | 
|  |  | 
|  | auto 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, | 
|  | base::OnceClosure callback, | 
|  | std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) { | 
|  | BLUETOOTH_LOG(EVENT) << "OnStartDiscoverySession: " << extension_id; | 
|  | // Clean up any existing session instance for the extension. | 
|  | auto iter = discovery_session_map_.find(extension_id); | 
|  | if (iter != discovery_session_map_.end()) | 
|  | delete iter->second; | 
|  | discovery_session_map_[extension_id] = discovery_session.release(); | 
|  | std::move(callback).Run(); | 
|  | } | 
|  |  | 
|  | void BluetoothEventRouter::OnExtensionHostDestroyed( | 
|  | content::BrowserContext* browser_context, | 
|  | ExtensionHost* host) { | 
|  | // The BluetoothEventRouter has its own context in incognito, so check | 
|  | // the context. | 
|  | if (browser_context != browser_context_) | 
|  | return; | 
|  |  | 
|  | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
|  | 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 |