//===-- AdbClient.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Other libraries and framework includes #include "lldb/Core/DataBuffer.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataEncoder.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/FileUtilities.h" // Project includes #include "AdbClient.h" #include #include #include #include using namespace lldb; using namespace lldb_private; using namespace lldb_private::platform_android; namespace { const uint32_t kReadTimeout = 1000000; // 1 second const char * kOKAY = "OKAY"; const char * kFAIL = "FAIL"; const char * kDATA = "DATA"; const char * kDONE = "DONE"; const char * kSEND = "SEND"; const char * kRECV = "RECV"; const char * kSTAT = "STAT"; const size_t kSyncPacketLen = 8; // Maximum size of a filesync DATA packet. const size_t kMaxPushData = 2*1024; // Default mode for pushed files. const uint32_t kDefaultMode = 0100770; // S_IFREG | S_IRWXU | S_IRWXG const char * kSocketNamespaceAbstract = "localabstract"; const char * kSocketNamespaceFileSystem = "localfilesystem"; } // namespace Error AdbClient::CreateByDeviceID(const std::string &device_id, AdbClient &adb) { DeviceIDList connect_devices; auto error = adb.GetDevices(connect_devices); if (error.Fail()) return error; if (device_id.empty()) { if (connect_devices.size() != 1) return Error("Expected a single connected device, got instead %" PRIu64, static_cast(connect_devices.size())); adb.SetDeviceID(connect_devices.front()); } else { auto find_it = std::find(connect_devices.begin(), connect_devices.end(), device_id); if (find_it == connect_devices.end()) return Error("Device \"%s\" not found", device_id.c_str()); adb.SetDeviceID(*find_it); } return error; } AdbClient::AdbClient (const std::string &device_id) : m_device_id (device_id) { } void AdbClient::SetDeviceID (const std::string &device_id) { m_device_id = device_id; } const std::string& AdbClient::GetDeviceID() const { return m_device_id; } Error AdbClient::Connect () { Error error; m_conn.Connect ("connect://localhost:5037", &error); return error; } Error AdbClient::GetDevices (DeviceIDList &device_list) { device_list.clear (); auto error = SendMessage ("host:devices"); if (error.Fail ()) return error; error = ReadResponseStatus (); if (error.Fail ()) return error; std::vector in_buffer; error = ReadMessage (in_buffer); llvm::StringRef response (&in_buffer[0], in_buffer.size ()); llvm::SmallVector devices; response.split (devices, "\n", -1, false); for (const auto device: devices) device_list.push_back (device.split ('\t').first); return error; } Error AdbClient::SetPortForwarding (const uint16_t local_port, const uint16_t remote_port) { char message[48]; snprintf (message, sizeof (message), "forward:tcp:%d;tcp:%d", local_port, remote_port); const auto error = SendDeviceMessage (message); if (error.Fail ()) return error; return ReadResponseStatus (); } Error AdbClient::SetPortForwarding (const uint16_t local_port, const char* remote_socket_name, const UnixSocketNamespace socket_namespace) { char message[PATH_MAX]; const char * sock_namespace_str = (socket_namespace == UnixSocketNamespaceAbstract) ? kSocketNamespaceAbstract : kSocketNamespaceFileSystem; snprintf (message, sizeof (message), "forward:tcp:%d;%s:%s", local_port, sock_namespace_str, remote_socket_name); const auto error = SendDeviceMessage (message); if (error.Fail ()) return error; return ReadResponseStatus (); } Error AdbClient::DeletePortForwarding (const uint16_t local_port) { char message[32]; snprintf (message, sizeof (message), "killforward:tcp:%d", local_port); const auto error = SendDeviceMessage (message); if (error.Fail ()) return error; return ReadResponseStatus (); } Error AdbClient::SendMessage (const std::string &packet, const bool reconnect) { Error error; if (reconnect) { error = Connect (); if (error.Fail ()) return error; } char length_buffer[5]; snprintf (length_buffer, sizeof (length_buffer), "%04x", static_cast(packet.size ())); ConnectionStatus status; m_conn.Write (length_buffer, 4, status, &error); if (error.Fail ()) return error; m_conn.Write (packet.c_str (), packet.size (), status, &error); return error; } Error AdbClient::SendDeviceMessage (const std::string &packet) { std::ostringstream msg; msg << "host-serial:" << m_device_id << ":" << packet; return SendMessage (msg.str ()); } Error AdbClient::ReadMessage (std::vector &message) { message.clear (); char buffer[5]; buffer[4] = 0; auto error = ReadAllBytes (buffer, 4); if (error.Fail ()) return error; unsigned int packet_len = 0; sscanf (buffer, "%x", &packet_len); message.resize (packet_len, 0); error = ReadAllBytes (&message[0], packet_len); if (error.Fail ()) message.clear (); return error; } Error AdbClient::ReadMessageStream (std::vector& message, uint32_t timeout_ms) { auto start = std::chrono::steady_clock::now(); message.clear(); Error error; lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; char buffer[1024]; while (error.Success() && status == lldb::eConnectionStatusSuccess) { auto end = std::chrono::steady_clock::now(); uint32_t elapsed_time = std::chrono::duration_cast(end - start).count(); if (elapsed_time >= timeout_ms) return Error("Timed out"); size_t n = m_conn.Read(buffer, sizeof(buffer), 1000 * (timeout_ms - elapsed_time), status, &error); if (n > 0) message.insert(message.end(), &buffer[0], &buffer[n]); } return error; } Error AdbClient::ReadResponseStatus() { char response_id[5]; static const size_t packet_len = 4; response_id[packet_len] = 0; auto error = ReadAllBytes (response_id, packet_len); if (error.Fail ()) return error; if (strncmp (response_id, kOKAY, packet_len) != 0) return GetResponseError (response_id); return error; } Error AdbClient::GetResponseError (const char *response_id) { if (strcmp (response_id, kFAIL) != 0) return Error ("Got unexpected response id from adb: \"%s\"", response_id); std::vector error_message; auto error = ReadMessage (error_message); if (error.Success ()) error.SetErrorString (std::string (&error_message[0], error_message.size ()).c_str ()); return error; } Error AdbClient::SwitchDeviceTransport () { std::ostringstream msg; msg << "host:transport:" << m_device_id; auto error = SendMessage (msg.str ()); if (error.Fail ()) return error; return ReadResponseStatus (); } Error AdbClient::PullFile (const FileSpec &remote_file, const FileSpec &local_file) { auto error = StartSync (); if (error.Fail ()) return error; const auto local_file_path = local_file.GetPath (); llvm::FileRemover local_file_remover (local_file_path.c_str ()); std::ofstream dst (local_file_path, std::ios::out | std::ios::binary); if (!dst.is_open ()) return Error ("Unable to open local file %s", local_file_path.c_str()); const auto remote_file_path = remote_file.GetPath (false); error = SendSyncRequest (kRECV, remote_file_path.length (), remote_file_path.c_str ()); if (error.Fail ()) return error; std::vector chunk; bool eof = false; while (!eof) { error = PullFileChunk (chunk, eof); if (error.Fail ()) return error; if (!eof) dst.write (&chunk[0], chunk.size ()); } local_file_remover.releaseFile (); return error; } Error AdbClient::PushFile (const FileSpec &local_file, const FileSpec &remote_file) { auto error = StartSync (); if (error.Fail ()) return error; const auto local_file_path (local_file.GetPath ()); std::ifstream src (local_file_path.c_str(), std::ios::in | std::ios::binary); if (!src.is_open ()) return Error ("Unable to open local file %s", local_file_path.c_str()); std::stringstream file_description; file_description << remote_file.GetPath(false).c_str() << "," << kDefaultMode; std::string file_description_str = file_description.str(); error = SendSyncRequest (kSEND, file_description_str.length(), file_description_str.c_str()); if (error.Fail ()) return error; char chunk[kMaxPushData]; while (!src.eof() && !src.read(chunk, kMaxPushData).bad()) { size_t chunk_size = src.gcount(); error = SendSyncRequest(kDATA, chunk_size, chunk); if (error.Fail ()) return Error ("Failed to send file chunk: %s", error.AsCString ()); } error = SendSyncRequest(kDONE, local_file.GetModificationTime().seconds(), nullptr); if (error.Fail ()) return error; std::string response_id; uint32_t data_len; error = ReadSyncHeader (response_id, data_len); if (error.Fail ()) return Error ("Failed to read DONE response: %s", error.AsCString ()); if (response_id == kFAIL) { std::string error_message (data_len, 0); error = ReadAllBytes (&error_message[0], data_len); if (error.Fail ()) return Error ("Failed to read DONE error message: %s", error.AsCString ()); return Error ("Failed to push file: %s", error_message.c_str ()); } else if (response_id != kOKAY) return Error ("Got unexpected DONE response: %s", response_id.c_str ()); // If there was an error reading the source file, finish the adb file // transfer first so that adb isn't expecting any more data. if (src.bad()) return Error ("Failed read on %s", local_file_path.c_str()); return error; } Error AdbClient::StartSync () { auto error = SwitchDeviceTransport (); if (error.Fail ()) return Error ("Failed to switch to device transport: %s", error.AsCString ()); error = Sync (); if (error.Fail ()) return Error ("Sync failed: %s", error.AsCString ()); return error; } Error AdbClient::Sync () { auto error = SendMessage ("sync:", false); if (error.Fail ()) return error; return ReadResponseStatus (); } Error AdbClient::PullFileChunk (std::vector &buffer, bool &eof) { buffer.clear (); std::string response_id; uint32_t data_len; auto error = ReadSyncHeader (response_id, data_len); if (error.Fail ()) return error; if (response_id == kDATA) { buffer.resize (data_len, 0); error = ReadAllBytes (&buffer[0], data_len); if (error.Fail ()) buffer.clear (); } else if (response_id == kDONE) { eof = true; } else if (response_id == kFAIL) { std::string error_message (data_len, 0); error = ReadAllBytes (&error_message[0], data_len); if (error.Fail ()) return Error ("Failed to read pull error message: %s", error.AsCString ()); return Error ("Failed to pull file: %s", error_message.c_str ()); } else return Error ("Pull failed with unknown response: %s", response_id.c_str ()); return Error (); } Error AdbClient::SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data) { const DataBufferSP data_sp (new DataBufferHeap (kSyncPacketLen, 0)); DataEncoder encoder (data_sp, eByteOrderLittle, sizeof (void*)); auto offset = encoder.PutData (0, request_id, strlen(request_id)); encoder.PutU32 (offset, data_len); Error error; ConnectionStatus status; m_conn.Write (data_sp->GetBytes (), kSyncPacketLen, status, &error); if (error.Fail ()) return error; if (data) m_conn.Write (data, data_len, status, &error); return error; } Error AdbClient::ReadSyncHeader (std::string &response_id, uint32_t &data_len) { char buffer[kSyncPacketLen]; auto error = ReadAllBytes (buffer, kSyncPacketLen); if (error.Success ()) { response_id.assign (&buffer[0], 4); DataExtractor extractor (&buffer[4], 4, eByteOrderLittle, sizeof (void*)); offset_t offset = 0; data_len = extractor.GetU32 (&offset); } return error; } Error AdbClient::ReadAllBytes (void *buffer, size_t size) { Error error; ConnectionStatus status; char *read_buffer = static_cast(buffer); size_t tota_read_bytes = 0; while (tota_read_bytes < size) { auto read_bytes = m_conn.Read (read_buffer + tota_read_bytes, size - tota_read_bytes, kReadTimeout, status, &error); if (error.Fail ()) return error; tota_read_bytes += read_bytes; } return error; } Error AdbClient::Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime) { auto error = StartSync (); if (error.Fail ()) return error; const std::string remote_file_path (remote_file.GetPath (false)); error = SendSyncRequest (kSTAT, remote_file_path.length (), remote_file_path.c_str ()); if (error.Fail ()) return Error ("Failed to send request: %s", error.AsCString ()); static const size_t stat_len = strlen (kSTAT); static const size_t response_len = stat_len + (sizeof (uint32_t) * 3); std::vector buffer (response_len); error = ReadAllBytes (&buffer[0], buffer.size ()); if (error.Fail ()) return Error ("Failed to read response: %s", error.AsCString ()); DataExtractor extractor (&buffer[0], buffer.size (), eByteOrderLittle, sizeof (void*)); offset_t offset = 0; const void* command = extractor.GetData (&offset, stat_len); if (!command) return Error ("Failed to get response command"); const char* command_str = static_cast (command); if (strncmp (command_str, kSTAT, stat_len)) return Error ("Got invalid stat command: %s", command_str); mode = extractor.GetU32 (&offset); size = extractor.GetU32 (&offset); mtime = extractor.GetU32 (&offset); return Error (); } Error AdbClient::Shell (const char* command, uint32_t timeout_ms, std::string* output) { auto error = SwitchDeviceTransport (); if (error.Fail ()) return Error ("Failed to switch to device transport: %s", error.AsCString ()); StreamString adb_command; adb_command.Printf("shell:%s", command); error = SendMessage (adb_command.GetData(), false); if (error.Fail ()) return error; error = ReadResponseStatus (); if (error.Fail ()) return error; std::vector in_buffer; error = ReadMessageStream (in_buffer, timeout_ms); if (error.Fail()) return error; if (output) output->assign(in_buffer.begin(), in_buffer.end()); return error; }