Commit 6bdba32b authored by keybuk@chromium.org's avatar keybuk@chromium.org

Bluetooth: add Device events, and cleanup JS API

The JavaScript API for getting the set of devices known to the adapter
and dealing with discovery was difficult to use, and had a messy
implementation. By streamlining the API not only does it become much
easier to work with, but the implementation becomes much cleaner as
well.

chrome.bluetooth.getDevices() now simply returns an array of devices in
its callback, it's up to the application to filter these since profile
filtering is unreliable during discovery and on Low Energy.

chrome.bluetooth.startDiscovery() now only has a callback to indicate
a success to the command, the hidden event and listener has been
removed in favor of the new device events.

Add chrome.bluetooth.onDeviceAdded, chrome.bluetooth.onDeviceChanged
and chrome.bluetooth.onDeviceRemoved events. These return updated
device objects when they are first known, changed and removed
respectively.

Take the opportunity of writing a proper documentation page for these
new methods and events showing their usage.

BUG=345050
TEST=BluetoothApiTest updated and included
R=armansito@chromium.org, miket@chromium.org, rpaquay@chromium.org, sky@chromium.org

Review URL: https://codereview.chromium.org/177113013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255706 0039d316-1c4b-4281-b951-d872f2087c98
parent 311544d1
...@@ -96,6 +96,12 @@ BluetoothAPI::BluetoothAPI(BrowserContext* context) ...@@ -96,6 +96,12 @@ BluetoothAPI::BluetoothAPI(BrowserContext* context)
: browser_context_(context) { : browser_context_(context) {
ExtensionSystem::Get(browser_context_)->event_router()->RegisterObserver( ExtensionSystem::Get(browser_context_)->event_router()->RegisterObserver(
this, bluetooth::OnAdapterStateChanged::kEventName); this, bluetooth::OnAdapterStateChanged::kEventName);
ExtensionSystem::Get(browser_context_)->event_router()->RegisterObserver(
this, bluetooth::OnDeviceAdded::kEventName);
ExtensionSystem::Get(browser_context_)->event_router()->RegisterObserver(
this, bluetooth::OnDeviceChanged::kEventName);
ExtensionSystem::Get(browser_context_)->event_router()->RegisterObserver(
this, bluetooth::OnDeviceRemoved::kEventName);
} }
BluetoothAPI::~BluetoothAPI() { BluetoothAPI::~BluetoothAPI() {
...@@ -272,52 +278,12 @@ bool BluetoothGetAdapterStateFunction::DoWork( ...@@ -272,52 +278,12 @@ bool BluetoothGetAdapterStateFunction::DoWork(
return true; return true;
} }
BluetoothGetDevicesFunction::BluetoothGetDevicesFunction()
: device_events_sent_(0) {}
void BluetoothGetDevicesFunction::DispatchDeviceSearchResult(
const BluetoothDevice& device) {
bluetooth::Device extension_device;
bluetooth::BluetoothDeviceToApiDevice(device, &extension_device);
GetEventRouter(browser_context())->DispatchDeviceEvent(
extensions::event_names::kBluetoothOnDeviceSearchResult,
extension_device);
device_events_sent_++;
}
void BluetoothGetDevicesFunction::FinishDeviceSearch() {
scoped_ptr<base::ListValue> args(new base::ListValue());
scoped_ptr<base::DictionaryValue> info(new base::DictionaryValue());
info->SetInteger("expectedEventCount", device_events_sent_);
args->Append(info.release());
scoped_ptr<extensions::Event> event(new extensions::Event(
extensions::event_names::kBluetoothOnDeviceSearchFinished, args.Pass()));
extensions::ExtensionSystem::Get(browser_context())
->event_router()
->BroadcastEvent(event.Pass());
SendResponse(true);
}
bool BluetoothGetDevicesFunction::DoWork( bool BluetoothGetDevicesFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) { scoped_refptr<BluetoothAdapter> adapter) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
scoped_ptr<GetDevices::Params> params(GetDevices::Params::Create(*args_)); base::ListValue* device_list = new base::ListValue;
EXTENSION_FUNCTION_VALIDATE(params.get() != NULL); SetResult(device_list);
const bluetooth::GetDevicesOptions& options = params->options;
std::string uuid;
if (options.profile.get() != NULL) {
uuid = options.profile->uuid;
if (!BluetoothDevice::IsUUIDValid(uuid)) {
SetError(kInvalidUuid);
SendResponse(false);
return false;
}
}
BluetoothAdapter::DeviceList devices = adapter->GetDevices(); BluetoothAdapter::DeviceList devices = adapter->GetDevices();
for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin(); for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin();
...@@ -325,11 +291,14 @@ bool BluetoothGetDevicesFunction::DoWork( ...@@ -325,11 +291,14 @@ bool BluetoothGetDevicesFunction::DoWork(
++iter) { ++iter) {
const BluetoothDevice* device = *iter; const BluetoothDevice* device = *iter;
DCHECK(device); DCHECK(device);
if (uuid.empty() || device->ProvidesServiceWithUUID(uuid))
DispatchDeviceSearchResult(*device); bluetooth::Device extension_device;
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
device_list->Append(extension_device.ToValue().release());
} }
FinishDeviceSearch(); SendResponse(true);
return true; return true;
} }
...@@ -637,12 +606,10 @@ void BluetoothStartDiscoveryFunction::OnSuccessCallback() { ...@@ -637,12 +606,10 @@ void BluetoothStartDiscoveryFunction::OnSuccessCallback() {
void BluetoothStartDiscoveryFunction::OnErrorCallback() { void BluetoothStartDiscoveryFunction::OnErrorCallback() {
SetError(kStartDiscoveryFailed); SetError(kStartDiscoveryFailed);
SendResponse(false); SendResponse(false);
GetEventRouter(browser_context())->OnListenerRemoved();
} }
bool BluetoothStartDiscoveryFunction::DoWork( bool BluetoothStartDiscoveryFunction::DoWork(
scoped_refptr<BluetoothAdapter> adapter) { scoped_refptr<BluetoothAdapter> adapter) {
GetEventRouter(browser_context())->OnListenerAdded();
GetEventRouter(browser_context())->StartDiscoverySession( GetEventRouter(browser_context())->StartDiscoverySession(
adapter, adapter,
extension_id(), extension_id(),
...@@ -654,13 +621,11 @@ bool BluetoothStartDiscoveryFunction::DoWork( ...@@ -654,13 +621,11 @@ bool BluetoothStartDiscoveryFunction::DoWork(
void BluetoothStopDiscoveryFunction::OnSuccessCallback() { void BluetoothStopDiscoveryFunction::OnSuccessCallback() {
SendResponse(true); SendResponse(true);
GetEventRouter(browser_context())->OnListenerRemoved();
} }
void BluetoothStopDiscoveryFunction::OnErrorCallback() { void BluetoothStopDiscoveryFunction::OnErrorCallback() {
SetError(kStopDiscoveryFailed); SetError(kStopDiscoveryFailed);
SendResponse(false); SendResponse(false);
GetEventRouter(browser_context())->OnListenerRemoved();
} }
bool BluetoothStopDiscoveryFunction::DoWork( bool BluetoothStopDiscoveryFunction::DoWork(
......
...@@ -128,19 +128,11 @@ class BluetoothGetDevicesFunction : public BluetoothExtensionFunction { ...@@ -128,19 +128,11 @@ class BluetoothGetDevicesFunction : public BluetoothExtensionFunction {
public: public:
DECLARE_EXTENSION_FUNCTION("bluetooth.getDevices", BLUETOOTH_GETDEVICES) DECLARE_EXTENSION_FUNCTION("bluetooth.getDevices", BLUETOOTH_GETDEVICES)
BluetoothGetDevicesFunction();
protected: protected:
virtual ~BluetoothGetDevicesFunction() {} virtual ~BluetoothGetDevicesFunction() {}
// BluetoothExtensionFunction: // BluetoothExtensionFunction:
virtual bool DoWork(scoped_refptr<device::BluetoothAdapter> adapter) OVERRIDE; virtual bool DoWork(scoped_refptr<device::BluetoothAdapter> adapter) OVERRIDE;
private:
void DispatchDeviceSearchResult(const device::BluetoothDevice& device);
void FinishDeviceSearch();
int device_events_sent_;
}; };
class BluetoothGetServicesFunction : public BluetoothExtensionFunction { class BluetoothGetServicesFunction : public BluetoothExtensionFunction {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_api.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_api.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h"
#include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h"
...@@ -66,6 +67,10 @@ class BluetoothApiTest : public ExtensionApiTest { ...@@ -66,6 +67,10 @@ class BluetoothApiTest : public ExtensionApiTest {
device2_.reset(new testing::NiceMock<MockBluetoothDevice>( device2_.reset(new testing::NiceMock<MockBluetoothDevice>(
mock_adapter_, 0, "d2", "21:22:23:24:25:26", mock_adapter_, 0, "d2", "21:22:23:24:25:26",
false /* paired */, false /* connected */)); false /* paired */, false /* connected */));
device3_.reset(new testing::NiceMock<MockBluetoothDevice>(
mock_adapter_, 0, "d3", "31:32:33:34:35:36",
false /* paired */, false /* connected */));
} }
void DiscoverySessionCallback( void DiscoverySessionCallback(
...@@ -91,6 +96,7 @@ class BluetoothApiTest : public ExtensionApiTest { ...@@ -91,6 +96,7 @@ class BluetoothApiTest : public ExtensionApiTest {
scoped_ptr<testing::NiceMock<MockBluetoothDiscoverySession> > mock_session_; scoped_ptr<testing::NiceMock<MockBluetoothDiscoverySession> > mock_session_;
scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device1_; scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device1_;
scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device2_; scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device2_;
scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device3_;
scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile1_; scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile1_;
scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile2_; scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile2_;
...@@ -323,6 +329,31 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, SetOutOfBandPairingData) { ...@@ -323,6 +329,31 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, SetOutOfBandPairingData) {
// crbug.com/132796 // crbug.com/132796
} }
IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DeviceEvents) {
ResultCatcher catcher;
catcher.RestrictToProfile(browser()->profile());
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII("bluetooth/device_events")));
ExtensionTestMessageListener events_received("ready", true);
event_router()->DeviceAdded(mock_adapter_, device1_.get());
event_router()->DeviceAdded(mock_adapter_, device2_.get());
EXPECT_CALL(*device2_.get(), GetDeviceName())
.WillRepeatedly(testing::Return("the real d2"));
EXPECT_CALL(*device2_.get(), GetName())
.WillRepeatedly(testing::Return(base::UTF8ToUTF16("the real d2")));
event_router()->DeviceChanged(mock_adapter_, device2_.get());
event_router()->DeviceAdded(mock_adapter_, device3_.get());
event_router()->DeviceRemoved(mock_adapter_, device1_.get());
EXPECT_TRUE(events_received.WaitUntilSatisfied());
events_received.Reply("go");
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) {
// Try with a failure to start. This will return an error as we haven't // Try with a failure to start. This will return an error as we haven't
// initialied a session object. // initialied a session object.
...@@ -330,8 +361,7 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { ...@@ -330,8 +361,7 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) {
.WillOnce( .WillOnce(
testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback)); testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback));
// StartDiscovery failure will remove the adapter that is no longer used. // StartDiscovery failure will not reference the adapter.
EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_));
scoped_refptr<api::BluetoothStartDiscoveryFunction> start_function; scoped_refptr<api::BluetoothStartDiscoveryFunction> start_function;
start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); start_function = setupFunction(new api::BluetoothStartDiscoveryFunction);
std::string error( std::string error(
...@@ -360,8 +390,8 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { ...@@ -360,8 +390,8 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) {
EXPECT_CALL(*session, Stop(testing::_, testing::_)) EXPECT_CALL(*session, Stop(testing::_, testing::_))
.WillOnce(testing::Invoke(StopDiscoverySessionCallback)); .WillOnce(testing::Invoke(StopDiscoverySessionCallback));
// StopDiscovery success will remove the adapter that is no longer used. // StopDiscovery success will remove the session object, unreferencing the
EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); // adapter.
scoped_refptr<api::BluetoothStopDiscoveryFunction> stop_function; scoped_refptr<api::BluetoothStopDiscoveryFunction> stop_function;
stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction);
(void) utils::RunFunctionAndReturnSingleResult( (void) utils::RunFunctionAndReturnSingleResult(
...@@ -371,7 +401,6 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { ...@@ -371,7 +401,6 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) {
// still owns the session. Make it appear inactive. // still owns the session. Make it appear inactive.
SetUpMockAdapter(); SetUpMockAdapter();
EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(false)); EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(false));
EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_));
stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction);
error = error =
utils::RunFunctionAndReturnError(stop_function.get(), "[]", browser()); utils::RunFunctionAndReturnError(stop_function.get(), "[]", browser());
...@@ -571,14 +600,8 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevices) { ...@@ -571,14 +600,8 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevices) {
devices.push_back(device1_.get()); devices.push_back(device1_.get());
devices.push_back(device2_.get()); devices.push_back(device2_.get());
EXPECT_CALL(*device1_, ProvidesServiceWithUUID(testing::_))
.WillOnce(testing::Return(false));
EXPECT_CALL(*device2_, ProvidesServiceWithUUID(testing::_))
.WillOnce(testing::Return(true));
EXPECT_CALL(*mock_adapter_, GetDevices()) EXPECT_CALL(*mock_adapter_, GetDevices())
.Times(2) .Times(1)
.WillRepeatedly(testing::Return(devices)); .WillRepeatedly(testing::Return(devices));
// Load and wait for setup // Load and wait for setup
...@@ -591,18 +614,3 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevices) { ...@@ -591,18 +614,3 @@ IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevices) {
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
} }
IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevicesError) {
ResultCatcher catcher;
catcher.RestrictToProfile(browser()->profile());
// Load and wait for setup
ExtensionTestMessageListener listener("ready", true);
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII("bluetooth/get_devices_error")));
EXPECT_TRUE(listener.WaitUntilSatisfied());
listener.Reply("go");
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_api_utils.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_api_utils.h"
#include "chrome/browser/extensions/event_names.h"
#include "chrome/common/extensions/api/bluetooth.h" #include "chrome/common/extensions/api/bluetooth.h"
#include "content/public/browser/notification_details.h" #include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h" #include "content/public/browser/notification_source.h"
...@@ -210,15 +209,6 @@ ExtensionBluetoothEventRouter::GetSocket(int id) { ...@@ -210,15 +209,6 @@ ExtensionBluetoothEventRouter::GetSocket(int id) {
return socket_entry->second.socket; return socket_entry->second.socket;
} }
void ExtensionBluetoothEventRouter::DispatchDeviceEvent(
const std::string& event_name, const bluetooth::Device& device) {
scoped_ptr<base::ListValue> args(new base::ListValue());
args->Append(device.ToValue().release());
scoped_ptr<Event> event(new Event(event_name, args.Pass()));
ExtensionSystem::Get(browser_context_)->event_router()->BroadcastEvent(
event.Pass());
}
void ExtensionBluetoothEventRouter::DispatchConnectionEvent( void ExtensionBluetoothEventRouter::DispatchConnectionEvent(
const std::string& extension_id, const std::string& extension_id,
const std::string& uuid, const std::string& uuid,
...@@ -228,13 +218,13 @@ void ExtensionBluetoothEventRouter::DispatchConnectionEvent( ...@@ -228,13 +218,13 @@ void ExtensionBluetoothEventRouter::DispatchConnectionEvent(
return; return;
int socket_id = RegisterSocket(extension_id, socket); int socket_id = RegisterSocket(extension_id, socket);
api::bluetooth::Socket result_socket; bluetooth::Socket result_socket;
api::bluetooth::BluetoothDeviceToApiDevice(*device, &result_socket.device); bluetooth::BluetoothDeviceToApiDevice(*device, &result_socket.device);
result_socket.profile.uuid = uuid; result_socket.profile.uuid = uuid;
result_socket.id = socket_id; result_socket.id = socket_id;
scoped_ptr<base::ListValue> args(new base::ListValue()); scoped_ptr<base::ListValue> args =
args->Append(result_socket.ToValue().release()); bluetooth::OnConnection::Create(result_socket);
scoped_ptr<Event> event(new Event( scoped_ptr<Event> event(new Event(
bluetooth::OnConnection::kEventName, args.Pass())); bluetooth::OnConnection::kEventName, args.Pass()));
ExtensionSystem::Get(browser_context_) ExtensionSystem::Get(browser_context_)
...@@ -294,11 +284,29 @@ void ExtensionBluetoothEventRouter::DeviceAdded( ...@@ -294,11 +284,29 @@ void ExtensionBluetoothEventRouter::DeviceAdded(
return; return;
} }
bluetooth::Device extension_device; DispatchDeviceEvent(bluetooth::OnDeviceAdded::kEventName, device);
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device); }
DispatchDeviceEvent(extensions::event_names::kBluetoothOnDeviceDiscovered, void ExtensionBluetoothEventRouter::DeviceChanged(
extension_device); device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
if (adapter != adapter_.get()) {
DVLOG(1) << "Ignoring event for adapter " << adapter->GetAddress();
return;
}
DispatchDeviceEvent(bluetooth::OnDeviceChanged::kEventName, device);
}
void ExtensionBluetoothEventRouter::DeviceRemoved(
device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
if (adapter != adapter_.get()) {
DVLOG(1) << "Ignoring event for adapter " << adapter->GetAddress();
return;
}
DispatchDeviceEvent(bluetooth::OnDeviceRemoved::kEventName, device);
} }
void ExtensionBluetoothEventRouter::InitializeAdapterIfNeeded() { void ExtensionBluetoothEventRouter::InitializeAdapterIfNeeded() {
...@@ -324,11 +332,11 @@ void ExtensionBluetoothEventRouter::MaybeReleaseAdapter() { ...@@ -324,11 +332,11 @@ void ExtensionBluetoothEventRouter::MaybeReleaseAdapter() {
} }
void ExtensionBluetoothEventRouter::DispatchAdapterStateEvent() { void ExtensionBluetoothEventRouter::DispatchAdapterStateEvent() {
api::bluetooth::AdapterState state; bluetooth::AdapterState state;
PopulateAdapterState(*adapter_.get(), &state); PopulateAdapterState(*adapter_.get(), &state);
scoped_ptr<base::ListValue> args(new base::ListValue()); scoped_ptr<base::ListValue> args =
args->Append(state.ToValue().release()); bluetooth::OnAdapterStateChanged::Create(state);
scoped_ptr<Event> event(new Event( scoped_ptr<Event> event(new Event(
bluetooth::OnAdapterStateChanged::kEventName, bluetooth::OnAdapterStateChanged::kEventName,
args.Pass())); args.Pass()));
...@@ -336,6 +344,19 @@ void ExtensionBluetoothEventRouter::DispatchAdapterStateEvent() { ...@@ -336,6 +344,19 @@ void ExtensionBluetoothEventRouter::DispatchAdapterStateEvent() {
event.Pass()); event.Pass());
} }
void ExtensionBluetoothEventRouter::DispatchDeviceEvent(
const std::string& event_name,
device::BluetoothDevice* device) {
bluetooth::Device extension_device;
bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
scoped_ptr<base::ListValue> args =
bluetooth::OnDeviceAdded::Create(extension_device);
scoped_ptr<Event> event(new Event(event_name, args.Pass()));
ExtensionSystem::Get(browser_context_)->event_router()->BroadcastEvent(
event.Pass());
}
void ExtensionBluetoothEventRouter::CleanUpForExtension( void ExtensionBluetoothEventRouter::CleanUpForExtension(
const std::string& extension_id) { const std::string& extension_id) {
// Remove all profiles added by the extension. // Remove all profiles added by the extension.
......
...@@ -108,11 +108,6 @@ class ExtensionBluetoothEventRouter ...@@ -108,11 +108,6 @@ class ExtensionBluetoothEventRouter
// Get the BluetoothSocket corresponding to |id|. // Get the BluetoothSocket corresponding to |id|.
scoped_refptr<device::BluetoothSocket> GetSocket(int id); scoped_refptr<device::BluetoothSocket> GetSocket(int id);
// Dispatch an event that takes a device as a parameter to all renderers.
void DispatchDeviceEvent(
const std::string& event_name,
const extensions::api::bluetooth::Device& device);
// Dispatch an event that takes a connection socket as a parameter to the // Dispatch an event that takes a connection socket as a parameter to the
// extension that registered the profile that the socket has connected to. // extension that registered the profile that the socket has connected to.
void DispatchConnectionEvent(const std::string& extension_id, void DispatchConnectionEvent(const std::string& extension_id,
...@@ -129,6 +124,10 @@ class ExtensionBluetoothEventRouter ...@@ -129,6 +124,10 @@ class ExtensionBluetoothEventRouter
bool discovering) OVERRIDE; bool discovering) OVERRIDE;
virtual void DeviceAdded(device::BluetoothAdapter* adapter, virtual void DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) OVERRIDE; device::BluetoothDevice* device) OVERRIDE;
virtual void DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) OVERRIDE;
virtual void DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) OVERRIDE;
// Overridden from content::NotificationObserver // Overridden from content::NotificationObserver
virtual void Observe(int type, virtual void Observe(int type,
...@@ -145,6 +144,8 @@ class ExtensionBluetoothEventRouter ...@@ -145,6 +144,8 @@ class ExtensionBluetoothEventRouter
void InitializeAdapter(scoped_refptr<device::BluetoothAdapter> adapter); void InitializeAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
void MaybeReleaseAdapter(); void MaybeReleaseAdapter();
void DispatchAdapterStateEvent(); void DispatchAdapterStateEvent();
void DispatchDeviceEvent(const std::string& event_name,
device::BluetoothDevice* device);
void CleanUpForExtension(const std::string& extension_id); void CleanUpForExtension(const std::string& extension_id);
void OnStartDiscoverySession( void OnStartDiscoverySession(
const std::string& extension_id, const std::string& extension_id,
......
...@@ -12,11 +12,6 @@ const char kOnInputMethodChanged[] = "inputMethodPrivate.onChanged"; ...@@ -12,11 +12,6 @@ const char kOnInputMethodChanged[] = "inputMethodPrivate.onChanged";
const char kOnContextMenus[] = "contextMenus"; const char kOnContextMenus[] = "contextMenus";
const char kBluetoothOnDeviceDiscovered[] = "bluetooth.onDeviceDiscovered";
const char kBluetoothOnDeviceSearchFinished[] =
"bluetooth.onDeviceSearchFinished";
const char kBluetoothOnDeviceSearchResult[] = "bluetooth.onDeviceSearchResult";
const char kOnNotificationError[] = "notifications.onError"; const char kOnNotificationError[] = "notifications.onError";
} // namespace event_names } // namespace event_names
......
...@@ -25,11 +25,6 @@ extern const char kOnInputMethodChanged[]; ...@@ -25,11 +25,6 @@ extern const char kOnInputMethodChanged[];
// Context menus. // Context menus.
extern const char kOnContextMenus[]; extern const char kOnContextMenus[];
// Bluetooth.
extern const char kBluetoothOnDeviceDiscovered[];
extern const char kBluetoothOnDeviceSearchFinished[];
extern const char kBluetoothOnDeviceSearchResult[];
// Notifications. // Notifications.
extern const char kOnNotificationError[]; extern const char kOnNotificationError[];
......
...@@ -104,7 +104,6 @@ namespace bluetooth { ...@@ -104,7 +104,6 @@ namespace bluetooth {
callback AddressCallback = void (DOMString result); callback AddressCallback = void (DOMString result);
callback BooleanCallback = void (boolean result); callback BooleanCallback = void (boolean result);
callback DataCallback = void (optional ArrayBuffer result); callback DataCallback = void (optional ArrayBuffer result);
callback DeviceCallback = void (Device device);
callback DevicesCallback = void (Device[] result); callback DevicesCallback = void (Device[] result);
callback NameCallback = void (DOMString result); callback NameCallback = void (DOMString result);
callback OutOfBandPairingDataCallback = void (OutOfBandPairingData data); callback OutOfBandPairingDataCallback = void (OutOfBandPairingData data);
...@@ -114,18 +113,6 @@ namespace bluetooth { ...@@ -114,18 +113,6 @@ namespace bluetooth {
callback SizeCallback = void (long result); callback SizeCallback = void (long result);
callback SocketCallback = void (Socket result); callback SocketCallback = void (Socket result);
// Options for the getDevices function. If |profile| is not provided, all
// devices known to the system are returned.
dictionary GetDevicesOptions {
// Only devices providing |profile| will be returned.
Profile? profile;
// Called for each matching device. Note that a service discovery request
// must be made to each non-matching device before it can be definitively
// excluded. This can take some time.
DeviceCallback deviceCallback;
};
// Options for the getProfiles function. // Options for the getProfiles function.
dictionary GetProfilesOptions { dictionary GetProfilesOptions {
// The remote Bluetooth device to retrieve the exported profiles list from. // The remote Bluetooth device to retrieve the exported profiles list from.
...@@ -180,12 +167,6 @@ namespace bluetooth { ...@@ -180,12 +167,6 @@ namespace bluetooth {
OutOfBandPairingData? data; OutOfBandPairingData? data;
}; };
// Options for the startDiscovery function.
dictionary StartDiscoveryOptions {
// Called for each device that is discovered.
DeviceCallback deviceCallback;
};
// These functions all report failures via chrome.runtime.lastError. // These functions all report failures via chrome.runtime.lastError.
interface Functions { interface Functions {
// Registers the JavaScript application as an implementation for the given // Registers the JavaScript application as an implementation for the given
...@@ -202,15 +183,10 @@ namespace bluetooth { ...@@ -202,15 +183,10 @@ namespace bluetooth {
// state. // state.
static void getAdapterState(AdapterStateCallback callback); static void getAdapterState(AdapterStateCallback callback);
// Get a bluetooth devices known to the system. Known devices are either // Get a list of Bluetooth devices known to the system, including paired
// currently paired, or have been paired in the past. // and recently discovered devices.
// |options| : Controls which devices are returned and provides
// |deviceCallback|, which is called for each matching device.
// |callback| : Called when the search is completed. // |callback| : Called when the search is completed.
// |options.deviceCallback| will not be called after static void getDevices(DevicesCallback callback);
// |callback| has been called.
static void getDevices(GetDevicesOptions options,
ResultCallback callback);
// Returns the set of exported profiles for the device specified in options. // Returns the set of exported profiles for the device specified in options.
// This function will not initiate a connection to the remote device. // This function will not initiate a connection to the remote device.
...@@ -259,14 +235,16 @@ namespace bluetooth { ...@@ -259,14 +235,16 @@ namespace bluetooth {
static void setOutOfBandPairingData(SetOutOfBandPairingDataOptions options, static void setOutOfBandPairingData(SetOutOfBandPairingDataOptions options,
optional ResultCallback callback); optional ResultCallback callback);
// Start discovery. Discovered devices will be returned via the // Start discovery. Newly discovered devices will be returned via the
// |onDeviceDiscovered| callback. Discovery will fail to start if it is // onDeviceAdded event. Previously discovered devices already known to
// already in progress. Discovery can be resource intensive: stopDiscovery // the adapter must be obtained using getDevices and will only be updated
// using the |onDeviceChanged| event if information about them changes.
//
// Discovery will fail to start if this application has already called
// startDiscovery. Discovery can be resource intensive: stopDiscovery
// should be called as soon as possible. // should be called as soon as possible.
// |options| : The options for this function.
// |callback| : Called to indicate success or failure. // |callback| : Called to indicate success or failure.
static void startDiscovery( static void startDiscovery(
StartDiscoveryOptions options,
optional ResultCallback callback); optional ResultCallback callback);
// Stop discovery. // Stop discovery.
...@@ -280,6 +258,17 @@ namespace bluetooth { ...@@ -280,6 +258,17 @@ namespace bluetooth {
// |state| : The new state of the adapter. // |state| : The new state of the adapter.
static void onAdapterStateChanged(AdapterState state); static void onAdapterStateChanged(AdapterState state);
// Fired when information about a new Bluetooth device is available.
static void onDeviceAdded(Device device);
// Fired when information about a known Bluetooth device has changed.
static void onDeviceChanged(Device device);
// Fired when a Bluetooth device that was previously discovered has been
// out of range for long enough to be considered unavailable again, and
// when a paired device is removed.
static void onDeviceRemoved(Device device);
// Fired when a connection has been made for a registered profile. // Fired when a connection has been made for a registered profile.
// |socket| : The socket for the connection. // |socket| : The socket for the connection.
static void onConnection(Socket socket); static void onConnection(Socket socket);
......
...@@ -984,7 +984,6 @@ void Dispatcher::PopulateSourceMap() { ...@@ -984,7 +984,6 @@ void Dispatcher::PopulateSourceMap() {
source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("bluetooth", IDR_BLUETOOTH_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("browserAction", source_map_.RegisterSource("browserAction",
IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("contextMenus", source_map_.RegisterSource("contextMenus",
......
// 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.
// Custom binding for the Bluetooth API.
var binding = require('binding').Binding.create('bluetooth');
var chrome = requireNative('chrome').GetChrome();
var Event = require('event_bindings').Event;
var lastError = require('lastError');
var sendRequest = require('sendRequest').sendRequest;
// Use custom binding to create an undocumented event listener that will
// receive events about device discovery and call the event listener that was
// provided with the request to begin discovery.
binding.registerCustomHook(function(api) {
var apiFunctions = api.apiFunctions;
var bluetooth = {};
function callCallbackIfPresent(name, args, error) {
var callback = args[args.length - 1];
if (typeof(callback) == 'function')
lastError.run(name, error, callback);
}
bluetooth.deviceDiscoveredHandler = null;
bluetooth.onDeviceDiscovered = new Event('bluetooth.onDeviceDiscovered');
function clearDeviceDiscoveredHandler() {
bluetooth.onDeviceDiscovered.removeListener(
bluetooth.deviceDiscoveredHandler);
bluetooth.deviceDiscoveredHandler = null;
}
apiFunctions.setHandleRequest('startDiscovery',
function() {
var args = arguments;
if (args.length > 0 && args[0] && args[0].deviceCallback) {
if (bluetooth.deviceDiscoveredHandler != null) {
callCallbackIfPresent('bluetooth.startDiscovery',
args,
'Concurrent discovery is not allowed.');
return;
}
bluetooth.deviceDiscoveredHandler = args[0].deviceCallback;
bluetooth.onDeviceDiscovered.addListener(
bluetooth.deviceDiscoveredHandler);
sendRequest(this.name,
args,
this.definition.parameters,
{customCallback:this.customCallback});
} else {
callCallbackIfPresent(
'bluetooth.startDiscovery',
args,
'deviceCallback is required in the options object');
return;
}
});
apiFunctions.setCustomCallback('startDiscovery',
function(name, request, response) {
if (chrome.runtime.lastError) {
clearDeviceDiscoveredHandler();
return;
}
});
apiFunctions.setHandleRequest('stopDiscovery',
function() {
clearDeviceDiscoveredHandler();
sendRequest(this.name, arguments, this.definition.parameters);
});
// An object to hold state during one call to getDevices.
bluetooth.getDevicesState = null;
// Hidden events used to deliver getDevices data to the client callbacks
bluetooth.onDeviceSearchResult = new Event('bluetooth.onDeviceSearchResult');
bluetooth.onDeviceSearchFinished =
new Event('bluetooth.onDeviceSearchFinished');
function deviceSearchResultHandler(device) {
bluetooth.getDevicesState.actualEvents++;
bluetooth.getDevicesState.deviceCallback(device);
maybeFinishDeviceSearch();
}
function deviceSearchFinishedHandler(info) {
bluetooth.getDevicesState.expectedEventCount = info.expectedEventCount;
maybeFinishDeviceSearch();
}
function addDeviceSearchListeners() {
bluetooth.onDeviceSearchResult.addListener(deviceSearchResultHandler);
bluetooth.onDeviceSearchFinished.addListener(deviceSearchFinishedHandler);
}
function removeDeviceSearchListeners() {
bluetooth.onDeviceSearchResult.removeListener(deviceSearchResultHandler);
bluetooth.onDeviceSearchFinished.removeListener(
deviceSearchFinishedHandler);
}
function maybeFinishDeviceSearch() {
var state = bluetooth.getDevicesState;
if (typeof(state.expectedEventCount) != 'undefined' &&
state.actualEvents >= state.expectedEventCount) {
finishDeviceSearch();
}
}
function finishDeviceSearch() {
var finalCallback = bluetooth.getDevicesState.finalCallback;
removeDeviceSearchListeners();
bluetooth.getDevicesState = null;
if (finalCallback) {
finalCallback();
}
}
apiFunctions.setUpdateArgumentsPostValidate('getDevices',
function() {
var args = $Array.slice(arguments);
if (bluetooth.getDevicesState != null) {
throw new Error('Concurrent calls to getDevices are not allowed.');
}
var state = { actualEvents: 0 };
if (typeof(args[args.length - 1]) == 'function') {
state.finalCallback = args.pop();
$Array.push(args,
function() {
if (chrome.runtime.lastError) {
finishDeviceSearch();
}
});
} else {
throw new Error('getDevices must have a final callback parameter.');
}
if (typeof(args[0].deviceCallback) == 'function') {
state.deviceCallback = args[0].deviceCallback;
} else {
throw new Error('getDevices must be passed options with a ' +
'deviceCallback.');
}
bluetooth.getDevicesState = state;
addDeviceSearchListeners();
return args;
});
});
exports.binding = binding.generate();
...@@ -33,7 +33,6 @@ without changes to the corresponding grd file. fb9 --> ...@@ -33,7 +33,6 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\app_runtime_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\app_runtime_custom_bindings.js" type="BINDATA" />
<include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="extensions\app_window_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="extensions\app_window_custom_bindings.js" type="BINDATA" />
<include name="IDR_BINDING_JS" file="extensions\binding.js" type="BINDATA" /> <include name="IDR_BINDING_JS" file="extensions\binding.js" type="BINDATA" />
<include name="IDR_BLUETOOTH_CUSTOM_BINDINGS_JS" file="extensions\bluetooth_custom_bindings.js" type="BINDATA" />
<include name="IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS" file="extensions\browser_action_custom_bindings.js" type="BINDATA" /> <include name="IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS" file="extensions\browser_action_custom_bindings.js" type="BINDATA" />
<include name="IDR_CAST_STREAMING_RTP_STREAM_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_rtp_stream_custom_bindings.js" type="BINDATA" /> <include name="IDR_CAST_STREAMING_RTP_STREAM_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_rtp_stream_custom_bindings.js" type="BINDATA" />
<include name="IDR_CAST_STREAMING_SESSION_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_session_custom_bindings.js" type="BINDATA" /> <include name="IDR_CAST_STREAMING_SESSION_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_session_custom_bindings.js" type="BINDATA" />
......
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "Test Errors From The Bluetooth getDevices API", "name": "Test Bluetooth Device Events",
"version": "1.0", "version": "1.0",
"description": "Tests the Bluetooth device events",
"app": { "app": {
"background": { "background": {
"scripts": ["runtest.js"] "scripts": ["runtest.js"]
......
// Copyright (c) 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.
var kExpectedDeviceNames = {
"21:22:23:24:25:26": "the real d2",
"31:32:33:34:35:36": "d3"
};
function testDeviceEvents() {
var expectedAddresses = Object.keys(kExpectedDeviceNames);
chrome.test.assertEq(expectedAddresses.length, Object.keys(devices).length);
for (var i = 0; i < expectedAddresses.length; ++i) {
var address = expectedAddresses[i];
chrome.test.assertTrue(address in devices);
chrome.test.assertEq(kExpectedDeviceNames[address], devices[address].name);
}
chrome.test.succeed();
}
function startTests() {
chrome.test.runTests([testDeviceEvents]);
}
var devices = {};
function recordDevice(device) {
devices[device.address] = device;
}
function removeDevice(device) {
delete devices[device.address];
chrome.test.sendMessage('ready', startTests);
}
function stopDiscoveryAndContinue() {
chrome.bluetooth.stopDiscovery();
chrome.bluetooth.onDeviceAdded.removeListener(recordDevice);
sendReady(startTests);
}
chrome.bluetooth.onDeviceAdded.addListener(recordDevice);
chrome.bluetooth.onDeviceChanged.addListener(recordDevice);
chrome.bluetooth.onDeviceRemoved.addListener(removeDevice);
...@@ -23,16 +23,17 @@ function sendReady(callback) { ...@@ -23,16 +23,17 @@ function sendReady(callback) {
chrome.test.sendMessage('ready', callback); chrome.test.sendMessage('ready', callback);
} }
function stopDiscoveryAndContinue() {
chrome.bluetooth.stopDiscovery();
sendReady(startTests);
}
var discoveredDevices = []; var discoveredDevices = [];
function recordDevice(device) { function recordDevice(device) {
discoveredDevices.push(device); discoveredDevices.push(device);
} }
function stopDiscoveryAndContinue() {
chrome.bluetooth.stopDiscovery();
chrome.bluetooth.onDeviceAdded.removeListener(recordDevice);
sendReady(startTests);
}
chrome.bluetooth.onDeviceAdded.addListener(recordDevice);
chrome.bluetooth.startDiscovery( chrome.bluetooth.startDiscovery(
{ deviceCallback:recordDevice },
function() { sendReady(stopDiscoveryAndContinue); }); function() { sendReady(stopDiscoveryAndContinue); });
...@@ -23,16 +23,17 @@ function sendReady(callback) { ...@@ -23,16 +23,17 @@ function sendReady(callback) {
chrome.test.sendMessage('ready', callback); chrome.test.sendMessage('ready', callback);
} }
function stopDiscoveryAndContinue() {
chrome.bluetooth.stopDiscovery();
sendReady(startTests);
}
var discoveredDevices = []; var discoveredDevices = [];
function recordDevice(device) { function recordDevice(device) {
discoveredDevices.push(device); discoveredDevices.push(device);
} }
function stopDiscoveryAndContinue() {
chrome.bluetooth.stopDiscovery();
chrome.bluetooth.onDeviceAdded.removeListener(recordDevice);
sendReady(startTests);
}
chrome.bluetooth.onDeviceAdded.addListener(recordDevice);
chrome.bluetooth.startDiscovery( chrome.bluetooth.startDiscovery(
{ deviceCallback:recordDevice },
function() { sendReady(stopDiscoveryAndContinue); }); function() { sendReady(stopDiscoveryAndContinue); });
...@@ -3,26 +3,14 @@ ...@@ -3,26 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
function testGetDevices() { function testGetDevices() {
chrome.test.assertEq(2, devices['all'].length); chrome.test.assertEq(2, devices.length);
chrome.test.assertEq('d1', devices['all'][0].name); chrome.test.assertEq('d1', devices[0].name);
chrome.test.assertEq('d2', devices['all'][1].name); chrome.test.assertEq('d2', devices[1].name);
chrome.test.assertEq(1, devices['uuid'].length);
chrome.test.assertEq('d2', devices['uuid'][0].name);
chrome.test.succeed(); chrome.test.succeed();
} }
var devices = { var devices = null;
'all': [],
'uuid': []
};
function recordDevicesInto(arrayKey) {
return function(device) {
devices[arrayKey].push(device);
};
}
function failOnError() { function failOnError() {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
...@@ -31,19 +19,11 @@ function failOnError() { ...@@ -31,19 +19,11 @@ function failOnError() {
} }
chrome.bluetooth.getDevices( chrome.bluetooth.getDevices(
{deviceCallback: recordDevicesInto('all')}, function(result) {
function() {
failOnError(); failOnError();
chrome.bluetooth.getDevices( devices = result;
{ chrome.test.sendMessage('ready',
profile: {uuid: '00000010-0000-1000-8000-00805f9b34fb'}, function(message) {
deviceCallback: recordDevicesInto('uuid') chrome.test.runTests([testGetDevices]);
},
function() {
failOnError();
chrome.test.sendMessage('ready',
function(message) {
chrome.test.runTests([testGetDevices]);
});
}); });
}); });
// 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.
function testGetDevicesReturnsError() {
chrome.test.assertEq("Invalid UUID", error);
chrome.test.succeed();
}
var error = "";
chrome.bluetooth.getDevices(
{
profile: {uuid:'this is nonsense'},
deviceCallback: function() {}
},
function() {
if (chrome.runtime.lastError) {
error = chrome.runtime.lastError.message;
}
chrome.test.sendMessage('ready',
function(message) {
chrome.test.runTests([testGetDevicesReturnsError]);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment