blob: 8a7cb18a2a301a5e87db8d2c824947937c2580e7 [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 <stddef.h>
#include <string.h>
#include <utility>
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/devtools/device/usb/usb_device_manager_helper.h"
#include "chrome/browser/devtools/device/usb/usb_device_provider.h"
#include "content/public/browser/browser_task_traits.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/socket/stream_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
using content::BrowserThread;
namespace {
const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread";
const int kBufferSize = 16 * 1024;
static const char kModelOffline[] = "Offline";
static const char kRequestLineFormat[] = "GET %s HTTP/1.1";
net::NetworkTrafficAnnotationTag kAndroidDeviceManagerTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("android_device_manager_socket", R"(
semantics {
sender: "Android Device Manager"
description:
"Remote debugging is supported over existing ADB (Android Debug "
"Bridge) connection, in addition to raw USB connection. This "
"socket talks to the local ADB daemon which routes debugging "
"traffic to a remote device."
trigger:
"A user connects to an Android device using remote debugging."
data: "Any data required for remote debugging."
destination: LOCAL
}
policy {
cookies_allowed: NO
setting:
"To use ADB with a device connected over USB, you must enable USB "
"debugging in the device system settings, under Developer options."
policy_exception_justification:
"This is not a network request and is only used for remote "
"debugging."
})");
static void PostDeviceInfoCallback(
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
const AndroidDeviceManager::DeviceInfoCallback& callback,
const AndroidDeviceManager::DeviceInfo& device_info) {
response_task_runner->PostTask(FROM_HERE,
base::BindOnce(callback, device_info));
}
static void PostCommandCallback(
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
const AndroidDeviceManager::CommandCallback& callback,
int result,
const std::string& response) {
response_task_runner->PostTask(FROM_HERE,
base::BindOnce(callback, result, response));
}
static void PostHttpUpgradeCallback(
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
const AndroidDeviceManager::HttpUpgradeCallback& callback,
int result,
const std::string& extensions,
const std::string& body_head,
std::unique_ptr<net::StreamSocket> socket) {
response_task_runner->PostTask(
FROM_HERE, base::BindOnce(callback, result, extensions, body_head,
std::move(socket)));
}
class HttpRequest {
public:
typedef AndroidDeviceManager::CommandCallback CommandCallback;
typedef AndroidDeviceManager::HttpUpgradeCallback HttpUpgradeCallback;
static void CommandRequest(const std::string& path,
const CommandCallback& callback,
int result,
std::unique_ptr<net::StreamSocket> socket) {
if (result != net::OK) {
callback.Run(result, std::string());
return;
}
new HttpRequest(std::move(socket), path, {}, callback);
}
static void HttpUpgradeRequest(const std::string& path,
const std::string& extensions,
const HttpUpgradeCallback& callback,
int result,
std::unique_ptr<net::StreamSocket> socket) {
if (result != net::OK) {
callback.Run(result, std::string(), std::string(),
base::WrapUnique<net::StreamSocket>(nullptr));
return;
}
std::map<std::string, std::string> headers = {
{"Upgrade", "WebSocket"},
{"Connection", "Upgrade"},
{"Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="},
{"Sec-WebSocket-Version", "13"}};
if (!extensions.empty())
headers["Sec-WebSocket-Extensions"] = extensions;
new HttpRequest(std::move(socket), path, headers, callback);
}
private:
HttpRequest(std::unique_ptr<net::StreamSocket> socket,
const std::string& path,
const std::map<std::string, std::string>& headers,
const CommandCallback& callback)
: socket_(std::move(socket)),
command_callback_(callback),
expected_total_size_(0),
header_size_(std::string::npos) {
SendRequest(path, headers);
}
HttpRequest(std::unique_ptr<net::StreamSocket> socket,
const std::string& path,
const std::map<std::string, std::string>& headers,
const HttpUpgradeCallback& callback)
: socket_(std::move(socket)),
http_upgrade_callback_(callback),
expected_total_size_(0),
header_size_(std::string::npos) {
SendRequest(path, headers);
}
~HttpRequest() {
}
void DoSendRequest(int result) {
while (result != net::ERR_IO_PENDING) {
if (!CheckNetResultOrDie(result))
return;
if (result > 0)
request_->DidConsume(result);
if (request_->BytesRemaining() == 0) {
request_ = nullptr;
ReadResponse(net::OK);
return;
}
result = socket_->Write(
request_.get(), request_->BytesRemaining(),
base::Bind(&HttpRequest::DoSendRequest, base::Unretained(this)),
kAndroidDeviceManagerTrafficAnnotation);
}
}
void SendRequest(const std::string& path,
std::map<std::string, std::string> headers) {
net::IPEndPoint remote_address;
socket_->GetPeerAddress(&remote_address);
headers["Host"] = remote_address.ToString();
std::string requestLine =
base::StringPrintf(kRequestLineFormat, path.c_str());
std::string crlf = "\r\n";
std::string colon = ": ";
std::vector<base::StringPiece> pieces = {requestLine, crlf};
for (const auto& header_and_value : headers) {
pieces.insert(pieces.end(), {header_and_value.first, colon,
header_and_value.second, crlf});
}
pieces.insert(pieces.end(), {crlf});
std::string request = base::StrCat(pieces);
scoped_refptr<net::IOBuffer> base_buffer =
base::MakeRefCounted<net::IOBuffer>(request.size());
memcpy(base_buffer->data(), request.data(), request.size());
request_ = base::MakeRefCounted<net::DrainableIOBuffer>(
std::move(base_buffer), request.size());
DoSendRequest(net::OK);
}
void ReadResponse(int result) {
if (!CheckNetResultOrDie(result))
return;
response_buffer_ = base::MakeRefCounted<net::IOBuffer>(kBufferSize);
result = socket_->Read(
response_buffer_.get(),
kBufferSize,
base::Bind(&HttpRequest::OnResponseData, base::Unretained(this)));
if (result != net::ERR_IO_PENDING)
OnResponseData(result);
}
void OnResponseData(int result) {
do {
if (!CheckNetResultOrDie(result))
return;
if (result == 0) {
CheckNetResultOrDie(net::ERR_CONNECTION_CLOSED);
return;
}
response_.append(response_buffer_->data(), result);
if (header_size_ == std::string::npos) {
header_size_ = response_.find("\r\n\r\n");
if (header_size_ != std::string::npos) {
header_size_ += 4;
int expected_body_size = 0;
// TODO(kaznacheev): Use net::HttpResponseHeader to parse the header.
std::string content_length = ExtractHeader("Content-Length:");
if (!content_length.empty()) {
if (!base::StringToInt(content_length, &expected_body_size)) {
CheckNetResultOrDie(net::ERR_FAILED);
return;
}
}
expected_total_size_ = header_size_ + expected_body_size;
}
}
// WebSocket handshake doesn't contain the Content-Length header. For this
// case, |expected_total_size_| is set to the size of the header (opening
// handshake).
//
// Some (part of) WebSocket frames can be already received into
// |response_|.
if (header_size_ != std::string::npos &&
response_.length() >= expected_total_size_) {
const std::string& body = response_.substr(header_size_);
if (!command_callback_.is_null()) {
command_callback_.Run(net::OK, body);
} else {
http_upgrade_callback_.Run(net::OK,
ExtractHeader("Sec-WebSocket-Extensions:"),
body, std::move(socket_));
}
delete this;
return;
}
result = socket_->Read(
response_buffer_.get(), kBufferSize,
base::Bind(&HttpRequest::OnResponseData, base::Unretained(this)));
} while (result != net::ERR_IO_PENDING);
}
std::string ExtractHeader(const std::string& header) {
size_t start_pos = response_.find(header);
if (start_pos == std::string::npos)
return std::string();
size_t endline_pos = response_.find("\n", start_pos);
if (endline_pos == std::string::npos)
return std::string();
std::string value = response_.substr(
start_pos + header.length(), endline_pos - start_pos - header.length());
base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
return value;
}
bool CheckNetResultOrDie(int result) {
if (result >= 0)
return true;
if (!command_callback_.is_null()) {
command_callback_.Run(result, std::string());
} else {
http_upgrade_callback_.Run(result, std::string(), std::string(),
base::WrapUnique<net::StreamSocket>(nullptr));
}
delete this;
return false;
}
std::unique_ptr<net::StreamSocket> socket_;
scoped_refptr<net::DrainableIOBuffer> request_;
std::string response_;
CommandCallback command_callback_;
HttpUpgradeCallback http_upgrade_callback_;
scoped_refptr<net::IOBuffer> response_buffer_;
// Initially set to 0. Once the end of the header is seen:
// - If the Content-Length header is included, set to the sum of the header
// size (including the last two CRLFs) and the value of
// the header.
// - Otherwise, this variable is set to the size of the header (including the
// last two CRLFs).
size_t expected_total_size_;
// Initially set to std::string::npos. Once the end of the header is seen,
// set to the size of the header part in |response_| including the two CRLFs
// at the end.
size_t header_size_;
};
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(std::unique_ptr<DeviceDescriptors>)>
DescriptorsCallback;
static void Start(
scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
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 (auto it = providers.begin(); it != providers.end(); ++it) {
device_task_runner->PostTask(
FROM_HERE, base::BindOnce(&DeviceProvider::QueryDevices, *it,
base::Bind(&DevicesRequest::ProcessSerials,
request, *it)));
}
device_task_runner->ReleaseSoon(FROM_HERE, request);
}
private:
explicit DevicesRequest(const DescriptorsCallback& callback)
: response_task_runner_(base::ThreadTaskRunnerHandle::Get()),
callback_(callback),
descriptors_(new DeviceDescriptors()) {}
friend class base::RefCountedThreadSafe<DevicesRequest>;
~DevicesRequest() {
response_task_runner_->PostTask(
FROM_HERE, base::BindOnce(callback_, std::move(descriptors_)));
}
typedef std::vector<std::string> Serials;
void ProcessSerials(scoped_refptr<DeviceProvider> provider,
const Serials& serials) {
for (auto it = serials.begin(); it != serials.end(); ++it) {
descriptors_->resize(descriptors_->size() + 1);
descriptors_->back().provider = provider;
descriptors_->back().serial = *it;
}
}
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner_;
DescriptorsCallback callback_;
std::unique_ptr<DeviceDescriptors> descriptors_;
};
void OnCountDevices(const base::Callback<void(int)>& callback,
int device_count) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
base::BindOnce(callback, device_count));
}
} // namespace
AndroidDeviceManager::BrowserInfo::BrowserInfo()
: type(kTypeOther) {
}
AndroidDeviceManager::BrowserInfo::BrowserInfo(const BrowserInfo& other) =
default;
AndroidDeviceManager::DeviceInfo::DeviceInfo()
: model(kModelOffline), connected(false) {
}
AndroidDeviceManager::DeviceInfo::DeviceInfo(const DeviceInfo& other) = default;
AndroidDeviceManager::DeviceInfo::~DeviceInfo() {
}
AndroidDeviceManager::DeviceDescriptor::DeviceDescriptor() {
}
AndroidDeviceManager::DeviceDescriptor::DeviceDescriptor(
const DeviceDescriptor& other) = default;
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, request, callback));
}
void AndroidDeviceManager::DeviceProvider::HttpUpgrade(
const std::string& serial,
const std::string& socket_name,
const std::string& path,
const std::string& extensions,
const HttpUpgradeCallback& callback) {
OpenSocket(
serial, socket_name,
base::Bind(&HttpRequest::HttpUpgradeRequest, path, extensions, callback));
}
void AndroidDeviceManager::DeviceProvider::ReleaseDevice(
const std::string& serial) {
}
AndroidDeviceManager::DeviceProvider::DeviceProvider() {
}
AndroidDeviceManager::DeviceProvider::~DeviceProvider() {
}
void AndroidDeviceManager::Device::QueryDeviceInfo(
const DeviceInfoCallback& callback) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&DeviceProvider::QueryDeviceInfo, provider_, serial_,
base::Bind(&PostDeviceInfoCallback,
base::ThreadTaskRunnerHandle::Get(), callback)));
}
void AndroidDeviceManager::Device::OpenSocket(const std::string& socket_name,
const SocketCallback& callback) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DeviceProvider::OpenSocket, provider_, serial_,
socket_name, callback));
}
void AndroidDeviceManager::Device::SendJsonRequest(
const std::string& socket_name,
const std::string& request,
const CommandCallback& callback) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DeviceProvider::SendJsonRequest, provider_,
serial_, socket_name, request,
base::Bind(&PostCommandCallback,
base::ThreadTaskRunnerHandle::Get(),
callback)));
}
void AndroidDeviceManager::Device::HttpUpgrade(
const std::string& socket_name,
const std::string& path,
const std::string& extensions,
const HttpUpgradeCallback& callback) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DeviceProvider::HttpUpgrade, provider_,
serial_, socket_name, path, extensions,
base::Bind(&PostHttpUpgradeCallback,
base::ThreadTaskRunnerHandle::Get(),
callback)));
}
AndroidDeviceManager::Device::Device(
scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
scoped_refptr<DeviceProvider> provider,
const std::string& serial)
: RefCountedDeleteOnSequence<Device>(base::ThreadTaskRunnerHandle::Get()),
task_runner_(device_task_runner),
provider_(provider),
serial_(serial),
weak_factory_(this) {}
AndroidDeviceManager::Device::~Device() {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DeviceProvider::ReleaseDevice,
std::move(provider_), std::move(serial_)));
}
AndroidDeviceManager::HandlerThread*
AndroidDeviceManager::HandlerThread::instance_ = nullptr;
// static
scoped_refptr<AndroidDeviceManager::HandlerThread>
AndroidDeviceManager::HandlerThread::GetInstance() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!instance_)
new HandlerThread();
return instance_;
}
AndroidDeviceManager::HandlerThread::HandlerThread() {
DCHECK_CURRENTLY_ON(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_ = nullptr;
}
}
scoped_refptr<base::SingleThreadTaskRunner>
AndroidDeviceManager::HandlerThread::message_loop() {
return thread_ ? thread_->task_runner() : nullptr;
}
// static
void AndroidDeviceManager::HandlerThread::StopThread(
base::Thread* thread) {
delete thread;
}
AndroidDeviceManager::HandlerThread::~HandlerThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
instance_ = nullptr;
if (!thread_)
return;
// Shut down thread on a thread other than UI so it can join a thread.
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&HandlerThread::StopThread, thread_));
}
// static
std::unique_ptr<AndroidDeviceManager> AndroidDeviceManager::Create() {
return base::WrapUnique(new AndroidDeviceManager());
}
void AndroidDeviceManager::SetDeviceProviders(
const DeviceProviders& providers) {
for (auto it = providers_.begin(); it != providers_.end(); ++it) {
(*it)->AddRef();
DeviceProvider* raw_ptr = it->get();
*it = nullptr;
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,
weak_factory_.GetWeakPtr(), callback));
}
void AndroidDeviceManager::CountDevices(
const base::Callback<void(int)>& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
handler_thread_->message_loop()->PostTask(
FROM_HERE, base::BindOnce(&UsbDeviceManagerHelper::CountDevices,
base::BindOnce(&OnCountDevices, callback)));
}
void AndroidDeviceManager::set_usb_device_manager_for_test(
device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
handler_thread_->message_loop()->PostTask(
FROM_HERE,
base::BindOnce(&UsbDeviceManagerHelper::SetUsbManagerForTesting,
std::move(fake_usb_manager)));
}
AndroidDeviceManager::AndroidDeviceManager()
: handler_thread_(HandlerThread::GetInstance()), weak_factory_(this) {}
AndroidDeviceManager::~AndroidDeviceManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
SetDeviceProviders(DeviceProviders());
}
void AndroidDeviceManager::UpdateDevices(
const DevicesCallback& callback,
std::unique_ptr<DeviceDescriptors> descriptors) {
Devices response;
DeviceWeakMap new_devices;
for (DeviceDescriptors::const_iterator it = descriptors->begin();
it != descriptors->end();
++it) {
auto 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);
}