blob: acac2e1f6e85a48197e3f3b9975cea340be337b4 [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 "chrome/browser/extensions/api/bluetooth/bluetooth_api.h"
#include <string>
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_api_utils.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/extensions/api/bluetooth.h"
#include "chrome/common/extensions/api/bluetooth/bluetooth_manifest_data.h"
#include "content/public/browser/browser_thread.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_profile.h"
#include "device/bluetooth/bluetooth_service_record.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "extensions/browser/event_router.h"
#include "extensions/common/permissions/permissions_data.h"
#include "net/base/io_buffer.h"
using content::BrowserContext;
using content::BrowserThread;
using device::BluetoothAdapter;
using device::BluetoothDevice;
using device::BluetoothProfile;
using device::BluetoothServiceRecord;
using device::BluetoothSocket;
using extensions::BluetoothApiSocket;
namespace AddProfile = extensions::api::bluetooth::AddProfile;
namespace bluetooth = extensions::api::bluetooth;
namespace Connect = extensions::api::bluetooth::Connect;
namespace Disconnect = extensions::api::bluetooth::Disconnect;
namespace GetDevice = extensions::api::bluetooth::GetDevice;
namespace GetDevices = extensions::api::bluetooth::GetDevices;
namespace RemoveProfile = extensions::api::bluetooth::RemoveProfile;
namespace Send = extensions::api::bluetooth::Send;
namespace {
const char kInvalidDevice[] = "Invalid device";
const char kInvalidUuid[] = "Invalid UUID";
const char kPermissionDenied[] = "Permission to add profile denied.";
const char kProfileAlreadyRegistered[] =
"This profile has already been registered";
const char kProfileNotFound[] = "Profile not found: invalid uuid";
const char kProfileRegistrationFailed[] = "Profile registration failed";
const char kStartDiscoveryFailed[] = "Starting discovery failed";
const char kStopDiscoveryFailed[] = "Failed to stop discovery";
extensions::BluetoothEventRouter* GetEventRouter(BrowserContext* context) {
// Note: |context| is valid on UI thread only.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return extensions::BluetoothAPI::Get(context)->event_router();
}
static void DispatchConnectionEventWorker(
void* browser_context_id,
const std::string& extension_id,
const device::BluetoothUUID& profile_uuid,
const device::BluetoothDevice* device,
scoped_refptr<device::BluetoothSocket> socket) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::BrowserContext* context =
reinterpret_cast<content::BrowserContext*>(browser_context_id);
if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
return;
extensions::BluetoothAPI* bluetooth_api =
extensions::BluetoothAPI::Get(context);
if (!bluetooth_api)
return;
bluetooth_api->DispatchConnectionEvent(
extension_id, profile_uuid, device, socket);
}
} // namespace
namespace extensions {
static base::LazyInstance<BrowserContextKeyedAPIFactory<BluetoothAPI> >
g_factory = LAZY_INSTANCE_INITIALIZER;
// static
BrowserContextKeyedAPIFactory<BluetoothAPI>*
BluetoothAPI::GetFactoryInstance() {
return g_factory.Pointer();
}
// static
BluetoothAPI* BluetoothAPI::Get(BrowserContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return GetFactoryInstance()->Get(context);
}
BluetoothAPI::ConnectionParams::ConnectionParams() {}
BluetoothAPI::ConnectionParams::~ConnectionParams() {}
BluetoothAPI::BluetoothAPI(content::BrowserContext* context)
: browser_context_(context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
EventRouter* event_router = EventRouter::Get(browser_context_);
event_router->RegisterObserver(this,
bluetooth::OnAdapterStateChanged::kEventName);
event_router->RegisterObserver(this, bluetooth::OnDeviceAdded::kEventName);
event_router->RegisterObserver(this, bluetooth::OnDeviceChanged::kEventName);
event_router->RegisterObserver(this, bluetooth::OnDeviceRemoved::kEventName);
}
BluetoothAPI::~BluetoothAPI() {}
BluetoothEventRouter* BluetoothAPI::event_router() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!event_router_) {
event_router_.reset(new BluetoothEventRouter(browser_context_));
}
return event_router_.get();
}
scoped_refptr<BluetoothAPI::SocketData> BluetoothAPI::socket_data() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!socket_data_) {
ApiResourceManager<BluetoothApiSocket>* socket_manager =
ApiResourceManager<BluetoothApiSocket>::Get(browser_context_);
DCHECK(socket_manager)
<< "There is no socket manager. "
"If this assertion is failing during a test, then it is likely that "
"TestExtensionSystem is failing to provide an instance of "
"ApiResourceManager<BluetoothApiSocket>.";
socket_data_ = socket_manager->data_;
}
return socket_data_;
}
void BluetoothAPI::Shutdown() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
void BluetoothAPI::OnListenerAdded(const EventListenerInfo& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (event_router()->IsBluetoothSupported())
event_router()->OnListenerAdded();
}
void BluetoothAPI::OnListenerRemoved(const EventListenerInfo& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (event_router()->IsBluetoothSupported())
event_router()->OnListenerRemoved();
}
void BluetoothAPI::DispatchConnectionEvent(
const std::string& extension_id,
const device::BluetoothUUID& uuid,
const device::BluetoothDevice* device,
scoped_refptr<device::BluetoothSocket> socket) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!event_router()->HasProfile(uuid))
return;
extensions::BluetoothAPI::ConnectionParams params;
params.browser_context_id = browser_context_;
params.thread_id = BluetoothApiSocket::kThreadId;
params.extension_id = extension_id;
params.uuid = uuid;
params.device_address = device->GetAddress();
params.socket = socket;
params.socket_data = socket_data();
BrowserThread::PostTask(
params.thread_id, FROM_HERE, base::Bind(&RegisterSocket, params));
}
// static
void BluetoothAPI::RegisterSocket(
const BluetoothAPI::ConnectionParams& params) {
DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
BluetoothApiSocket* api_socket = new BluetoothApiSocket(
params.extension_id, params.socket, params.device_address, params.uuid);
int socket_id = params.socket_data->Add(api_socket);
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(&RegisterSocketUI, params, socket_id));
}
// static
void BluetoothAPI::RegisterSocketUI(const ConnectionParams& params,
int socket_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::BrowserContext* context =
reinterpret_cast<content::BrowserContext*>(params.browser_context_id);
if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
return;
BluetoothAPI::Get(context)->event_router()->GetAdapter(
base::Bind(&RegisterSocketWithAdapterUI, params, socket_id));
}
void BluetoothAPI::RegisterSocketWithAdapterUI(
const ConnectionParams& params,
int socket_id,
scoped_refptr<device::BluetoothAdapter> adapter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::BrowserContext* context =
reinterpret_cast<content::BrowserContext*>(params.browser_context_id);
if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
return;
BluetoothDevice* device = adapter->GetDevice(params.device_address);
if (!device)
return;
api::bluetooth::Socket result_socket;
bluetooth::BluetoothDeviceToApiDevice(*device, &result_socket.device);
result_socket.uuid = params.uuid.canonical_value();
result_socket.id = socket_id;
scoped_ptr<base::ListValue> args =
bluetooth::OnConnection::Create(result_socket);
scoped_ptr<Event> event(
new Event(bluetooth::OnConnection::kEventName, args.Pass()));
EventRouter* router = EventRouter::Get(context);
if (router)
router->DispatchEventToExtension(params.extension_id, event.Pass());
}
namespace api {
BluetoothGetAdapterStateFunction::~BluetoothGetAdapterStateFunction() {}
bool BluetoothGetAdapterStateFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) {
bluetooth::AdapterState state;
PopulateAdapterState(*adapter.get(), &state);
results_ = bluetooth::GetAdapterState::Results::Create(state);
SendResponse(true);
return true;
}
BluetoothGetDevicesFunction::~BluetoothGetDevicesFunction() {}
bool BluetoothGetDevicesFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::ListValue* device_list = new base::ListValue;
SetResult(device_list);
BluetoothAdapter::DeviceList devices = adapter->GetDevices();
for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
iter != devices.end();
++iter) {
const BluetoothDevice* device = *iter;
DCHECK(device);
bluetooth::Device extension_device;
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
device_list->Append(extension_device.ToValue().release());
}
SendResponse(true);
return true;
}
BluetoothGetDeviceFunction::~BluetoothGetDeviceFunction() {}
bool BluetoothGetDeviceFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
scoped_ptr<GetDevice::Params> params(GetDevice::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
BluetoothDevice* device = adapter->GetDevice(params->device_address);
if (device) {
bluetooth::Device extension_device;
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
SetResult(extension_device.ToValue().release());
SendResponse(true);
} else {
SetError(kInvalidDevice);
SendResponse(false);
}
return false;
}
BluetoothAddProfileFunction::BluetoothAddProfileFunction() {}
BluetoothAddProfileFunction::~BluetoothAddProfileFunction() {}
bool BluetoothAddProfileFunction::RunAsync() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
scoped_ptr<AddProfile::Params> params(AddProfile::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
device::BluetoothUUID uuid(params->profile.uuid);
if (!uuid.IsValid()) {
SetError(kInvalidUuid);
return false;
}
BluetoothPermissionRequest param(params->profile.uuid);
if (!BluetoothManifestData::CheckRequest(GetExtension(), param)) {
error_ = kPermissionDenied;
return false;
}
uuid_ = uuid;
if (GetEventRouter(browser_context())->HasProfile(uuid_)) {
SetError(kProfileAlreadyRegistered);
return false;
}
BluetoothProfile::Options options;
if (params->profile.name.get())
options.name = *params->profile.name.get();
if (params->profile.channel.get())
options.channel = *params->profile.channel.get();
if (params->profile.psm.get())
options.psm = *params->profile.psm.get();
if (params->profile.require_authentication.get()) {
options.require_authentication =
*params->profile.require_authentication.get();
}
if (params->profile.require_authorization.get()) {
options.require_authorization =
*params->profile.require_authorization.get();
}
if (params->profile.auto_connect.get())
options.auto_connect = *params->profile.auto_connect.get();
if (params->profile.version.get())
options.version = *params->profile.version.get();
if (params->profile.features.get())
options.features = *params->profile.features.get();
RegisterProfile(
options,
base::Bind(&BluetoothAddProfileFunction::OnProfileRegistered, this));
return true;
}
void BluetoothAddProfileFunction::RegisterProfile(
const BluetoothProfile::Options& options,
const BluetoothProfile::ProfileCallback& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
BluetoothProfile::Register(uuid_, options, callback);
}
void BluetoothAddProfileFunction::OnProfileRegistered(
BluetoothProfile* bluetooth_profile) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!bluetooth_profile) {
SetError(kProfileRegistrationFailed);
SendResponse(false);
return;
}
if (GetEventRouter(browser_context())->HasProfile(uuid_)) {
bluetooth_profile->Unregister();
SetError(kProfileAlreadyRegistered);
SendResponse(false);
return;
}
bluetooth_profile->SetConnectionCallback(
base::Bind(&DispatchConnectionEventWorker,
browser_context(),
extension_id(),
uuid_));
GetEventRouter(browser_context())
->AddProfile(uuid_, extension_id(), bluetooth_profile);
SendResponse(true);
}
BluetoothRemoveProfileFunction::~BluetoothRemoveProfileFunction() {}
bool BluetoothRemoveProfileFunction::RunSync() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
scoped_ptr<RemoveProfile::Params> params(
RemoveProfile::Params::Create(*args_));
device::BluetoothUUID uuid(params->profile.uuid);
if (!uuid.IsValid()) {
SetError(kInvalidUuid);
return false;
}
if (!GetEventRouter(browser_context())->HasProfile(uuid)) {
SetError(kProfileNotFound);
return false;
}
GetEventRouter(browser_context())->RemoveProfile(uuid);
return true;
}
BluetoothConnectFunction::~BluetoothConnectFunction() {}
bool BluetoothConnectFunction::DoWork(scoped_refptr<BluetoothAdapter> adapter) {
scoped_ptr<Connect::Params> params(Connect::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
const bluetooth::ConnectOptions& options = params->options;
device::BluetoothUUID uuid(options.profile.uuid);
if (!uuid.IsValid()) {
SetError(kInvalidUuid);
SendResponse(false);
return false;
}
BluetoothDevice* device = adapter->GetDevice(options.device.address);
if (!device) {
SetError(kInvalidDevice);
SendResponse(false);
return false;
}
BluetoothProfile* bluetooth_profile =
GetEventRouter(browser_context())->GetProfile(uuid);
if (!bluetooth_profile) {
SetError(kProfileNotFound);
SendResponse(false);
return false;
}
device->ConnectToProfile(
bluetooth_profile,
base::Bind(&BluetoothConnectFunction::OnConnectedCallback,
this,
adapter,
device->GetAddress()),
base::Bind(&BluetoothConnectFunction::OnErrorCallback, this));
return true;
}
void BluetoothConnectFunction::OnConnectedCallback(
scoped_refptr<device::BluetoothAdapter> adapter,
const std::string& device_address) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// TODO(tengs): Remove this once we have an API for starting the connection
// monitor.
BluetoothDevice* device = adapter->GetDevice(device_address);
if (!device) {
SetError(kInvalidDevice);
SendResponse(false);
return;
}
// Start the connection monitor, and return success even if this fails,
// as the connection was still opened successfully.
device->StartConnectionMonitor(
base::Bind(&BluetoothConnectFunction::OnMonitorStartedCallback, this),
base::Bind(&BluetoothConnectFunction::OnMonitorStartedCallback, this));
}
void BluetoothConnectFunction::OnMonitorStartedCallback() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
SendResponse(true);
}
void BluetoothConnectFunction::OnErrorCallback(const std::string& error) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
SetError(error);
SendResponse(false);
}
bool BluetoothDisconnectFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.disconnect() instead.");
return false;
}
bool BluetoothSendFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.send() instead.");
return false;
}
bool BluetoothUpdateSocketFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.update() instead.");
return false;
}
bool BluetoothSetSocketPausedFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.setPaused() instead.");
return false;
}
bool BluetoothGetSocketFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.getInfo() instead.");
return false;
}
bool BluetoothGetSocketsFunction::RunAsync() {
// TODO(keybuk): Remove.
SetError("Removed. Use chrome.bluetoothSocket.getSockets() instead.");
return false;
}
void BluetoothStartDiscoveryFunction::OnSuccessCallback() {
SendResponse(true);
}
void BluetoothStartDiscoveryFunction::OnErrorCallback() {
SetError(kStartDiscoveryFailed);
SendResponse(false);
}
bool BluetoothStartDiscoveryFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) {
GetEventRouter(browser_context())->StartDiscoverySession(
adapter,
extension_id(),
base::Bind(&BluetoothStartDiscoveryFunction::OnSuccessCallback, this),
base::Bind(&BluetoothStartDiscoveryFunction::OnErrorCallback, this));
return true;
}
void BluetoothStopDiscoveryFunction::OnSuccessCallback() {
SendResponse(true);
}
void BluetoothStopDiscoveryFunction::OnErrorCallback() {
SetError(kStopDiscoveryFailed);
SendResponse(false);
}
bool BluetoothStopDiscoveryFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) {
GetEventRouter(browser_context())->StopDiscoverySession(
adapter,
extension_id(),
base::Bind(&BluetoothStopDiscoveryFunction::OnSuccessCallback, this),
base::Bind(&BluetoothStopDiscoveryFunction::OnErrorCallback, this));
return true;
}
} // namespace api
} // namespace extensions