// Copyright 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/android_device.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
#include "chrome/browser/devtools/adb/android_rsa.h"
#include "chrome/browser/devtools/adb/android_usb_device.h"
#include "chrome/browser/devtools/adb_client_socket.h"
#include "net/base/net_errors.h"
using content::BrowserThread;
namespace {
const char kHostTransportCommand[] = "host:transport:%s|%s";
const char kHostDevicesCommand[] = "host:devices";
const char kLocalAbstractCommand[] = "localabstract:%s";
const int kAdbPort = 5037;
const int kBufferSize = 16 * 1024;
// AdbDeviceImpl --------------------------------------------------------------
class AdbDeviceImpl : public AndroidDevice {
public:
AdbDeviceImpl(const std::string& serial, bool is_connected);
virtual void RunCommand(const std::string& command,
const CommandCallback& callback) OVERRIDE;
virtual void OpenSocket(const std::string& name,
const SocketCallback& callback) OVERRIDE;
private:
virtual ~AdbDeviceImpl() {}
};
AdbDeviceImpl::AdbDeviceImpl(const std::string& serial, bool is_connected)
: AndroidDevice(serial, is_connected) {
}
void AdbDeviceImpl::RunCommand(const std::string& command,
const CommandCallback& callback) {
std::string query = base::StringPrintf(kHostTransportCommand,
serial().c_str(), command.c_str());
AdbClientSocket::AdbQuery(kAdbPort, query, callback);
}
void AdbDeviceImpl::OpenSocket(const std::string& name,
const SocketCallback& callback) {
std::string socket_name =
base::StringPrintf(kLocalAbstractCommand, name.c_str());
AdbClientSocket::TransportQuery(kAdbPort, serial(), socket_name, callback);
}
// UsbDeviceImpl --------------------------------------------------------------
class UsbDeviceImpl : public AndroidDevice {
public:
explicit UsbDeviceImpl(AndroidUsbDevice* device);
virtual void RunCommand(const std::string& command,
const CommandCallback& callback) OVERRIDE;
virtual void OpenSocket(const std::string& name,
const SocketCallback& callback) OVERRIDE;
private:
void OnOpenSocket(const SocketCallback& callback,
net::StreamSocket* socket,
int result);
void OpenedForCommand(const CommandCallback& callback,
net::StreamSocket* socket,
int result);
void OnRead(net::StreamSocket* socket,
scoped_refptr<net::IOBuffer> buffer,
const std::string& data,
const CommandCallback& callback,
int result);
virtual ~UsbDeviceImpl() {}
scoped_refptr<AndroidUsbDevice> device_;
};
UsbDeviceImpl::UsbDeviceImpl(AndroidUsbDevice* device)
: AndroidDevice(device->serial(), device->is_connected()),
device_(device) {
device_->InitOnCallerThread();
}
void UsbDeviceImpl::RunCommand(const std::string& command,
const CommandCallback& callback) {
net::StreamSocket* socket = device_->CreateSocket(command);
int result = socket->Connect(base::Bind(&UsbDeviceImpl::OpenedForCommand,
this, callback, socket));
if (result != net::ERR_IO_PENDING)
callback.Run(result, std::string());
}
void UsbDeviceImpl::OpenSocket(const std::string& name,
const SocketCallback& callback) {
std::string socket_name =
base::StringPrintf(kLocalAbstractCommand, name.c_str());
net::StreamSocket* socket = device_->CreateSocket(socket_name);
int result = socket->Connect(base::Bind(&UsbDeviceImpl::OnOpenSocket, this,
callback, socket));
if (result != net::ERR_IO_PENDING)
callback.Run(result, NULL);
}
void UsbDeviceImpl::OnOpenSocket(const SocketCallback& callback,
net::StreamSocket* socket,
int result) {
callback.Run(result, result == net::OK ? socket : NULL);
}
void UsbDeviceImpl::OpenedForCommand(const CommandCallback& callback,
net::StreamSocket* socket,
int result) {
if (result != net::OK) {
callback.Run(result, std::string());
return;
}
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
result = socket->Read(buffer, kBufferSize,
base::Bind(&UsbDeviceImpl::OnRead, this,
socket, buffer, std::string(), callback));
if (result != net::ERR_IO_PENDING)
OnRead(socket, buffer, std::string(), callback, result);
}
void UsbDeviceImpl::OnRead(net::StreamSocket* socket,
scoped_refptr<net::IOBuffer> buffer,
const std::string& data,
const CommandCallback& callback,
int result) {
if (result <= 0) {
callback.Run(result, result == 0 ? data : std::string());
delete socket;
return;
}
std::string new_data = data + std::string(buffer->data(), result);
result = socket->Read(buffer, kBufferSize,
base::Bind(&UsbDeviceImpl::OnRead, this,
socket, buffer, new_data, callback));
if (result != net::ERR_IO_PENDING)
OnRead(socket, buffer, new_data, callback, result);
}
// AdbDeviceProvider -------------------------------------------
class AdbDeviceProvider: public AndroidDeviceProvider {
public:
virtual void QueryDevices(const QueryDevicesCallback& callback) OVERRIDE;
private:
void QueryDevicesOnAdbThread(const QueryDevicesCallback& callback);
void ReceivedAdbDevices(const QueryDevicesCallback& callback, int result,
const std::string& response);
virtual ~AdbDeviceProvider();
};
AdbDeviceProvider::~AdbDeviceProvider() {
}
void AdbDeviceProvider::QueryDevices(const QueryDevicesCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
adb_thread_->message_loop()->PostTask(
FROM_HERE, base::Bind(&AdbDeviceProvider::QueryDevicesOnAdbThread,
this, callback));
}
void AdbDeviceProvider::QueryDevicesOnAdbThread(
const QueryDevicesCallback& callback) {
DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
AdbClientSocket::AdbQuery(
kAdbPort, kHostDevicesCommand,
base::Bind(&AdbDeviceProvider::ReceivedAdbDevices, this, callback));
}
void AdbDeviceProvider::ReceivedAdbDevices(const QueryDevicesCallback& callback,
int result_code,
const std::string& response) {
DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
AndroidDevices result;
#if defined(DEBUG_DEVTOOLS)
// For desktop remote debugging.
result.push_back(new AdbDeviceImpl("", true));
#endif // defined(DEBUG_DEVTOOLS)
std::vector<std::string> serials;
Tokenize(response, "\n", &serials);
for (size_t i = 0; i < serials.size(); ++i) {
std::vector<std::string> tokens;
Tokenize(serials[i], "\t ", &tokens);
bool offline = tokens.size() > 1 && tokens[1] == "offline";
result.push_back(new AdbDeviceImpl(tokens[0], !offline));
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&AdbDeviceProvider::RunCallbackOnUIThread,
callback, result));
}
// UsbDeviceProvider -------------------------------------------
class UsbDeviceProvider: public AndroidDeviceProvider {
public:
explicit UsbDeviceProvider(Profile* profile);
virtual void QueryDevices(const QueryDevicesCallback& callback) OVERRIDE;
private:
virtual ~UsbDeviceProvider();
void WrapDevicesOnAdbThread(const QueryDevicesCallback& callback,
const AndroidUsbDevices& devices);
void EnumeratedDevices(const QueryDevicesCallback& callback,
const AndroidUsbDevices& devices);
scoped_ptr<crypto::RSAPrivateKey> rsa_key_;
};
UsbDeviceProvider::UsbDeviceProvider(Profile* profile){
rsa_key_.reset(AndroidRSAPrivateKey(profile));
}
UsbDeviceProvider::~UsbDeviceProvider() {
}
void UsbDeviceProvider::QueryDevices(const QueryDevicesCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
AndroidUsbDevice::Enumerate(rsa_key_.get(),
base::Bind(&UsbDeviceProvider::EnumeratedDevices,
this, callback));
}
void UsbDeviceProvider::EnumeratedDevices(const QueryDevicesCallback& callback,
const AndroidUsbDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
adb_thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&UsbDeviceProvider::WrapDevicesOnAdbThread,
this, callback, devices));
}
void UsbDeviceProvider::WrapDevicesOnAdbThread(
const QueryDevicesCallback& callback,const AndroidUsbDevices& devices) {
DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
AndroidDevices result;
for (AndroidUsbDevices::const_iterator it = devices.begin();
it != devices.end(); ++it)
result.push_back(new UsbDeviceImpl(*it));
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&UsbDeviceProvider::RunCallbackOnUIThread,
callback, result));
}
} // namespace
// AndroidDevice -------------------------------------------
AndroidDevice::AndroidDevice(const std::string& serial, bool is_connected)
: serial_(serial),
is_connected_(is_connected) {
}
void AndroidDevice::HttpQuery(
const std::string& la_name,
const std::string& request,
const CommandCallback& callback) {
OpenSocket(la_name, base::Bind(&AndroidDevice::OnHttpSocketOpened, this,
request, callback));
}
void AndroidDevice::HttpUpgrade(
const std::string& la_name,
const std::string& request,
const SocketCallback& callback) {
OpenSocket(la_name, base::Bind(&AndroidDevice::OnHttpSocketOpened2, this,
request, callback));
}
AndroidDevice::~AndroidDevice() {
}
void AndroidDevice::OnHttpSocketOpened(
const std::string& request,
const CommandCallback& callback,
int result,
net::StreamSocket* socket) {
if (result != net::OK) {
callback.Run(result, std::string());
return;
}
AdbClientSocket::HttpQuery(socket, request, callback);
}
void AndroidDevice::OnHttpSocketOpened2(
const std::string& request,
const SocketCallback& callback,
int result,
net::StreamSocket* socket) {
if (result != net::OK) {
callback.Run(result, NULL);
return;
}
AdbClientSocket::HttpQuery(socket, request, callback);
}
// AdbCountDevicesCommand -----------------------------------------------------
// TODO(zvorygin): Remove this class.
class AdbCountDevicesCommand : public base::RefCountedThreadSafe<
AdbCountDevicesCommand, BrowserThread::DeleteOnUIThread> {
public:
typedef base::Callback<void(int)> Callback;
AdbCountDevicesCommand(
scoped_refptr<RefCountedAdbThread> adb_thread,
const Callback& callback);
private:
friend struct BrowserThread::DeleteOnThread<
BrowserThread::UI>;
friend class base::DeleteHelper<AdbCountDevicesCommand>;
virtual ~AdbCountDevicesCommand();
void RequestAdbDeviceCount();
void ReceivedAdbDeviceCount(int result, const std::string& response);
void Respond(int count);
scoped_refptr<RefCountedAdbThread> adb_thread_;
Callback callback_;
};
AdbCountDevicesCommand::AdbCountDevicesCommand(
scoped_refptr<RefCountedAdbThread> adb_thread,
const Callback& callback)
: adb_thread_(adb_thread),
callback_(callback) {
adb_thread_->message_loop()->PostTask(
FROM_HERE, base::Bind(&AdbCountDevicesCommand::RequestAdbDeviceCount,
this));
}
AdbCountDevicesCommand::~AdbCountDevicesCommand() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
void AdbCountDevicesCommand::RequestAdbDeviceCount() {
DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
AdbClientSocket::AdbQuery(
kAdbPort, kHostDevicesCommand,
base::Bind(&AdbCountDevicesCommand::ReceivedAdbDeviceCount, this));
}
void AdbCountDevicesCommand::ReceivedAdbDeviceCount(
int result,
const std::string& response) {
DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
std::vector<std::string> serials;
Tokenize(response, "\n", &serials);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&AdbCountDevicesCommand::Respond, this, serials.size()));
}
void AdbCountDevicesCommand::Respond(int count) {
callback_.Run(count);
}
// AndroidDeviceProvider ---------------------------------------------------
AndroidDeviceProvider::AndroidDeviceProvider()
: adb_thread_(RefCountedAdbThread::GetInstance()) {
}
AndroidDeviceProvider::~AndroidDeviceProvider() {
}
// static
void AndroidDeviceProvider::RunCallbackOnUIThread(
const QueryDevicesCallback& callback,
const AndroidDevices& result) {
callback.Run(result);
}
// static
void AndroidDeviceProvider::CountDevices(bool discover_usb_devices,
const base::Callback<void(int)>& callback) {
if (discover_usb_devices) {
AndroidUsbDevice::CountDevices(callback);
return;
}
new AdbCountDevicesCommand(RefCountedAdbThread::GetInstance(), callback);
}
// static
scoped_refptr<AndroidDeviceProvider>
AndroidDeviceProvider::GetUsbDeviceProvider(Profile* profile) {
return new UsbDeviceProvider(profile);
}
// static
scoped_refptr<AndroidDeviceProvider>
AndroidDeviceProvider::GetAdbDeviceProvider() {
return new AdbDeviceProvider();
}