blob: 458bb9dff0865b15c343b2192598f339d90a7f86 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/device/android_device_manager.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/socket/stream_socket.h"
using content::BrowserThread;
namespace {
const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread";
const int kBufferSize = 16 * 1024;
static const char kModelOffline[] = "Offline";
static const char kHttpGetRequest[] = "GET %s HTTP/1.1\r\n\r\n";
static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n";
static void PostDeviceInfoCallback(
scoped_refptr<base::MessageLoopProxy> response_message_loop,
const AndroidDeviceManager::DeviceInfoCallback& callback,
const AndroidDeviceManager::DeviceInfo& device_info) {
response_message_loop->PostTask(FROM_HERE, base::Bind(callback, device_info));
}
static void PostCommandCallback(
scoped_refptr<base::MessageLoopProxy> response_message_loop,
const AndroidDeviceManager::CommandCallback& callback,
int result,
const std::string& response) {
response_message_loop->PostTask(FROM_HERE,
base::Bind(callback, result, response));
}
static void PostSocketCallback(
scoped_refptr<base::MessageLoopProxy> response_message_loop,
const AndroidDeviceManager::SocketCallback& callback,
int result,
scoped_ptr<net::StreamSocket> socket) {
response_message_loop->PostTask(
FROM_HERE, base::Bind(callback, result, base::Passed(&socket)));
}
class HttpRequest {
public:
typedef AndroidDeviceManager::CommandCallback CommandCallback;
typedef AndroidDeviceManager::SocketCallback SocketCallback;
static void CommandRequest(const std::string& request,
const CommandCallback& callback,
int result,
scoped_ptr<net::StreamSocket> socket) {
if (result != net::OK) {
callback.Run(result, std::string());
return;
}
new HttpRequest(socket.Pass(), request, callback);
}
static void SocketRequest(const std::string& request,
const SocketCallback& callback,
int result,
scoped_ptr<net::StreamSocket> socket) {
if (result != net::OK) {
callback.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
return;
}
new HttpRequest(socket.Pass(), request, callback);
}
private:
HttpRequest(scoped_ptr<net::StreamSocket> socket,
const std::string& request,
const CommandCallback& callback)
: socket_(socket.Pass()),
command_callback_(callback),
body_pos_(0) {
SendRequest(request);
}
HttpRequest(scoped_ptr<net::StreamSocket> socket,
const std::string& request,
const SocketCallback& callback)
: socket_(socket.Pass()),
socket_callback_(callback),
body_pos_(0) {
SendRequest(request);
}
~HttpRequest() {
}
void SendRequest(const std::string& request) {
scoped_refptr<net::StringIOBuffer> request_buffer =
new net::StringIOBuffer(request);
int result = socket_->Write(
request_buffer.get(),
request_buffer->size(),
base::Bind(&HttpRequest::ReadResponse, base::Unretained(this)));
if (result != net::ERR_IO_PENDING)
ReadResponse(result);
}
void ReadResponse(int result) {
if (!CheckNetResultOrDie(result))
return;
scoped_refptr<net::IOBuffer> response_buffer =
new net::IOBuffer(kBufferSize);
result = socket_->Read(
response_buffer.get(),
kBufferSize,
base::Bind(&HttpRequest::OnResponseData, base::Unretained(this),
response_buffer,
-1));
if (result != net::ERR_IO_PENDING)
OnResponseData(response_buffer, -1, result);
}
void OnResponseData(scoped_refptr<net::IOBuffer> response_buffer,
int bytes_total,
int result) {
if (!CheckNetResultOrDie(result))
return;
if (result == 0) {
CheckNetResultOrDie(net::ERR_CONNECTION_CLOSED);
return;
}
response_ += std::string(response_buffer->data(), result);
int expected_length = 0;
if (bytes_total < 0) {
// TODO(kaznacheev): Use net::HttpResponseHeader to parse the header.
size_t content_pos = response_.find("Content-Length:");
if (content_pos != std::string::npos) {
size_t endline_pos = response_.find("\n", content_pos);
if (endline_pos != std::string::npos) {
std::string len = response_.substr(content_pos + 15,
endline_pos - content_pos - 15);
base::TrimWhitespace(len, base::TRIM_ALL, &len);
if (!base::StringToInt(len, &expected_length)) {
CheckNetResultOrDie(net::ERR_FAILED);
return;
}
}
}
body_pos_ = response_.find("\r\n\r\n");
if (body_pos_ != std::string::npos) {
body_pos_ += 4;
bytes_total = body_pos_ + expected_length;
}
}
if (bytes_total == static_cast<int>(response_.length())) {
if (!command_callback_.is_null())
command_callback_.Run(net::OK, response_.substr(body_pos_));
else
socket_callback_.Run(net::OK, socket_.Pass());
delete this;
return;
}
result = socket_->Read(
response_buffer.get(),
kBufferSize,
base::Bind(&HttpRequest::OnResponseData,
base::Unretained(this),
response_buffer,
bytes_total));
if (result != net::ERR_IO_PENDING)
OnResponseData(response_buffer, bytes_total, result);
}
bool CheckNetResultOrDie(int result) {
if (result >= 0)
return true;
if (!command_callback_.is_null())
command_callback_.Run(result, std::string());
else
socket_callback_.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
delete this;
return false;
}
scoped_ptr<net::StreamSocket> socket_;
std::string response_;
AndroidDeviceManager::CommandCallback command_callback_;
AndroidDeviceManager::SocketCallback socket_callback_;
size_t body_pos_;
};
class DevicesRequest : public base::RefCountedThreadSafe<DevicesRequest> {
public:
typedef AndroidDeviceManager::DeviceInfo DeviceInfo;
typedef AndroidDeviceManager::DeviceProvider DeviceProvider;
typedef AndroidDeviceManager::DeviceProviders DeviceProviders;
typedef AndroidDeviceManager::DeviceDescriptors DeviceDescriptors;
typedef base::Callback<void(DeviceDescriptors*)>
DescriptorsCallback;
static void Start(scoped_refptr<base::MessageLoopProxy> device_message_loop,
const DeviceProviders& providers,
const DescriptorsCallback& callback) {
// Don't keep counted reference on calling thread;
DevicesRequest* request = new DevicesRequest(callback);
// Avoid destruction while sending requests
request->AddRef();
for (DeviceProviders::const_iterator it = providers.begin();
it != providers.end(); ++it) {
device_message_loop->PostTask(
FROM_HERE,
base::Bind(
&DeviceProvider::QueryDevices,
*it,
base::Bind(&DevicesRequest::ProcessSerials, request, *it)));
}
device_message_loop->ReleaseSoon(FROM_HERE, request);
}
private:
explicit DevicesRequest(const DescriptorsCallback& callback)
: response_message_loop_(base::MessageLoopProxy::current()),
callback_(callback),
descriptors_(new DeviceDescriptors()) {
}
friend class base::RefCountedThreadSafe<DevicesRequest>;
~DevicesRequest() {
response_message_loop_->PostTask(FROM_HERE,
base::Bind(callback_, descriptors_.release()));
}
typedef std::vector<std::string> Serials;
void ProcessSerials(scoped_refptr<DeviceProvider> provider,
const Serials& serials) {
for (Serials::const_iterator it = serials.begin(); it != serials.end();
++it) {
descriptors_->resize(descriptors_->size() + 1);
descriptors_->back().provider = provider;
descriptors_->back().serial = *it;
}
}
scoped_refptr<base::MessageLoopProxy> response_message_loop_;
DescriptorsCallback callback_;
scoped_ptr<DeviceDescriptors> descriptors_;
};
void ReleaseDeviceAndProvider(
AndroidDeviceManager::DeviceProvider* provider,
const std::string& serial) {
provider->ReleaseDevice(serial);
provider->Release();
}
} // namespace
AndroidDeviceManager::BrowserInfo::BrowserInfo()
: type(kTypeOther) {
}
AndroidDeviceManager::DeviceInfo::DeviceInfo()
: model(kModelOffline), connected(false) {
}
AndroidDeviceManager::DeviceInfo::~DeviceInfo() {
}
AndroidDeviceManager::DeviceDescriptor::DeviceDescriptor() {
}
AndroidDeviceManager::DeviceDescriptor::~DeviceDescriptor() {
}
void AndroidDeviceManager::DeviceProvider::SendJsonRequest(
const std::string& serial,
const std::string& socket_name,
const std::string& request,
const CommandCallback& callback) {
OpenSocket(serial,
socket_name,
base::Bind(&HttpRequest::CommandRequest,
base::StringPrintf(kHttpGetRequest, request.c_str()),
callback));
}
void AndroidDeviceManager::DeviceProvider::HttpUpgrade(
const std::string& serial,
const std::string& socket_name,
const std::string& url,
const SocketCallback& callback) {
OpenSocket(
serial,
socket_name,
base::Bind(&HttpRequest::SocketRequest,
base::StringPrintf(kWebSocketUpgradeRequest, url.c_str()),
callback));
}
void AndroidDeviceManager::DeviceProvider::ReleaseDevice(
const std::string& serial) {
}
AndroidDeviceManager::DeviceProvider::DeviceProvider() {
}
AndroidDeviceManager::DeviceProvider::~DeviceProvider() {
}
void AndroidDeviceManager::Device::QueryDeviceInfo(
const DeviceInfoCallback& callback) {
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&DeviceProvider::QueryDeviceInfo,
provider_,
serial_,
base::Bind(&PostDeviceInfoCallback,
base::MessageLoopProxy::current(),
callback)));
}
void AndroidDeviceManager::Device::OpenSocket(const std::string& socket_name,
const SocketCallback& callback) {
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&DeviceProvider::OpenSocket,
provider_,
serial_,
socket_name,
callback));
}
void AndroidDeviceManager::Device::SendJsonRequest(
const std::string& socket_name,
const std::string& request,
const CommandCallback& callback) {
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&DeviceProvider::SendJsonRequest,
provider_,
serial_,
socket_name,
request,
base::Bind(&PostCommandCallback,
base::MessageLoopProxy::current(),
callback)));
}
void AndroidDeviceManager::Device::HttpUpgrade(const std::string& socket_name,
const std::string& url,
const SocketCallback& callback) {
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&DeviceProvider::HttpUpgrade,
provider_,
serial_,
socket_name,
url,
base::Bind(&PostSocketCallback,
base::MessageLoopProxy::current(),
callback)));
}
AndroidDeviceManager::Device::Device(
scoped_refptr<base::MessageLoopProxy> device_message_loop,
scoped_refptr<DeviceProvider> provider,
const std::string& serial)
: message_loop_proxy_(device_message_loop),
provider_(provider),
serial_(serial),
weak_factory_(this) {
}
AndroidDeviceManager::Device::~Device() {
std::set<AndroidWebSocket*> sockets_copy(sockets_);
for (AndroidWebSocket* socket : sockets_copy)
socket->OnSocketClosed();
provider_->AddRef();
DeviceProvider* raw_ptr = provider_.get();
provider_ = NULL;
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&ReleaseDeviceAndProvider,
base::Unretained(raw_ptr),
serial_));
}
AndroidDeviceManager::HandlerThread*
AndroidDeviceManager::HandlerThread::instance_ = NULL;
// static
scoped_refptr<AndroidDeviceManager::HandlerThread>
AndroidDeviceManager::HandlerThread::GetInstance() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!instance_)
new HandlerThread();
return instance_;
}
AndroidDeviceManager::HandlerThread::HandlerThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
instance_ = this;
thread_ = new base::Thread(kDevToolsAdbBridgeThreadName);
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
if (!thread_->StartWithOptions(options)) {
delete thread_;
thread_ = NULL;
}
}
scoped_refptr<base::MessageLoopProxy>
AndroidDeviceManager::HandlerThread::message_loop() {
return thread_ ? thread_->message_loop_proxy() : NULL;
}
// static
void AndroidDeviceManager::HandlerThread::StopThread(
base::Thread* thread) {
thread->Stop();
}
AndroidDeviceManager::HandlerThread::~HandlerThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
instance_ = NULL;
if (!thread_)
return;
// Shut down thread on FILE thread to join into IO.
content::BrowserThread::PostTask(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&HandlerThread::StopThread, thread_));
}
// static
scoped_refptr<AndroidDeviceManager> AndroidDeviceManager::Create() {
return new AndroidDeviceManager();
}
void AndroidDeviceManager::SetDeviceProviders(
const DeviceProviders& providers) {
for (DeviceProviders::iterator it = providers_.begin();
it != providers_.end(); ++it) {
(*it)->AddRef();
DeviceProvider* raw_ptr = it->get();
*it = NULL;
handler_thread_->message_loop()->ReleaseSoon(FROM_HERE, raw_ptr);
}
providers_ = providers;
}
void AndroidDeviceManager::QueryDevices(const DevicesCallback& callback) {
DevicesRequest::Start(handler_thread_->message_loop(),
providers_,
base::Bind(&AndroidDeviceManager::UpdateDevices,
this,
callback));
}
AndroidDeviceManager::AndroidDeviceManager()
: handler_thread_(HandlerThread::GetInstance()) {
}
AndroidDeviceManager::~AndroidDeviceManager() {
SetDeviceProviders(DeviceProviders());
}
void AndroidDeviceManager::UpdateDevices(
const DevicesCallback& callback,
DeviceDescriptors* descriptors_raw) {
scoped_ptr<DeviceDescriptors> descriptors(descriptors_raw);
Devices response;
DeviceWeakMap new_devices;
for (DeviceDescriptors::const_iterator it = descriptors->begin();
it != descriptors->end();
++it) {
DeviceWeakMap::iterator found = devices_.find(it->serial);
scoped_refptr<Device> device;
if (found == devices_.end() || !found->second ||
found->second->provider_.get() != it->provider.get()) {
device = new Device(handler_thread_->message_loop(),
it->provider, it->serial);
} else {
device = found->second.get();
}
response.push_back(device);
new_devices[it->serial] = device->weak_factory_.GetWeakPtr();
}
devices_.swap(new_devices);
callback.Run(response);
}