| Index: chrome/test/chromedriver/net/adb_client_socket.cc
|
| diff --git a/chrome/test/chromedriver/net/adb_client_socket.cc b/chrome/test/chromedriver/net/adb_client_socket.cc
|
| index 3d5ea5e68f5b83dcc47e8acae0405c19ca1ce0fb..448378f3357b9463054086d435a41d76b2f98b5b 100644
|
| --- a/chrome/test/chromedriver/net/adb_client_socket.cc
|
| +++ b/chrome/test/chromedriver/net/adb_client_socket.cc
|
| @@ -23,9 +23,14 @@
|
| namespace {
|
|
|
| const int kBufferSize = 16 * 1024;
|
| +const size_t kAdbDataChunkSize = 32 * 1024;
|
| const char kOkayResponse[] = "OKAY";
|
| const char kHostTransportCommand[] = "host:transport:%s";
|
| const char kLocalAbstractCommand[] = "localabstract:%s";
|
| +const char kSyncCommand[] = "sync:";
|
| +const char kSendCommand[] = "SEND";
|
| +const char kDataCommand[] = "DATA";
|
| +const char kDoneCommand[] = "DONE";
|
|
|
| typedef base::Callback<void(int, const std::string&)> CommandCallback;
|
| typedef base::Callback<void(int, net::StreamSocket*)> SocketCallback;
|
| @@ -301,6 +306,129 @@ class AdbQuerySocket : AdbClientSocket {
|
| CommandCallback callback_;
|
| };
|
|
|
| +// Implement the ADB protocol to send a file to the device.
|
| +// The protocol consists of the following steps:
|
| +// * Send "host:transport" command with device serial
|
| +// * Send "sync:" command to initialize file transfer
|
| +// * Send "SEND" command with name and mode of the file
|
| +// * Send "DATA" command one or more times for the file content
|
| +// * Send "DONE" command to indicate end of file transfer
|
| +// The first two commands use normal ADB command format implemented by
|
| +// AdbClientSocket::SendCommand. The remaining commands use a special
|
| +// format implemented by AdbSendFileSocket::SendPayload.
|
| +class AdbSendFileSocket : AdbClientSocket {
|
| + public:
|
| + AdbSendFileSocket(int port,
|
| + const std::string& serial,
|
| + const std::string& filename,
|
| + const std::string& content,
|
| + const CommandCallback& callback)
|
| + : AdbClientSocket(port),
|
| + serial_(serial),
|
| + filename_(filename),
|
| + content_(content),
|
| + current_offset_(0),
|
| + callback_(callback) {
|
| + Connect(
|
| + base::Bind(&AdbSendFileSocket::SendTransport, base::Unretained(this)));
|
| + }
|
| +
|
| + private:
|
| + ~AdbSendFileSocket() {}
|
| +
|
| + void SendTransport(int result) {
|
| + if (!CheckNetResultOrDie(result))
|
| + return;
|
| + SendCommand(
|
| + base::StringPrintf(kHostTransportCommand, serial_.c_str()), true, true,
|
| + base::Bind(&AdbSendFileSocket::SendSync, base::Unretained(this)));
|
| + }
|
| +
|
| + void SendSync(int result, const std::string& response) {
|
| + if (!CheckNetResultOrDie(result))
|
| + return;
|
| + SendCommand(
|
| + kSyncCommand, true, true,
|
| + base::Bind(&AdbSendFileSocket::SendSend, base::Unretained(this)));
|
| + }
|
| +
|
| + void SendSend(int result, const std::string& response) {
|
| + if (!CheckNetResultOrDie(result))
|
| + return;
|
| + // File mode. The following value is equivalent to S_IRUSR | S_IWUSR.
|
| + // Can't use the symbolic names since they are not available on Windows.
|
| + int mode = 0600;
|
| + std::string payload = base::StringPrintf("%s,%d", filename_.c_str(), mode);
|
| + SendPayload(
|
| + kSendCommand, payload.length(), payload.c_str(), payload.length(),
|
| + base::Bind(&AdbSendFileSocket::SendContent, base::Unretained(this)));
|
| + }
|
| +
|
| + void SendContent(int result) {
|
| + if (!CheckNetResultOrDie(result))
|
| + return;
|
| + if (current_offset_ >= content_.length()) {
|
| + SendDone();
|
| + return;
|
| + }
|
| + size_t offset = current_offset_;
|
| + size_t length = std::min(content_.length() - offset, kAdbDataChunkSize);
|
| + current_offset_ += length;
|
| + SendPayload(
|
| + kDataCommand, length, content_.c_str() + offset, length,
|
| + base::Bind(&AdbSendFileSocket::SendContent, base::Unretained(this)));
|
| + }
|
| +
|
| + void SendDone() {
|
| + int data = time(NULL);
|
| + SendPayload(kDoneCommand, data, nullptr, 0,
|
| + base::Bind(&AdbSendFileSocket::ReadFinalResponse,
|
| + base::Unretained(this)));
|
| + }
|
| +
|
| + void ReadFinalResponse(int result) {
|
| + ReadResponse(callback_, true, false, result);
|
| + }
|
| +
|
| + // Send a special payload command ("SEND", "DATA", or "DONE").
|
| + // Each command consists of a command line, followed by a 4-byte integer
|
| + // sent in raw little-endian format, followed by an optional payload.
|
| + void SendPayload(const char* command,
|
| + int data,
|
| + const char* payload,
|
| + size_t payloadLength,
|
| + const net::CompletionCallback& callback) {
|
| + std::string buffer(command);
|
| + for (int i = 0; i < 4; i++) {
|
| + buffer.append(1, static_cast<char>(data & 0xff));
|
| + data >>= 8;
|
| + }
|
| + if (payloadLength > 0)
|
| + buffer.append(payload, payloadLength);
|
| +
|
| + scoped_refptr<net::StringIOBuffer> request_buffer =
|
| + new net::StringIOBuffer(buffer);
|
| + int result =
|
| + socket_->Write(request_buffer.get(), request_buffer->size(), callback);
|
| + if (result != net::ERR_IO_PENDING)
|
| + callback.Run(result);
|
| + }
|
| +
|
| + bool CheckNetResultOrDie(int result) {
|
| + if (result >= 0)
|
| + return true;
|
| + callback_.Run(result, NULL);
|
| + delete this;
|
| + return false;
|
| + }
|
| +
|
| + std::string serial_;
|
| + std::string filename_;
|
| + std::string content_;
|
| + size_t current_offset_;
|
| + CommandCallback callback_;
|
| +};
|
| +
|
| } // namespace
|
|
|
| // static
|
| @@ -310,6 +438,15 @@ void AdbClientSocket::AdbQuery(int port,
|
| new AdbQuerySocket(port, query, callback);
|
| }
|
|
|
| +// static
|
| +void AdbClientSocket::SendFile(int port,
|
| + const std::string& serial,
|
| + const std::string& filename,
|
| + const std::string& content,
|
| + const CommandCallback& callback) {
|
| + new AdbSendFileSocket(port, serial, filename, content, callback);
|
| +}
|
| +
|
| #if BUILDFLAG(DEBUG_DEVTOOLS)
|
| static void UseTransportQueryForDesktop(const SocketCallback& callback,
|
| net::StreamSocket* socket,
|
|
|