diff options
author | rkc <rkc@chromium.org> | 2014-10-07 14:27:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-07 21:27:34 +0000 |
commit | a3e8914682cb7e046b5a76e93c053f070ea25a0b (patch) | |
tree | f86a43abc6257b908ffbcbee14b42f43f4d34a11 | |
parent | 5fb1e3aedf28059eb8bb51aa1b530f24be2f18ef (diff) | |
download | chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.zip chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.tar.gz chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.tar.bz2 |
Prototype for copresenceSockets.
This is the prototype implementation of the chrome.copresenceSocket API. This
CL adds the API and the copresence_sockets component (which is a currently a
thin wrapper around the bluetooth APIs).
Only the receive parts of the code have been implemented with this CL.
Reviews requested,
rockot@ - Extension parts
armansito@ - BT parts
caitkp@ - Owners review for components
BUG=418615
Review URL: https://codereview.chromium.org/610633002
Cr-Commit-Position: refs/heads/master@{#298577}
24 files changed, 972 insertions, 1 deletions
diff --git a/chrome/common/extensions/docs/templates/public/apps/copresenceSocket.html b/chrome/common/extensions/docs/templates/public/apps/copresenceSocket.html new file mode 100644 index 0000000..62ffccc --- /dev/null +++ b/chrome/common/extensions/docs/templates/public/apps/copresenceSocket.html @@ -0,0 +1 @@ +{{+partials.standard_apps_api api:apis.apps.copresence_socket/}} diff --git a/components/BUILD.gn b/components/BUILD.gn index 2eed3b7..90b52a1 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn @@ -27,6 +27,7 @@ group("all_components") { "//components/component_updater", "//components/content_settings/core/browser", "//components/content_settings/core/common", + "//components/copresence_sockets", "//components/crash/app", "//components/crash/browser", "//components/crx_file", @@ -114,7 +115,10 @@ group("all_components") { deps -= [ "//components/pairing" ] } if (is_ios) { - deps -= [ "//components/keyed_service/content" ] + deps -= [ + "//components/keyed_service/content", + "//components/copresence_sockets", + ] } if (!enable_plugins) { diff --git a/components/OWNERS b/components/OWNERS index aa88043..a1c635a 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -20,6 +20,8 @@ per-file copresence.gypi=ckehoe@chromium.org per-file copresence.gypi=xiyuan@chromium.org per-file copresence.gypi=derat@chromium.org +per-file copresence_sockets.gypi=rkc@chromium.org + per-file crash.gypi=jochen@chromium.org per-file crash.gypi=rsesek@chromium.org per-file crash.gypi=thestig@chromium.org diff --git a/components/components.gyp b/components/components.gyp index b9af219..5778920 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -62,6 +62,7 @@ ['OS != "ios"', { 'includes': [ 'cdm.gypi', + 'copresence_sockets.gypi', 'navigation_interception.gypi', 'plugins.gypi', 'power.gypi', diff --git a/components/copresence_sockets.gypi b/components/copresence_sockets.gypi new file mode 100644 index 0000000..60cb957 --- /dev/null +++ b/components/copresence_sockets.gypi @@ -0,0 +1,28 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'copresence_sockets', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + '../device/bluetooth/bluetooth.gyp:device_bluetooth', + '../net/net.gyp:net', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + # GN version: //components/copresence_sockets + 'copresence_sockets/copresence_peer.cc', + 'copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.cc', + 'copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h', + 'copresence_sockets/public/copresence_peer.h', + 'copresence_sockets/public/copresence_socket.h', + ], + }, + ], +} diff --git a/components/copresence_sockets/BUILD.gn b/components/copresence_sockets/BUILD.gn new file mode 100644 index 0000000..f48f601 --- /dev/null +++ b/components/copresence_sockets/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +static_library("copresence_sockets") { + sources = [ + "copresence_peer.cc", + "transports/bluetooth/copresence_socket_bluetooth.cc", + "transports/bluetooth/copresence_socket_bluetooth.h", + "public/copresence_peer.h", + "public/copresence_socket.h", + ] + + deps = [ + "//base", + "//device/bluetooth", + "//net", + ] +} diff --git a/components/copresence_sockets/DEPS b/components/copresence_sockets/DEPS new file mode 100644 index 0000000..338b701 --- /dev/null +++ b/components/copresence_sockets/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+base", + "+device/bluetooth", + "+net", +] diff --git a/components/copresence_sockets/OWNERS b/components/copresence_sockets/OWNERS new file mode 100644 index 0000000..6a2cb03 --- /dev/null +++ b/components/copresence_sockets/OWNERS @@ -0,0 +1 @@ +rkc@chromium.org diff --git a/components/copresence_sockets/copresence_peer.cc b/components/copresence_sockets/copresence_peer.cc new file mode 100644 index 0000000..af897df --- /dev/null +++ b/components/copresence_sockets/copresence_peer.cc @@ -0,0 +1,157 @@ +// 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 "components/copresence_sockets/public/copresence_peer.h" + +#include <stdint.h> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/format_macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h" +#include "device/bluetooth/bluetooth_adapter.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "device/bluetooth/bluetooth_uuid.h" + +namespace { + +const char kAdapterError[] = "NOADAPTER"; + +device::BluetoothUUID GenerateRandomUuid() { + // Random hex string of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. + return device::BluetoothUUID(base::StringPrintf( + "%08" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%012" PRIx64, + base::RandGenerator(UINT32_MAX), + base::RandGenerator(UINT16_MAX), + base::RandGenerator(UINT16_MAX), + base::RandGenerator(UINT16_MAX), + base::RandGenerator(static_cast<uint64>(UINT16_MAX) + UINT32_MAX))); +} + +// This class will confirm pairing for a device that is expecting a pairing +// confirmation. +class DefaultApprovalDelegate + : public device::BluetoothDevice::PairingDelegate { + public: + DefaultApprovalDelegate() {} + virtual ~DefaultApprovalDelegate() {} + + // device::BluetoothDevice::PairingDelegate overrides: + virtual void RequestPinCode(device::BluetoothDevice* device) override {} + virtual void RequestPasskey(device::BluetoothDevice* device) override {} + virtual void DisplayPinCode(device::BluetoothDevice* device, + const std::string& pincode) override {} + virtual void DisplayPasskey(device::BluetoothDevice* device, + uint32 passkey) override {} + virtual void KeysEntered(device::BluetoothDevice* device, + uint32 entered) override {} + virtual void ConfirmPasskey(device::BluetoothDevice* device, + uint32 passkey) override {} + virtual void AuthorizePairing(device::BluetoothDevice* device) override { + if (device->ExpectingConfirmation()) + device->ConfirmPairing(); + } +}; + +} // namespace + +namespace copresence_sockets { + +CopresencePeer::CopresencePeer(const CreatePeerCallback& create_callback, + const AcceptCallback& accept_callback) + : create_callback_(create_callback), + accept_callback_(accept_callback), + delegate_(nullptr), + weak_ptr_factory_(this) { + DCHECK(!create_callback.is_null()); + DCHECK(!accept_callback.is_null()); + + if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { + create_callback_.Run(std::string()); + return; + } + + device::BluetoothAdapterFactory::GetAdapter(base::Bind( + &CopresencePeer::OnGetAdapter, weak_ptr_factory_.GetWeakPtr())); +} + +std::string CopresencePeer::GetLocatorData() { + if (!adapter_.get()) + return kAdapterError; + // TODO(rkc): Fix the "1." once we have finalized the locator format with + // other platforms. http://crbug.com/418616 + return "1." + adapter_->GetAddress() + "." + service_uuid_.value(); +} + +CopresencePeer::~CopresencePeer() { + server_socket_->Disconnect(base::Bind(&base::DoNothing)); + server_socket_->Close(); + if (delegate_) + adapter_->RemovePairingDelegate(delegate_.get()); +} + +// Private methods. + +void CopresencePeer::OnGetAdapter( + scoped_refptr<device::BluetoothAdapter> adapter) { + if (!adapter.get() || !adapter->IsPresent() || !adapter->IsPowered()) { + create_callback_.Run(std::string()); + return; + } + + adapter_ = adapter; + service_uuid_ = GenerateRandomUuid(); + + delegate_ = make_scoped_ptr(new DefaultApprovalDelegate()); + VLOG(2) << "Creating service with UUID: " << service_uuid_.value(); + adapter_->AddPairingDelegate( + delegate_.get(), + device::BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + adapter_->CreateRfcommService( + service_uuid_, + device::BluetoothAdapter::ServiceOptions(), + base::Bind(&CopresencePeer::OnCreateService, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CopresencePeer::OnCreateServiceError, + weak_ptr_factory_.GetWeakPtr())); +} + +void CopresencePeer::OnCreateService( + scoped_refptr<device::BluetoothSocket> socket) { + if (!socket.get()) { + create_callback_.Run(std::string()); + return; + } + + server_socket_ = socket; + create_callback_.Run(GetLocatorData()); + server_socket_->Accept( + base::Bind(&CopresencePeer::OnAccept, weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CopresencePeer::OnAcceptError, + weak_ptr_factory_.GetWeakPtr())); +} + +void CopresencePeer::OnCreateServiceError(const std::string& message) { + LOG(WARNING) << "Couldn't create Bluetooth service: " << message; + create_callback_.Run(std::string()); +} + +void CopresencePeer::OnAccept(const device::BluetoothDevice* device, + scoped_refptr<device::BluetoothSocket> socket) { + if (!socket.get()) + return; + accept_callback_.Run(make_scoped_ptr(new CopresenceSocketBluetooth(socket))); +} + +void CopresencePeer::OnAcceptError(const std::string& message) { + LOG(WARNING) << "Couldn't accept Bluetooth connection: " << message; +} + +} // namespace copresence_sockets diff --git a/components/copresence_sockets/public/copresence_peer.h b/components/copresence_sockets/public/copresence_peer.h new file mode 100644 index 0000000..6c8d09b --- /dev/null +++ b/components/copresence_sockets/public/copresence_peer.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_PEER_H_ +#define COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_PEER_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_uuid.h" + +namespace device { +class BluetoothAdapter; +class BluetoothDevice; +class BluetoothSocket; +} + +namespace copresence_sockets { + +class CopresenceSocket; + +// A CopresencePeer is an object that can be connected to. Creating a peer +// will create a server that will listen on a medium (currently only Bluetooth) +// and accept connections. +class CopresencePeer { + public: + // Callback with the locator data for the created peer. On a failed create, + // the locator string will be empty. + typedef base::Callback<void(const std::string&)> CreatePeerCallback; + + // Callback that accepts connections. The callback takes ownership of the + // Copresence socket passed in the parameter. + typedef base::Callback<void(scoped_ptr<CopresenceSocket> socket)> + AcceptCallback; + + // Create a CopresencePeer and start listening for connections. Once the peer + // object is created, the locator data for the object is returned via + // create_callback. This locator data can be used to connect to this peer by + // remote CopresenceSockets. Any connections accepted by this peer are + // delivered as CopresenceSocket objects to accept_callback. + CopresencePeer(const CreatePeerCallback& create_callback, + const AcceptCallback& accept_callback); + + virtual ~CopresencePeer(); + + // This will return a string containing the data needed for a remote + // CopresenceSocket to connect to this peer. + std::string GetLocatorData(); + + private: + void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter); + void OnCreateService(scoped_refptr<device::BluetoothSocket> socket); + void OnCreateServiceError(const std::string& message); + + void OnAccept(const device::BluetoothDevice* device, + scoped_refptr<device::BluetoothSocket> socket); + void OnAcceptError(const std::string& message); + + scoped_refptr<device::BluetoothAdapter> adapter_; + scoped_refptr<device::BluetoothSocket> server_socket_; + device::BluetoothUUID service_uuid_; + + CreatePeerCallback create_callback_; + AcceptCallback accept_callback_; + + scoped_ptr<device::BluetoothDevice::PairingDelegate> delegate_; + + base::WeakPtrFactory<CopresencePeer> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CopresencePeer); +}; + +} // namespace copresence_sockets + +#endif // COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_PEER_H_ diff --git a/components/copresence_sockets/public/copresence_socket.h b/components/copresence_sockets/public/copresence_socket.h new file mode 100644 index 0000000..c970732 --- /dev/null +++ b/components/copresence_sockets/public/copresence_socket.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_SOCKET_H_ +#define COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_SOCKET_H_ + +#include <string> + +#include "base/macros.h" + +namespace net { +class IOBuffer; +} + +namespace copresence_sockets { + +// A CopresenceSocket is an object that is used to send receive data to and +// from CopresencePeers. +// TODO(rkc): Add the ability to connect to a remote CopresencePeer. +class CopresenceSocket { + public: + typedef base::Callback<void(const scoped_refptr<net::IOBuffer>& buffer, + int buffer_size)> ReceiveCallback; + + CopresenceSocket() {} + virtual ~CopresenceSocket() {} + + // Send data on this socket. If unable to send the data, return false. This + // operation only guarantees that if the return value is a success, the send + // was started. It does not make any guarantees about the completion of the + // operation. + // TODO(rkc): Expand the bool into more a more detailed failures enum. + virtual bool Send(const scoped_refptr<net::IOBuffer>& buffer, + int buffer_size) = 0; + virtual void Receive(const ReceiveCallback& callback) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(CopresenceSocket); +}; + +} // namespace copresence_sockets + +#endif // COMPONENTS_COPRESENCE_SOCKETS_COPRESENCE_SOCKET_H_ diff --git a/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.cc b/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.cc new file mode 100644 index 0000000..5887ef5 --- /dev/null +++ b/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.cc @@ -0,0 +1,74 @@ +// 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 "components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h" + +#include "base/bind.h" +#include "base/strings/string_piece.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "net/base/io_buffer.h" + +namespace { + +// TODO(rkc): This number is totally arbitrary. Figure out what this should be. +const int kMaxReceiveBytes = 4096; + +} // namespace + +namespace copresence_sockets { + +CopresenceSocketBluetooth::CopresenceSocketBluetooth( + const scoped_refptr<device::BluetoothSocket>& socket) + : socket_(socket), weak_ptr_factory_(this) { +} + +CopresenceSocketBluetooth::~CopresenceSocketBluetooth() { +} + +bool CopresenceSocketBluetooth::Send(const scoped_refptr<net::IOBuffer>& buffer, + int buffer_size) { + socket_->Send(buffer, + buffer_size, + base::Bind(&CopresenceSocketBluetooth::OnSendComplete, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CopresenceSocketBluetooth::OnSendError, + weak_ptr_factory_.GetWeakPtr())); + return true; +} + +void CopresenceSocketBluetooth::Receive(const ReceiveCallback& callback) { + receive_callback_ = callback; + socket_->Receive(kMaxReceiveBytes, + base::Bind(&CopresenceSocketBluetooth::OnReceive, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CopresenceSocketBluetooth::OnReceiveError, + weak_ptr_factory_.GetWeakPtr())); +} + +void CopresenceSocketBluetooth::OnSendComplete(int status) { +} + +void CopresenceSocketBluetooth::OnSendError(const std::string& message) { + LOG(ERROR) << "Bluetooth send error: " << message; +} + +void CopresenceSocketBluetooth::OnReceive( + int size, + scoped_refptr<net::IOBuffer> io_buffer) { + // Dispatch the data to the callback and go back to listening for more data. + receive_callback_.Run(io_buffer, size); + socket_->Receive(kMaxReceiveBytes, + base::Bind(&CopresenceSocketBluetooth::OnReceive, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CopresenceSocketBluetooth::OnReceiveError, + weak_ptr_factory_.GetWeakPtr())); +} + +void CopresenceSocketBluetooth::OnReceiveError( + device::BluetoothSocket::ErrorReason /* reason */, + const std::string& message) { + LOG(ERROR) << "Bluetooth receive error: " << message; +} + +} // namespace copresence_sockets diff --git a/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h b/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h new file mode 100644 index 0000000..6544d35 --- /dev/null +++ b/components/copresence_sockets/transports/bluetooth/copresence_socket_bluetooth.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef COMPONENTS_COPRESENCE_SOCKETS_TRANSPORTS_COPRESENCE_SOCKET_BLUETOOTH_H_ +#define COMPONENTS_COPRESENCE_SOCKETS_TRANSPORTS_COPRESENCE_SOCKET_BLUETOOTH_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "components/copresence_sockets/public/copresence_socket.h" +#include "device/bluetooth/bluetooth_socket.h" + +namespace net { +class IOBuffer; +} + +namespace copresence_sockets { + +// A CopresenceSocketBluetooth is the Bluetooth implementation of a +// CopresenceSocket. This is currently a thin wrapper around BluetoothSocket. +class CopresenceSocketBluetooth : public CopresenceSocket { + public: + explicit CopresenceSocketBluetooth( + const scoped_refptr<device::BluetoothSocket>& socket); + virtual ~CopresenceSocketBluetooth(); + + // CopresenceSocket overrides: + virtual bool Send(const scoped_refptr<net::IOBuffer>& buffer, + int buffer_size) override; + virtual void Receive(const ReceiveCallback& callback) override; + + private: + void OnSendComplete(int status); + void OnSendError(const std::string& message); + + void OnReceive(int size, scoped_refptr<net::IOBuffer> io_buffer); + void OnReceiveError(device::BluetoothSocket::ErrorReason reason, + const std::string& message); + + scoped_refptr<device::BluetoothSocket> socket_; + ReceiveCallback receive_callback_; + + base::WeakPtrFactory<CopresenceSocketBluetooth> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CopresenceSocketBluetooth); +}; + +} // namespace copresence_sockets + +#endif // COMPONENTS_COPRESENCE_SOCKETS_TRANSPORTS_COPRESENCE_SOCKET_BLUETOOTH_H_ diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index a274a52..9ea3e4f 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -11,6 +11,7 @@ source_set("browser") { ] deps = [ + "//components/copresence_sockets", "//components/keyed_service/content", "//components/keyed_service/core", "//components/pref_registry", @@ -97,6 +98,8 @@ source_set("browser") { "api/cast_channel/logger.h", "api/cast_channel/logger_util.cc", "api/cast_channel/logger_util.h", + "api/copresence_socket/copresence_socket_api.cc", + "api/copresence_socket/copresence_socket_api.h", "api/declarative/declarative_api.cc", "api/declarative/declarative_api.h", "api/declarative/declarative_rule.h", diff --git a/extensions/browser/api/copresence_socket/DEPS b/extensions/browser/api/copresence_socket/DEPS new file mode 100644 index 0000000..bf5b843 --- /dev/null +++ b/extensions/browser/api/copresence_socket/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/copresence_sockets", +] diff --git a/extensions/browser/api/copresence_socket/OWNERS b/extensions/browser/api/copresence_socket/OWNERS new file mode 100644 index 0000000..6a2cb03 --- /dev/null +++ b/extensions/browser/api/copresence_socket/OWNERS @@ -0,0 +1 @@ +rkc@chromium.org diff --git a/extensions/browser/api/copresence_socket/copresence_socket_api.cc b/extensions/browser/api/copresence_socket/copresence_socket_api.cc new file mode 100644 index 0000000..0704297 --- /dev/null +++ b/extensions/browser/api/copresence_socket/copresence_socket_api.cc @@ -0,0 +1,222 @@ +// 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 "extensions/browser/api/copresence_socket/copresence_socket_api.h" + +#include "base/lazy_instance.h" +#include "components/copresence_sockets/public/copresence_peer.h" +#include "components/copresence_sockets/public/copresence_socket.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/event_router.h" +#include "extensions/common/api/copresence_socket.h" +#include "net/base/io_buffer.h" + +using copresence_sockets::CopresencePeer; +using copresence_sockets::CopresenceSocket; + +namespace extensions { + +class CopresencePeerResource : public ApiResource { + public: + // Takes ownership of peer. + CopresencePeerResource(const std::string& owner_extension_id, + scoped_ptr<copresence_sockets::CopresencePeer> peer) + : ApiResource(owner_extension_id), peer_(peer.Pass()) {} + + virtual ~CopresencePeerResource() {} + + copresence_sockets::CopresencePeer* peer() { return peer_.get(); } + + static const content::BrowserThread::ID kThreadId = + content::BrowserThread::UI; + + private: + scoped_ptr<copresence_sockets::CopresencePeer> peer_; + + DISALLOW_COPY_AND_ASSIGN(CopresencePeerResource); +}; + +class CopresenceSocketResource : public ApiResource { + public: + // Takes ownership of socket. + CopresenceSocketResource( + const std::string& owner_extension_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket) + : ApiResource(owner_extension_id), socket_(socket.Pass()) {} + + virtual ~CopresenceSocketResource() {} + + copresence_sockets::CopresenceSocket* socket() { return socket_.get(); } + + static const content::BrowserThread::ID kThreadId = + content::BrowserThread::UI; + + private: + scoped_ptr<copresence_sockets::CopresenceSocket> socket_; + + DISALLOW_COPY_AND_ASSIGN(CopresenceSocketResource); +}; + +CopresenceSocketFunction::CopresenceSocketFunction() + : peers_manager_(nullptr), sockets_manager_(nullptr) { +} + +CopresenceSocketFunction::~CopresenceSocketFunction() { + delete peers_manager_; + delete sockets_manager_; +} + +void CopresenceSocketFunction::Initialize() { + peers_manager_ = + new ApiResourceManager<CopresencePeerResource>(browser_context()); + sockets_manager_ = + new ApiResourceManager<CopresenceSocketResource>(browser_context()); +} + +int CopresenceSocketFunction::AddPeer(CopresencePeerResource* peer) { + return peers_manager_->Add(peer); +} + +int CopresenceSocketFunction::AddSocket(CopresenceSocketResource* socket) { + return sockets_manager_->Add(socket); +} + +void CopresenceSocketFunction::ReplacePeer(const std::string& extension_id, + int peer_id, + CopresencePeerResource* peer) { + peers_manager_->Replace(extension_id, peer_id, peer); +} + +CopresencePeerResource* CopresenceSocketFunction::GetPeer(int peer_id) { + return peers_manager_->Get(extension_id(), peer_id); +} + +CopresenceSocketResource* CopresenceSocketFunction::GetSocket(int socket_id) { + return sockets_manager_->Get(extension_id(), socket_id); +} + +void CopresenceSocketFunction::RemovePeer(int peer_id) { + peers_manager_->Remove(extension_id(), peer_id); +} + +void CopresenceSocketFunction::RemoveSocket(int socket_id) { + sockets_manager_->Remove(extension_id(), socket_id); +} + +void CopresenceSocketFunction::DispatchOnReceiveEvent( + int socket_id, + const scoped_refptr<net::IOBuffer>& buffer, + int size) { + core_api::copresence_socket::ReceiveInfo info; + info.socket_id = socket_id; + info.data = std::string(buffer->data(), size); + // Send the data to the client app. + scoped_ptr<Event> event( + new Event(core_api::copresence_socket::OnReceive::kEventName, + core_api::copresence_socket::OnReceive::Create(info), + browser_context())); + EventRouter::Get(browser_context()) + ->DispatchEventToExtension(extension_id(), event.Pass()); + VLOG(2) << "Dispatched OnReceive event: socketId = " << socket_id + << " and data = " << info.data; +} + +void CopresenceSocketFunction::DispatchOnConnectedEvent( + int peer_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket) { + int socket_id = + AddSocket(new CopresenceSocketResource(extension_id(), socket.Pass())); + + // Send the messages to the client app. + scoped_ptr<Event> event(new Event( + core_api::copresence_socket::OnConnected::kEventName, + core_api::copresence_socket::OnConnected::Create(peer_id, socket_id), + browser_context())); + EventRouter::Get(browser_context()) + ->DispatchEventToExtension(extension_id(), event.Pass()); + VLOG(2) << "Dispatched OnConnected event: peerId = " << peer_id + << " and socketId = " << socket_id; + + socket->Receive(base::Bind(base::Bind( + &CopresenceSocketFunction::DispatchOnReceiveEvent, this, peer_id))); +} + +ExtensionFunction::ResponseAction CopresenceSocketFunction::Run() { + Initialize(); + return Execute(); +} + +// CopresenceSocketCreatePeerFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketCreatePeerFunction::Execute() { + // Add an empty peer to create a placeholder peer_id. We will need to bind + // this id to the OnConnected event dispatcher, so we need it before we + // create the actual peer. Once we have the peer created, we'll replace the + // placeholder with the actual peer object. + int peer_id = AddPeer(new CopresencePeerResource(extension_id(), nullptr)); + + scoped_ptr<CopresencePeer> peer = make_scoped_ptr(new CopresencePeer( + base::Bind(&CopresenceSocketCreatePeerFunction::OnCreated, this, peer_id), + base::Bind( + &CopresenceSocketFunction::DispatchOnConnectedEvent, this, peer_id))); + + ReplacePeer(extension_id(), + peer_id, + new CopresencePeerResource(extension_id(), peer.Pass())); + + return RespondLater(); +} + +void CopresenceSocketCreatePeerFunction::OnCreated(int peer_id, + const std::string& locator) { + core_api::copresence_socket::PeerInfo peer_info; + peer_info.peer_id = peer_id; + peer_info.locator = locator; + Respond(ArgumentList( + core_api::copresence_socket::CreatePeer::Results::Create(peer_info))); +} + +// CopresenceSocketDestroyPeerFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketDestroyPeerFunction::Execute() { + scoped_ptr<core_api::copresence_socket::DestroyPeer::Params> params( + core_api::copresence_socket::DestroyPeer::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + RemovePeer(params->peer_id); + return RespondNow(NoArguments()); +} + +// CopresenceSocketSendFunction implementation: +ExtensionFunction::ResponseAction CopresenceSocketSendFunction::Execute() { + scoped_ptr<core_api::copresence_socket::Send::Params> params( + core_api::copresence_socket::Send::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + CopresenceSocketResource* socket = GetSocket(params->socket_id); + if (!socket) { + VLOG(1) << "Socket not found. ID = " << params->socket_id; + return RespondNow( + ArgumentList(core_api::copresence_socket::Send::Results::Create( + core_api::copresence_socket::SOCKET_STATUS_INVALID_SOCKET))); + } + + socket->socket()->Send(new net::StringIOBuffer(params->data), + params->data.size()); + return RespondNow( + ArgumentList(core_api::copresence_socket::Send::Results::Create( + core_api::copresence_socket::SOCKET_STATUS_NO_ERROR))); +} + +// CopresenceSocketDisconnectFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketDisconnectFunction::Execute() { + scoped_ptr<core_api::copresence_socket::Disconnect::Params> params( + core_api::copresence_socket::Disconnect::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + return RespondLater(); +} + +} // namespace extensions diff --git a/extensions/browser/api/copresence_socket/copresence_socket_api.h b/extensions/browser/api/copresence_socket/copresence_socket_api.h new file mode 100644 index 0000000..97f6782 --- /dev/null +++ b/extensions/browser/api/copresence_socket/copresence_socket_api.h @@ -0,0 +1,120 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/api/api_resource.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/extension_function.h" +#include "extensions/browser/extension_function_histogram_value.h" + +namespace copresence_sockets { +class CopresenceSocket; +} + +namespace net { +class IOBuffer; +} + +namespace extensions { + +class CopresencePeerResource; +class CopresenceSocketResource; + +class CopresenceSocketFunction : public UIThreadExtensionFunction { + public: + CopresenceSocketFunction(); + + void DispatchOnConnectedEvent( + int peer_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket); + void DispatchOnReceiveEvent(int socket_id, + const scoped_refptr<net::IOBuffer>& buffer, + int size); + + protected: + // ExtensionFunction overrides: + virtual ExtensionFunction::ResponseAction Run() override; + + // Override this and do actual work here. + virtual ExtensionFunction::ResponseAction Execute() = 0; + + // Takes ownership of peer. + int AddPeer(CopresencePeerResource* peer); + // Takes ownership of socket. + int AddSocket(CopresenceSocketResource* socket); + + // Takes ownership of peer. + void ReplacePeer(const std::string& extension_id, + int peer_id, + CopresencePeerResource* peer); + + CopresencePeerResource* GetPeer(int peer_id); + CopresenceSocketResource* GetSocket(int socket_id); + + void RemovePeer(int peer_id); + void RemoveSocket(int socket_id); + + virtual ~CopresenceSocketFunction(); + + private: + void Initialize(); + + ApiResourceManager<CopresencePeerResource>* peers_manager_; + ApiResourceManager<CopresenceSocketResource>* sockets_manager_; +}; + +class CopresenceSocketCreatePeerFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.createPeer", + COPRESENCESOCKET_CREATEPEER); + + protected: + virtual ~CopresenceSocketCreatePeerFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; + + private: + void OnCreated(int peer_id, const std::string& locator); +}; + +class CopresenceSocketDestroyPeerFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.destroyPeer", + COPRESENCESOCKET_DESTROYPEER); + + protected: + virtual ~CopresenceSocketDestroyPeerFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +class CopresenceSocketSendFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.send", COPRESENCESOCKET_SEND); + + protected: + virtual ~CopresenceSocketSendFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +class CopresenceSocketDisconnectFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.disconnect", + COPRESENCESOCKET_DISCONNECT); + + protected: + virtual ~CopresenceSocketDisconnectFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index a524997..e66feda 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -959,6 +959,10 @@ enum HistogramValue { HOTWORDPRIVATE_SETHOTWORDALWAYSONSEARCHENABLED, WEBVIEWINTERNAL_LOADDATAWITHBASEURL, GUESTVIEWINTERNAL_DESTROYGUEST, + COPRESENCESOCKET_CREATEPEER, + COPRESENCESOCKET_DESTROYPEER, + COPRESENCESOCKET_SEND, + COPRESENCESOCKET_DISCONNECT, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index 343000d..537608a 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -89,6 +89,11 @@ "dependencies": ["permission:declarativeWebRequest"], "contexts": ["blessed_extension"] }, + "copresenceSocket": { + "channel" : "dev", + "dependencies": ["permission:copresence"], + "contexts": ["blessed_extension"] + }, "dns": { "dependencies": ["permission:dns"], "contexts": ["blessed_extension"] diff --git a/extensions/common/api/copresence_socket.idl b/extensions/common/api/copresence_socket.idl new file mode 100644 index 0000000..c26d854 --- /dev/null +++ b/extensions/common/api/copresence_socket.idl @@ -0,0 +1,135 @@ +// 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. + +// Use the <code>chrome.copresenceSocket</code> API to create persistent +// sockets to send data to and receive from data nearby devices. +namespace copresenceSocket { + + enum TransportType { + // Only use the Internet as the transport. + online, + // Only use an offline transport. + offline + }; + + // The properties for the peer created by the $ref:createPeer function. + [noinline_doc] dictionary ConnectionProperties { + // Flag indicating whether the socket should use a low latency transport + // (if available). + boolean? lowLatency; + + // Flag to force the socket to use a particular type of transport. + TransportType? type; + }; + + + // Result of the <code>createPeer</code> call. + [noinline_doc] dictionary PeerInfo { + // The ID of the newly created peer. + long peerId; + + // An opaque string containing the locator data for this peer. This + // locator is needed to connect to this socket. + DOMString locator; + }; + + // Data from an <code>onReceive</code> event. + [noinline_doc] dictionary ReceiveInfo { + // The socket identifier. + long socketId; + + // The data received. + ArrayBuffer data; + }; + + // Status of a socket operation. + enum SocketStatus { + // There was no error in the previous operation. + no_error, + + // The socket was disconnected. + disconnected, + + // The socket id provided is invalid. + invalid_socket, + + // There was a failure during connection. + connect_failure, + + // There was an error while trying to send data. + send_failure, + + // There was an error while trying to receive data. + receive_failure + }; + + // Callback from the <code>createPeer</code> method. + // |peerInfo| : The result of the socket creation. + callback CreateCallback = void (PeerInfo peerInfo); + + // Callback from the <code>connectToPeer</code> method. + // |socketId| : ID of the socket created between the local and remote peers. + // This ID is only valid if status == no_error. + // |status| : Status of the connect operation. + callback ConnectCallback = void (long socketId, SocketStatus status); + + // Callback from the <code>send</code> method. + // |status| : Status of the send operation. + callback SendCallback = void (SocketStatus status); + + // Callback from the <code>disconnect</code> method. + callback DisconnectCallback = void (); + + // These functions all report failures via chrome.runtime.lastError. + interface Functions { + // Peer functions. + + // Creates a peer that can be connected to by a nearby devices. + // |callback| : Called when the peer has been created. + static void createPeer(CreateCallback callback); + + // Destroys the peer. This will close any connections to this peer + // from remote hosts and will prevent any further connections to it. + // |peerId|: Peer ID returned by <code>createPeer</code>. + static void destroyPeer(long peerId); + + // Socket functions. + + // Sends data on the given Copresence socket. + // |socketId| : The socket identifier. + // |data| : The data to send. + // |callback| : Called when the <code>send</code> operation completes. + static void send(long socketId, ArrayBuffer data, + optional SendCallback callback); + + // Disconnects and destroys a socket. The socket id is no longer valid and any + // pending send callbacks are cancelled as soon at the function is called. + // However, the connection is guaranteed to be closed only when the callback + // is invoked. + // |socketId| : The socket identifier. + // |callback| : Called when the <code>disconnect</code> operation completes. + static void disconnect(long socketId, + optional DisconnectCallback callback); + }; + + interface Events { + // Event raised when data has been received for a given socket. + // |info| : The event data. + static void onReceive(ReceiveInfo info); + + // Event raised when a peer receives a new connection. A new socket is + // created and the id is passed to this event via socketId. + // |peerId| : ID of the peer that received this connection. + // |socketId| : ID of the new socket that was created which can be used to + // communicate on this connection. + static void onConnected(long peerId, long socketId); + + // Event raised when there is a status update for a socket. This can be an + // error or disconnection. After event is raised, since there has either + // been an error or disconnection, no more <code>onReceive</code> events + // are raised for this socket and the socketId is invalidated. + // |status| : The status update for the socket. + static void onSocketStatusUpdated(long socketId, SocketStatus status); + }; +}; diff --git a/extensions/common/api/schemas.gypi b/extensions/common/api/schemas.gypi index a7cc9e9..3ee747f 100644 --- a/extensions/common/api/schemas.gypi +++ b/extensions/common/api/schemas.gypi @@ -18,6 +18,7 @@ 'bluetooth_private.json', 'bluetooth_socket.idl', 'cast_channel.idl', + 'copresence_socket.idl', 'dns.idl', 'events.json', 'extensions_manifest_types.json', diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index e657635..0a80101 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -287,6 +287,7 @@ 'dependencies': [ '../base/base.gyp:base', '../base/base.gyp:base_prefs', + '../components/components.gyp:copresence_sockets', '../components/components.gyp:keyed_service_content', '../components/components.gyp:keyed_service_core', '../components/components.gyp:pref_registry', @@ -382,6 +383,8 @@ 'browser/api/cast_channel/logger.h', 'browser/api/cast_channel/logger_util.cc', 'browser/api/cast_channel/logger_util.h', + 'browser/api/copresence_socket/copresence_socket_api.cc', + 'browser/api/copresence_socket/copresence_socket_api.h', 'browser/api/declarative/deduping_factory.h', 'browser/api/declarative/declarative_api.cc', 'browser/api/declarative/declarative_api.h', diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 1cd1f76..b40c638 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -42543,6 +42543,10 @@ Therefore, the affected-histogram name has to have at least one dot in it. <int value="898" label="HOTWORDPRIVATE_SETHOTWORDALWAYSONSEARCHENABLED"/> <int value="899" label="WEBVIEWINTERNAL_LOADDATAWITHBASEURL"/> <int value="900" label="GUESTVIEWINTERNAL_DESTROYGUEST"/> + <int value="901" label="COPRESENCESOCKET_CREATEPEER"/> + <int value="902" label="COPRESENCESOCKET_DESTROYPEER"/> + <int value="903" label="COPRESENCESOCKET_SEND"/> + <int value="904" label="COPRESENCESOCKET_DISCONNECT"/> </enum> <enum name="ExtensionInstallCause" type="int"> |