| Index: chrome/browser/devtools/tethering_adb_filter.cc
|
| diff --git a/chrome/browser/devtools/tethering_adb_filter.cc b/chrome/browser/devtools/tethering_adb_filter.cc
|
| deleted file mode 100644
|
| index 9a06bfe6a00a5213b8069dad7e9885253788f826..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/devtools/tethering_adb_filter.cc
|
| +++ /dev/null
|
| @@ -1,594 +0,0 @@
|
| -// Copyright (c) 2013 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/devtools/tethering_adb_filter.h"
|
| -
|
| -#include <algorithm>
|
| -#include <map>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/prefs/pref_service.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "chrome/browser/devtools/adb_client_socket.h"
|
| -#include "chrome/browser/devtools/adb_web_socket.h"
|
| -#include "chrome/browser/devtools/devtools_protocol.h"
|
| -#include "chrome/common/pref_names.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "net/base/address_list.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/net_util.h"
|
| -#include "net/dns/host_resolver.h"
|
| -#include "net/socket/tcp_client_socket.h"
|
| -
|
| -using content::BrowserThread;
|
| -
|
| -namespace {
|
| -
|
| -const int kBufferSize = 16 * 1024;
|
| -
|
| -enum {
|
| - kStatusError = -3,
|
| - kStatusDisconnecting = -2,
|
| - kStatusConnecting = -1,
|
| - kStatusOK = 0,
|
| - // Positive values are used to count open connections.
|
| -};
|
| -
|
| -static const char kPortAttribute[] = "port";
|
| -static const char kConnectionIdAttribute[] = "connectionId";
|
| -static const char kTetheringAccepted[] = "Tethering.accepted";
|
| -static const char kTetheringBind[] = "Tethering.bind";
|
| -static const char kTetheringUnbind[] = "Tethering.unbind";
|
| -
|
| -static const char kChromeProductName[] = "Chrome";
|
| -static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
|
| -const int kMinVersionPortForwarding = 28;
|
| -
|
| -class SocketTunnel {
|
| - public:
|
| - typedef base::Callback<void(int)> CounterCallback;
|
| -
|
| - SocketTunnel(const std::string& location, const CounterCallback& callback)
|
| - : location_(location),
|
| - pending_writes_(0),
|
| - pending_destruction_(false),
|
| - callback_(callback),
|
| - about_to_destroy_(false) {
|
| - callback_.Run(1);
|
| - }
|
| -
|
| - void Start(int result, net::StreamSocket* socket) {
|
| - if (result < 0) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| - remote_socket_.reset(socket);
|
| -
|
| - std::vector<std::string> tokens;
|
| - Tokenize(location_, ":", &tokens);
|
| - int port = 0;
|
| - if (tokens.size() != 2 || !base::StringToInt(tokens[1], &port)) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| -
|
| - host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
|
| - net::HostResolver::RequestInfo request_info(
|
| - net::HostPortPair(tokens[0], port));
|
| - result = host_resolver_->Resolve(
|
| - request_info,
|
| - net::DEFAULT_PRIORITY,
|
| - &address_list_,
|
| - base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
|
| - NULL,
|
| - net::BoundNetLog());
|
| - if (result != net::ERR_IO_PENDING)
|
| - OnResolved(result);
|
| - }
|
| -
|
| - private:
|
| - void OnResolved(int result) {
|
| - if (result < 0) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| -
|
| - host_socket_.reset(new net::TCPClientSocket(address_list_, NULL,
|
| - net::NetLog::Source()));
|
| - result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
|
| - base::Unretained(this)));
|
| - if (result != net::ERR_IO_PENDING)
|
| - OnConnected(result);
|
| - }
|
| -
|
| - ~SocketTunnel() {
|
| - about_to_destroy_ = true;
|
| - if (host_socket_)
|
| - host_socket_->Disconnect();
|
| - if (remote_socket_)
|
| - remote_socket_->Disconnect();
|
| - callback_.Run(-1);
|
| - }
|
| -
|
| - void OnConnected(int result) {
|
| - if (result < 0) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| -
|
| - Pump(host_socket_.get(), remote_socket_.get());
|
| - Pump(remote_socket_.get(), host_socket_.get());
|
| - }
|
| -
|
| - void Pump(net::StreamSocket* from, net::StreamSocket* to) {
|
| - scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
|
| - int result = from->Read(
|
| - buffer.get(),
|
| - kBufferSize,
|
| - base::Bind(
|
| - &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
|
| - if (result != net::ERR_IO_PENDING)
|
| - OnRead(from, to, buffer, result);
|
| - }
|
| -
|
| - void OnRead(net::StreamSocket* from,
|
| - net::StreamSocket* to,
|
| - scoped_refptr<net::IOBuffer> buffer,
|
| - int result) {
|
| - if (result <= 0) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| -
|
| - int total = result;
|
| - scoped_refptr<net::DrainableIOBuffer> drainable =
|
| - new net::DrainableIOBuffer(buffer.get(), total);
|
| -
|
| - ++pending_writes_;
|
| - result = to->Write(drainable.get(),
|
| - total,
|
| - base::Bind(&SocketTunnel::OnWritten,
|
| - base::Unretained(this),
|
| - drainable,
|
| - from,
|
| - to));
|
| - if (result != net::ERR_IO_PENDING)
|
| - OnWritten(drainable, from, to, result);
|
| - }
|
| -
|
| - void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
|
| - net::StreamSocket* from,
|
| - net::StreamSocket* to,
|
| - int result) {
|
| - --pending_writes_;
|
| - if (result < 0) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| -
|
| - drainable->DidConsume(result);
|
| - if (drainable->BytesRemaining() > 0) {
|
| - ++pending_writes_;
|
| - result = to->Write(drainable.get(),
|
| - drainable->BytesRemaining(),
|
| - base::Bind(&SocketTunnel::OnWritten,
|
| - base::Unretained(this),
|
| - drainable,
|
| - from,
|
| - to));
|
| - if (result != net::ERR_IO_PENDING)
|
| - OnWritten(drainable, from, to, result);
|
| - return;
|
| - }
|
| -
|
| - if (pending_destruction_) {
|
| - SelfDestruct();
|
| - return;
|
| - }
|
| - Pump(from, to);
|
| - }
|
| -
|
| - void SelfDestruct() {
|
| - // In case one of the connections closes, we could get here
|
| - // from another one due to Disconnect firing back on all
|
| - // read callbacks.
|
| - if (about_to_destroy_)
|
| - return;
|
| - if (pending_writes_ > 0) {
|
| - pending_destruction_ = true;
|
| - return;
|
| - }
|
| - delete this;
|
| - }
|
| -
|
| - std::string location_;
|
| - scoped_ptr<net::StreamSocket> remote_socket_;
|
| - scoped_ptr<net::StreamSocket> host_socket_;
|
| - scoped_ptr<net::HostResolver> host_resolver_;
|
| - net::AddressList address_list_;
|
| - int pending_writes_;
|
| - bool pending_destruction_;
|
| - CounterCallback callback_;
|
| - bool about_to_destroy_;
|
| -};
|
| -
|
| -typedef std::vector<int> ParsedVersion;
|
| -
|
| -static ParsedVersion ParseVersion(const std::string& version) {
|
| - ParsedVersion result;
|
| - std::vector<std::string> parts;
|
| - Tokenize(version, ".", &parts);
|
| - for (size_t i = 0; i != parts.size(); ++i) {
|
| - int value = 0;
|
| - base::StringToInt(parts[i], &value);
|
| - result.push_back(value);
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -static bool IsVersionLower(const ParsedVersion& left,
|
| - const ParsedVersion& right) {
|
| - return std::lexicographical_compare(
|
| - left.begin(), left.end(), right.begin(), right.end());
|
| -}
|
| -
|
| -static bool IsPortForwardingSupported(const ParsedVersion& version) {
|
| - return !version.empty() && version[0] >= kMinVersionPortForwarding;
|
| -}
|
| -
|
| -static std::string FindBestSocketForTethering(
|
| - const DevToolsAdbBridge::RemoteBrowsers browsers) {
|
| - std::string socket;
|
| - ParsedVersion newest_version;
|
| - for (DevToolsAdbBridge::RemoteBrowsers::const_iterator it = browsers.begin();
|
| - it != browsers.end(); ++it) {
|
| - scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser = *it;
|
| - ParsedVersion current_version = ParseVersion(browser->version());
|
| - if (browser->product() == kChromeProductName &&
|
| - IsPortForwardingSupported(current_version) &&
|
| - IsVersionLower(newest_version, current_version)) {
|
| - socket = browser->socket();
|
| - newest_version = current_version;
|
| - }
|
| - }
|
| - return socket;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -TetheringAdbFilter::TetheringAdbFilter(
|
| - scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
|
| - base::MessageLoop* adb_message_loop,
|
| - PrefService* pref_service,
|
| - scoped_refptr<AdbWebSocket> web_socket)
|
| - : device_(device),
|
| - adb_message_loop_(adb_message_loop),
|
| - web_socket_(web_socket),
|
| - command_id_(0) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - pref_change_registrar_.Init(pref_service);
|
| -
|
| - OnPrefsChange();
|
| -
|
| - base::Closure pref_callback = base::Bind(
|
| - &TetheringAdbFilter::OnPrefsChange, base::Unretained(this));
|
| - pref_change_registrar_.Add(
|
| - prefs::kDevToolsPortForwardingEnabled, pref_callback);
|
| - pref_change_registrar_.Add(
|
| - prefs::kDevToolsPortForwardingConfig, pref_callback);
|
| -}
|
| -
|
| -TetheringAdbFilter::~TetheringAdbFilter() {
|
| -}
|
| -
|
| -void TetheringAdbFilter::OnPrefsChange() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - ForwardingMap new_forwarding_map;
|
| -
|
| - PrefService* pref_service = pref_change_registrar_.prefs();
|
| - bool enabled =
|
| - pref_service->GetBoolean(prefs::kDevToolsPortForwardingEnabled);
|
| - if (enabled) {
|
| - const DictionaryValue* dict =
|
| - pref_service->GetDictionary(prefs::kDevToolsPortForwardingConfig);
|
| - for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
|
| - int port_num;
|
| - std::string location;
|
| - if (base::StringToInt(it.key(), &port_num) &&
|
| - dict->GetString(it.key(), &location))
|
| - new_forwarding_map[port_num] = location;
|
| - }
|
| - }
|
| -
|
| - adb_message_loop_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&TetheringAdbFilter::ChangeForwardingMap,
|
| - this, new_forwarding_map));
|
| -}
|
| -
|
| -void TetheringAdbFilter::ChangeForwardingMap(ForwardingMap new_forwarding_map) {
|
| - DCHECK_EQ(base::MessageLoop::current(), adb_message_loop_);
|
| -
|
| - SerializeChanges(kTetheringUnbind, new_forwarding_map, forwarding_map_);
|
| - SerializeChanges(kTetheringBind, forwarding_map_, new_forwarding_map);
|
| - forwarding_map_ = new_forwarding_map;
|
| -}
|
| -
|
| -void TetheringAdbFilter::SerializeChanges(const std::string& method,
|
| - const ForwardingMap& old_map,
|
| - const ForwardingMap& new_map) {
|
| - for (ForwardingMap::const_iterator new_it(new_map.begin());
|
| - new_it != new_map.end(); ++new_it) {
|
| - int port = new_it->first;
|
| - const std::string& location = new_it->second;
|
| - ForwardingMap::const_iterator old_it = old_map.find(port);
|
| - if (old_it != old_map.end() && old_it->second == location)
|
| - continue; // The port points to the same location in both configs, skip.
|
| -
|
| - SendCommand(method, port);
|
| - }
|
| -}
|
| -
|
| -void TetheringAdbFilter::SendCommand(const std::string& method, int port) {
|
| - base::DictionaryValue params;
|
| - params.SetInteger(kPortAttribute, port);
|
| - DevToolsProtocol::Command command(++command_id_, method, ¶ms);
|
| -
|
| - if (method == kTetheringBind) {
|
| - pending_responses_[command.id()] =
|
| - base::Bind(&TetheringAdbFilter::ProcessBindResponse,
|
| - base::Unretained(this), port);
|
| -#if defined(DEBUG_DEVTOOLS)
|
| - port_status_[port] = kStatusConnecting;
|
| - UpdatePortStatusMap();
|
| -#endif // defined(DEBUG_DEVTOOLS)
|
| - } else {
|
| - DCHECK_EQ(kTetheringUnbind, method);
|
| -
|
| - PortStatusMap::iterator it = port_status_.find(port);
|
| - if (it != port_status_.end() && it->second == kStatusError) {
|
| - // The bind command failed on this port, do not attempt unbind.
|
| - port_status_.erase(it);
|
| - UpdatePortStatusMap();
|
| - return;
|
| - }
|
| -
|
| - pending_responses_[command.id()] =
|
| - base::Bind(&TetheringAdbFilter::ProcessUnbindResponse,
|
| - base::Unretained(this), port);
|
| -#if defined(DEBUG_DEVTOOLS)
|
| - port_status_[port] = kStatusDisconnecting;
|
| - UpdatePortStatusMap();
|
| -#endif // defined(DEBUG_DEVTOOLS)
|
| - }
|
| -
|
| - web_socket_->SendFrameOnHandlerThread(command.Serialize());
|
| -}
|
| -
|
| -bool TetheringAdbFilter::ProcessResponse(const std::string& message) {
|
| - scoped_ptr<DevToolsProtocol::Response> response(
|
| - DevToolsProtocol::ParseResponse(message));
|
| - if (!response)
|
| - return false;
|
| -
|
| - CommandCallbackMap::iterator it = pending_responses_.find(response->id());
|
| - if (it == pending_responses_.end())
|
| - return false;
|
| -
|
| - it->second.Run(response->error_code() ? kStatusError : kStatusOK);
|
| - pending_responses_.erase(it);
|
| - return true;
|
| -}
|
| -
|
| -void TetheringAdbFilter::ProcessBindResponse(int port, PortStatus status) {
|
| - port_status_[port] = status;
|
| - UpdatePortStatusMap();
|
| -}
|
| -
|
| -void TetheringAdbFilter::ProcessUnbindResponse(int port, PortStatus status) {
|
| - PortStatusMap::iterator it = port_status_.find(port);
|
| - if (it == port_status_.end())
|
| - return;
|
| - if (status == kStatusError)
|
| - it->second = status;
|
| - else
|
| - port_status_.erase(it);
|
| - UpdatePortStatusMap();
|
| -}
|
| -
|
| -void TetheringAdbFilter::UpdateSocketCount(int port, int increment) {
|
| -#if defined(DEBUG_DEVTOOLS)
|
| - PortStatusMap::iterator it = port_status_.find(port);
|
| - if (it == port_status_.end())
|
| - return;
|
| - if (it->second < 0 || (it->second == 0 && increment < 0))
|
| - return;
|
| - it->second += increment;
|
| - UpdatePortStatusMap();
|
| -#endif // defined(DEBUG_DEVTOOLS)
|
| -}
|
| -
|
| -void TetheringAdbFilter::UpdatePortStatusMap() {
|
| - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
| - base::Bind(&TetheringAdbFilter::UpdatePortStatusMapOnUIThread,
|
| - this, port_status_));
|
| -}
|
| -
|
| -void TetheringAdbFilter::UpdatePortStatusMapOnUIThread(
|
| - const PortStatusMap& status_map) {
|
| - port_status_on_ui_thread_ = status_map;
|
| -}
|
| -
|
| -const TetheringAdbFilter::PortStatusMap&
|
| -TetheringAdbFilter::GetPortStatusMap() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - return port_status_on_ui_thread_;
|
| -}
|
| -
|
| -bool TetheringAdbFilter::ProcessIncomingMessage(const std::string& message) {
|
| - DCHECK_EQ(base::MessageLoop::current(), adb_message_loop_);
|
| -
|
| - if (ProcessResponse(message))
|
| - return true;
|
| -
|
| - scoped_ptr<DevToolsProtocol::Notification> notification(
|
| - DevToolsProtocol::ParseNotification(message));
|
| - if (!notification)
|
| - return false;
|
| -
|
| - if (notification->method() != kTetheringAccepted)
|
| - return false;
|
| -
|
| - DictionaryValue* params = notification->params();
|
| - if (!params)
|
| - return false;
|
| -
|
| - int port;
|
| - std::string connection_id;
|
| - if (!params->GetInteger(kPortAttribute, &port) ||
|
| - !params->GetString(kConnectionIdAttribute, &connection_id))
|
| - return false;
|
| -
|
| - std::map<int, std::string>::iterator it = forwarding_map_.find(port);
|
| - if (it == forwarding_map_.end())
|
| - return false;
|
| -
|
| - std::string location = it->second;
|
| -
|
| - SocketTunnel* tunnel = new SocketTunnel(location,
|
| - base::Bind(&TetheringAdbFilter::UpdateSocketCount, this, port));
|
| -
|
| - device_->OpenSocket(connection_id.c_str(),
|
| - base::Bind(&SocketTunnel::Start, base::Unretained(tunnel)));
|
| - return true;
|
| -}
|
| -
|
| -class PortForwardingController::Connection : public AdbWebSocket::Delegate {
|
| - public:
|
| - Connection(
|
| - Registry* registry,
|
| - scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
|
| - const std::string& socket,
|
| - scoped_refptr<DevToolsAdbBridge> bridge,
|
| - PrefService* pref_service);
|
| -
|
| - void Shutdown();
|
| -
|
| - TetheringAdbFilter::PortStatusMap port_status();
|
| -
|
| - private:
|
| - virtual ~Connection();
|
| -
|
| - virtual void OnSocketOpened() OVERRIDE;
|
| - virtual void OnFrameRead(const std::string& message) OVERRIDE;
|
| - virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
|
| - virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
|
| -
|
| - Registry* registry_;
|
| - scoped_refptr<DevToolsAdbBridge::AndroidDevice> device_;
|
| - scoped_refptr<DevToolsAdbBridge> bridge_;
|
| - PrefService* pref_service_;
|
| -
|
| - scoped_refptr<TetheringAdbFilter> tethering_adb_filter_;
|
| - scoped_refptr<AdbWebSocket> web_socket_;
|
| -};
|
| -
|
| -PortForwardingController::Connection::Connection(
|
| - Registry* registry,
|
| - scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
|
| - const std::string& socket,
|
| - scoped_refptr<DevToolsAdbBridge> bridge,
|
| - PrefService* pref_service)
|
| - : registry_(registry),
|
| - device_(device),
|
| - bridge_(bridge),
|
| - pref_service_(pref_service) {
|
| - (*registry_)[device_->serial()] = this;
|
| - web_socket_ = new AdbWebSocket(
|
| - device, socket, kDevToolsRemoteBrowserTarget,
|
| - bridge->GetAdbMessageLoop(), this);
|
| -}
|
| -
|
| -void PortForwardingController::Connection::Shutdown() {
|
| - registry_ = NULL;
|
| - // This will have no effect if the socket is not connected yet.
|
| - web_socket_->Disconnect();
|
| -}
|
| -
|
| -TetheringAdbFilter::PortStatusMap
|
| -PortForwardingController::Connection::port_status() {
|
| - if (tethering_adb_filter_)
|
| - return tethering_adb_filter_->GetPortStatusMap();
|
| - else
|
| - return TetheringAdbFilter::PortStatusMap();
|
| -}
|
| -
|
| -PortForwardingController::Connection::~Connection() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - if (registry_) {
|
| - DCHECK(registry_->find(device_->serial()) != registry_->end());
|
| - registry_->erase(device_->serial());
|
| - }
|
| -}
|
| -
|
| -void PortForwardingController::Connection::OnSocketOpened() {
|
| - if (!registry_) {
|
| - // Socket was created after Shutdown was called. Disconnect immediately.
|
| - web_socket_->Disconnect();
|
| - return;
|
| - }
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - tethering_adb_filter_ = new TetheringAdbFilter(
|
| - device_, bridge_->GetAdbMessageLoop(), pref_service_, web_socket_);
|
| -}
|
| -
|
| -void PortForwardingController::Connection::OnFrameRead(
|
| - const std::string& message) {
|
| -}
|
| -
|
| -void PortForwardingController::Connection::OnSocketClosed(
|
| - bool closed_by_device) {
|
| - delete this;
|
| -}
|
| -
|
| -bool PortForwardingController::Connection::ProcessIncomingMessage(
|
| - const std::string& message) {
|
| - return tethering_adb_filter_->ProcessIncomingMessage(message);
|
| -}
|
| -
|
| -
|
| -PortForwardingController::PortForwardingController(
|
| - scoped_refptr<DevToolsAdbBridge> bridge,
|
| - PrefService* pref_service)
|
| - : bridge_(bridge),
|
| - pref_service_(pref_service) {
|
| -}
|
| -
|
| -PortForwardingController::~PortForwardingController() {
|
| - for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
|
| - it->second->Shutdown();
|
| -}
|
| -
|
| -void PortForwardingController::UpdateDeviceList(
|
| - const DevToolsAdbBridge::RemoteDevices& devices) {
|
| - for (DevToolsAdbBridge::RemoteDevices::const_iterator it = devices.begin();
|
| - it != devices.end(); ++it) {
|
| - Registry::iterator rit = registry_.find((*it)->serial());
|
| - if (rit == registry_.end()) {
|
| - std::string socket = FindBestSocketForTethering((*it)->browsers());
|
| - if (!socket.empty() || (*it)->serial().empty()) {
|
| - // Will delete itself when disconnected.
|
| - new Connection(
|
| - ®istry_, (*it)->device(), socket, bridge_, pref_service_);
|
| - }
|
| - } else {
|
| - (*it)->set_port_status((*rit).second->port_status());
|
| - }
|
| - }
|
| -}
|
|
|