// Copyright 2014 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 "chrome/browser/extensions/api/bluetooth/bluetooth_private_api.h" #include "base/callback.h" #include "base/lazy_instance.h" #include "base/strings/string_util.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_api.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h" #include "chrome/common/extensions/api/bluetooth_private.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" namespace bt_private = extensions::api::bluetooth_private; namespace extensions { static base::LazyInstance<BrowserContextKeyedAPIFactory<BluetoothPrivateAPI> > g_factory = LAZY_INSTANCE_INITIALIZER; // static BrowserContextKeyedAPIFactory<BluetoothPrivateAPI>* BluetoothPrivateAPI::GetFactoryInstance() { return g_factory.Pointer(); } BluetoothPrivateAPI::BluetoothPrivateAPI(content::BrowserContext* context) : browser_context_(context) { EventRouter::Get(browser_context_) ->RegisterObserver(this, bt_private::OnPairing::kEventName); } BluetoothPrivateAPI::~BluetoothPrivateAPI() {} void BluetoothPrivateAPI::Shutdown() { EventRouter::Get(browser_context_)->UnregisterObserver(this); } void BluetoothPrivateAPI::OnListenerAdded(const EventListenerInfo& details) { // This function can be called multiple times for the same JS listener, for // example, once for the addListener call and again if it is a lazy listener. if (!details.browser_context) return; BluetoothAPI::Get(browser_context_)->event_router()->AddPairingDelegate( details.extension_id); } void BluetoothPrivateAPI::OnListenerRemoved(const EventListenerInfo& details) { // This function can be called multiple times for the same JS listener, for // example, once for the addListener call and again if it is a lazy listener. if (!details.browser_context) return; BluetoothAPI::Get(browser_context_)->event_router()->RemovePairingDelegate( details.extension_id); } namespace api { namespace { const char kNameProperty[] = "name"; const char kPoweredProperty[] = "powered"; const char kDiscoverableProperty[] = "discoverable"; const char kSetAdapterPropertyError[] = "Error setting adapter properties: $1"; const char kDeviceNotFoundError[] = "Given address is not a valid Bluetooth device."; const char kPairingNotEnabled[] = "Pairing must be enabled to set a pairing response."; const char kInvalidPairingResponseOptions[] = "Invalid pairing response options"; const char kAdapterNotPresent[] = "Could not find a Bluetooth adapter."; // Returns true if the pairing response options passed into the // setPairingResponse function are valid. bool ValidatePairingResponseOptions( const device::BluetoothDevice* device, const bt_private::SetPairingResponseOptions& options) { bool response = options.response != bt_private::PAIRING_RESPONSE_NONE; bool pincode = options.pincode.get() != NULL; bool passkey = options.passkey.get() != NULL; if (!response && !pincode && !passkey) return false; if (pincode && passkey) return false; if (options.response != bt_private::PAIRING_RESPONSE_CONFIRM && (pincode || passkey)) return false; // Check the BluetoothDevice is in expecting the correct response. if (!device->ExpectingConfirmation() && !device->ExpectingPinCode() && !device->ExpectingPasskey()) return false; if (pincode && !device->ExpectingPinCode()) return false; if (passkey && !device->ExpectingPasskey()) return false; if (options.response == bt_private::PAIRING_RESPONSE_CONFIRM && !pincode && !passkey && !device->ExpectingConfirmation()) return false; return true; } } // namespace BluetoothPrivateSetAdapterStateFunction:: BluetoothPrivateSetAdapterStateFunction() {} BluetoothPrivateSetAdapterStateFunction:: ~BluetoothPrivateSetAdapterStateFunction() {} bool BluetoothPrivateSetAdapterStateFunction::DoWork( scoped_refptr<device::BluetoothAdapter> adapter) { scoped_ptr<bt_private::SetAdapterState::Params> params( bt_private::SetAdapterState::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); if (!adapter->IsPresent()) { SetError(kAdapterNotPresent); SendResponse(false); return true; } const bt_private::NewAdapterState& new_state = params->adapter_state; // These properties are not owned. std::string* name = new_state.name.get(); bool* powered = new_state.powered.get(); bool* discoverable = new_state.discoverable.get(); if (name && adapter->GetName() != *name) { pending_properties_.insert(kNameProperty); adapter->SetName(*name, CreatePropertySetCallback(kNameProperty), CreatePropertyErrorCallback(kNameProperty)); } if (powered && adapter->IsPowered() != *powered) { pending_properties_.insert(kPoweredProperty); adapter->SetPowered(*powered, CreatePropertySetCallback(kPoweredProperty), CreatePropertyErrorCallback(kPoweredProperty)); } if (discoverable && adapter->IsDiscoverable() != *discoverable) { pending_properties_.insert(kDiscoverableProperty); adapter->SetDiscoverable( *discoverable, CreatePropertySetCallback(kDiscoverableProperty), CreatePropertyErrorCallback(kDiscoverableProperty)); } if (pending_properties_.empty()) SendResponse(true); return true; } base::Closure BluetoothPrivateSetAdapterStateFunction::CreatePropertySetCallback( const std::string& property_name) { return base::Bind( &BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertySet, this, property_name); } base::Closure BluetoothPrivateSetAdapterStateFunction::CreatePropertyErrorCallback( const std::string& property_name) { return base::Bind( &BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertyError, this, property_name); } void BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertySet( const std::string& property) { DCHECK(pending_properties_.find(property) != pending_properties_.end()); DCHECK(failed_properties_.find(property) == failed_properties_.end()); pending_properties_.erase(property); if (pending_properties_.empty()) { if (failed_properties_.empty()) SendResponse(true); else SendError(); } } void BluetoothPrivateSetAdapterStateFunction::OnAdapterPropertyError( const std::string& property) { DCHECK(pending_properties_.find(property) != pending_properties_.end()); DCHECK(failed_properties_.find(property) == failed_properties_.end()); pending_properties_.erase(property); failed_properties_.insert(property); if (pending_properties_.empty()) SendError(); } void BluetoothPrivateSetAdapterStateFunction::SendError() { DCHECK(pending_properties_.empty()); DCHECK(!failed_properties_.empty()); std::vector<std::string> failed_vector; std::copy(failed_properties_.begin(), failed_properties_.end(), std::back_inserter(failed_vector)); std::vector<std::string> replacements(1); replacements[0] = JoinString(failed_vector, ", "); std::string error = ReplaceStringPlaceholders(kSetAdapterPropertyError, replacements, NULL); SetError(error); SendResponse(false); } BluetoothPrivateSetPairingResponseFunction:: BluetoothPrivateSetPairingResponseFunction() {} BluetoothPrivateSetPairingResponseFunction:: ~BluetoothPrivateSetPairingResponseFunction() {} bool BluetoothPrivateSetPairingResponseFunction::DoWork( scoped_refptr<device::BluetoothAdapter> adapter) { scoped_ptr<bt_private::SetPairingResponse::Params> params( bt_private::SetPairingResponse::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); const bt_private::SetPairingResponseOptions& options = params->options; BluetoothEventRouter* router = BluetoothAPI::Get(browser_context())->event_router(); if (!router->GetPairingDelegate(extension_id())) { SetError(kPairingNotEnabled); SendResponse(false); return true; } const std::string& device_address = options.device.address; device::BluetoothDevice* device = adapter->GetDevice(device_address); if (!device) { SetError(kDeviceNotFoundError); SendResponse(false); return true; } if (!ValidatePairingResponseOptions(device, options)) { SetError(kInvalidPairingResponseOptions); SendResponse(false); return true; } if (options.pincode.get()) { device->SetPinCode(*options.pincode.get()); } else if (options.passkey.get()) { device->SetPasskey(*options.passkey.get()); } else { switch (options.response) { case bt_private::PAIRING_RESPONSE_CONFIRM: device->ConfirmPairing(); break; case bt_private::PAIRING_RESPONSE_REJECT: device->RejectPairing(); break; case bt_private::PAIRING_RESPONSE_CANCEL: device->CancelPairing(); break; default: NOTREACHED(); } } SendResponse(true); return true; } } // namespace api } // namespace extensions