Support remote-android with multiple connected devices.

Summary:
This change introduces a new URL scheme for `platform connect`:
```
adb://device-id:port
```

Reviewers: vharron, tberghammer, clayborg, ovyalov

Reviewed By: ovyalov

Subscribers: tberghammer, lldb-commits

Differential Revision: http://reviews.llvm.org/D9358

llvm-svn: 236321
diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.cpp b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
index 36f8eadc..8c45616 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.cpp
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
@@ -31,27 +31,28 @@
 }  // namespace
 
 Error
-AdbClient::CreateByDeviceID (const char* device_id, AdbClient &adb)
+AdbClient::CreateByDeviceID(const std::string &device_id, AdbClient &adb)
 {
     DeviceIDList connect_devices;
-    auto error = adb.GetDevices (connect_devices);
-    if (error.Fail ())
+    auto error = adb.GetDevices(connect_devices);
+    if (error.Fail())
         return error;
 
-    if (device_id)
+    if (device_id.empty())
     {
-        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);
+        if (connect_devices.size() != 1)
+            return Error("Expected a single connected device, got instead %" PRIu64,
+                    static_cast<uint64_t>(connect_devices.size()));
 
-        adb.SetDeviceID (*find_it);
+        adb.SetDeviceID(connect_devices.front());
     }
     else
     {
-        if (connect_devices.size () != 1)
-            return Error ("Expected a single connected device, got instead %" PRIu64, static_cast<uint64_t>(connect_devices.size ()));
+        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 (connect_devices.front ());
+        adb.SetDeviceID(*find_it);
     }
     return error;
 }
diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.h b/lldb/source/Plugins/Platform/Android/AdbClient.h
index 3dcc006..235fc56 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.h
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.h
@@ -32,7 +32,7 @@
     using DeviceIDList = std::list<std::string>;
 
     static Error
-    CreateByDeviceID (const char* device_id, AdbClient &adb);
+    CreateByDeviceID(const std::string &device_id, AdbClient &adb);
 
     AdbClient () = default;
     explicit AdbClient (const std::string &device_id);
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
index 7bc3287..3bd628b 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -13,6 +13,7 @@
 #include "lldb/Core/Log.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Host/HostInfo.h"
+#include "Utility/UriParser.h"
 
 // Project includes
 #include "AdbClient.h"
@@ -171,9 +172,9 @@
 }
 
 Error
-PlatformAndroid::ConnectRemote (Args& args)
+PlatformAndroid::ConnectRemote(Args& args)
 {
-    m_device_id.clear ();
+    m_device_id.clear();
 
     if (IsHost())
     {
@@ -183,17 +184,25 @@
     if (!m_remote_platform_sp)
         m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
 
-    auto error = PlatformLinux::ConnectRemote (args);
-    if (error.Success ())
+    int port;
+    std::string scheme, host, path;
+    const char *url = args.GetArgumentAtIndex(0);
+    if (!url)
+        return Error("URL is null.");
+    if (!UriParser::Parse(url, scheme, host, port, path))
+        return Error("Invalid URL: %s", url);
+    if (scheme == "adb")
+        m_device_id = host;
+
+    auto error = PlatformLinux::ConnectRemote(args);
+    if (error.Success())
     {
-        // Fetch the device list from ADB and if only 1 device found then use that device
-        // TODO: Handle the case when more device is available
         AdbClient adb;
-        error = AdbClient::CreateByDeviceID (nullptr, adb);
-        if (error.Fail ())
+        error = AdbClient::CreateByDeviceID(m_device_id, adb);
+        if (error.Fail())
             return error;
 
-        m_device_id = adb.GetDeviceID ();
+        m_device_id = adb.GetDeviceID();
     }
     return error;
 }
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
index 2cb487a..213c3b2 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
@@ -27,18 +27,16 @@
 {
     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
 
-    // Fetch the device list from ADB and if only 1 device found then use that device
-    // TODO: Handle the case when more device is available
     AdbClient adb;
-    auto error = AdbClient::CreateByDeviceID (nullptr, adb);
+    auto error = AdbClient::CreateByDeviceID(device_id, adb);
     if (error.Fail ())
         return error;
 
-    device_id = adb.GetDeviceID ();
+    device_id = adb.GetDeviceID();
     if (log)
         log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
 
-    return adb.SetPortForwarding (port);
+    return adb.SetPortForwarding(port);
 }
 
 static Error
@@ -55,9 +53,7 @@
 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
 {
     for (const auto& it : m_port_forwards)
-    {
-        DeleteForwardPortWithAdb (it.second.first, it.second.second);
-    }
+        DeleteForwardPortWithAdb(it.second, m_device_id);
 }
 
 uint16_t
@@ -67,12 +63,11 @@
     if (port == 0)
         return port;
 
-    std::string device_id;
-    Error error = ForwardPortWithAdb (port, device_id);
+    Error error = ForwardPortWithAdb(port, m_device_id);
     if (error.Fail ())
         return 0;
 
-    m_port_forwards[pid] = std::make_pair (port, device_id);
+    m_port_forwards[pid] = port;
 
     return port;
 }
@@ -87,23 +82,28 @@
 Error
 PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
 {
-    if (args.GetArgumentCount () != 1)
-        return Error ("\"platform connect\" takes a single argument: <connect-url>");
-  
+    m_device_id.clear();
+
+    if (args.GetArgumentCount() != 1)
+        return Error("\"platform connect\" takes a single argument: <connect-url>");
+
     int port;
     std::string scheme, host, path;
     const char *url = args.GetArgumentAtIndex (0);
+    if (!url)
+        return Error("URL is null.");
     if (!UriParser::Parse (url, scheme, host, port, path))
-        return Error ("invalid uri");
+        return Error("Invalid URL: %s", url);
+    if (scheme == "adb")
+        m_device_id = host;
 
-    std::string device_id;
-    Error error = ForwardPortWithAdb (port, device_id);
-    if (error.Fail ())
+    Error error = ForwardPortWithAdb(port, m_device_id);
+    if (error.Fail())
         return error;
 
-    m_port_forwards[g_remote_platform_pid] = std::make_pair (port, device_id);
+    m_port_forwards[g_remote_platform_pid] = port;
 
-    error = PlatformRemoteGDBServer::ConnectRemote (args);
+    error = PlatformRemoteGDBServer::ConnectRemote(args);
     if (error.Fail ())
         DeleteForwardPort (g_remote_platform_pid);
 
@@ -120,18 +120,18 @@
 void
 PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
 {
-    Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
 
-    auto it = m_port_forwards.find (pid);
-    if (it == m_port_forwards.end ())
+    auto it = m_port_forwards.find(pid);
+    if (it == m_port_forwards.end())
         return;
 
-    const auto& forward_val = it->second;
-    const auto error = DeleteForwardPortWithAdb (forward_val.first, forward_val.second);
-    if (error.Fail ()) {
+    const auto port = it->second;
+    const auto error = DeleteForwardPortWithAdb(port, m_device_id);
+    if (error.Fail()) {
         if (log)
-            log->Printf ("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
-                         pid, forward_val.first, forward_val.second.c_str (), error.AsCString ());
+            log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
+                         pid, port, m_device_id.c_str(), error.AsCString());
     }
-    m_port_forwards.erase (it);
+    m_port_forwards.erase(it);
 }
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h b/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h
index 1959bb8..e549435 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h
@@ -37,7 +37,8 @@
     DisconnectRemote () override;
 
 protected:
-    std::map<lldb::pid_t, std::pair<uint16_t, std::string>> m_port_forwards;
+    std::string m_device_id;
+    std::map<lldb::pid_t, uint16_t> m_port_forwards;
 
     uint16_t
     LaunchGDBserverAndGetPort (lldb::pid_t &pid) override;