summaryrefslogtreecommitdiff
path: root/source/Plugins/Platform/Android/AdbClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/Platform/Android/AdbClient.cpp')
-rw-r--r--source/Plugins/Platform/Android/AdbClient.cpp569
1 files changed, 569 insertions, 0 deletions
diff --git a/source/Plugins/Platform/Android/AdbClient.cpp b/source/Plugins/Platform/Android/AdbClient.cpp
new file mode 100644
index 0000000000000..736447fd22d21
--- /dev/null
+++ b/source/Plugins/Platform/Android/AdbClient.cpp
@@ -0,0 +1,569 @@
+//===-- 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 <limits.h>
+
+#include <algorithm>
+#include <fstream>
+#include <sstream>
+
+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<uint64_t>(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<char> in_buffer;
+ error = ReadMessage (in_buffer);
+
+ llvm::StringRef response (&in_buffer[0], in_buffer.size ());
+ llvm::SmallVector<llvm::StringRef, 4> 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<int>(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<char> &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<char>& 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<std::chrono::milliseconds>(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<char> 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<char> 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<char> &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<char*>(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<char> 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<const char*> (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<char> 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;
+}