OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/devtools/devtools_adb_bridge.h" | 5 #include "chrome/browser/devtools/devtools_adb_bridge.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop_proxy.h" | 13 #include "base/message_loop_proxy.h" |
14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
15 #include "base/stringprintf.h" | 15 #include "base/stringprintf.h" |
16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
17 #include "base/threading/thread.h" | 17 #include "base/threading/thread.h" |
18 #include "base/values.h" | 18 #include "base/values.h" |
19 #include "chrome/browser/devtools/adb_client_socket.h" | 19 #include "chrome/browser/devtools/adb_client_socket.h" |
| 20 #include "chrome/browser/profiles/profile.h" |
20 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
| 22 #include "content/public/browser/devtools_agent_host.h" |
| 23 #include "content/public/browser/devtools_client_host.h" |
| 24 #include "content/public/browser/devtools_manager.h" |
21 #include "net/base/net_errors.h" | 25 #include "net/base/net_errors.h" |
| 26 #include "net/server/web_socket.h" |
22 | 27 |
23 using content::BrowserThread; | 28 using content::BrowserThread; |
| 29 using net::WebSocket; |
24 | 30 |
25 namespace { | 31 namespace { |
26 | 32 |
27 static const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread"; | 33 static const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread"; |
28 static const char kDevToolsChannelName[] = "chrome_devtools_remote"; | 34 static const char kDevToolsChannelName[] = "chrome_devtools_remote"; |
29 static const char kHostDevicesCommand[] = "host:devices"; | 35 static const char kHostDevicesCommand[] = "host:devices"; |
30 static const char kDeviceModelCommand[] = | 36 static const char kDeviceModelCommand[] = |
31 "host:transport:%s|shell:getprop ro.product.model"; | 37 "host:transport:%s|shell:getprop ro.product.model"; |
32 static const char kPageListQuery[] = "/json"; | 38 |
| 39 static const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n"; |
| 40 static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n" |
| 41 "Upgrade: WebSocket\r\n" |
| 42 "Connection: Upgrade\r\n" |
| 43 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" |
| 44 "Sec-WebSocket-Version: 13\r\n" |
| 45 "\r\n"; |
33 const int kAdbPort = 5037; | 46 const int kAdbPort = 5037; |
| 47 const int kBufferSize = 16 * 1024; |
| 48 |
| 49 typedef DevToolsAdbBridge::Callback Callback; |
| 50 typedef DevToolsAdbBridge::PagesCallback PagesCallback; |
| 51 |
| 52 class AdbQueryCommand : public base::RefCounted<AdbQueryCommand> { |
| 53 public: |
| 54 AdbQueryCommand(const std::string& query, |
| 55 const Callback& callback) |
| 56 : query_(query), |
| 57 callback_(callback) { |
| 58 } |
| 59 |
| 60 void Run() { |
| 61 AdbClientSocket::AdbQuery(kAdbPort, query_, |
| 62 base::Bind(&AdbQueryCommand::Handle, this)); |
| 63 } |
| 64 |
| 65 private: |
| 66 friend class base::RefCounted<AdbQueryCommand>; |
| 67 virtual ~AdbQueryCommand() {} |
| 68 |
| 69 void Handle(int result, const std::string& response) { |
| 70 BrowserThread::PostTask( |
| 71 BrowserThread::UI, FROM_HERE, |
| 72 base::Bind(&AdbQueryCommand::Respond, this, result, response)); |
| 73 } |
| 74 |
| 75 void Respond(int result, const std::string& response) { |
| 76 callback_.Run(result, response); |
| 77 } |
| 78 |
| 79 std::string query_; |
| 80 Callback callback_; |
| 81 }; |
| 82 |
| 83 class AdbPagesCommand : public base::RefCounted<AdbPagesCommand> { |
| 84 public: |
| 85 explicit AdbPagesCommand(const PagesCallback& callback) |
| 86 : callback_(callback) { |
| 87 pages_.reset(new DevToolsAdbBridge::RemotePages()); |
| 88 } |
| 89 |
| 90 void Run() { |
| 91 AdbClientSocket::AdbQuery( |
| 92 kAdbPort, kHostDevicesCommand, |
| 93 base::Bind(&AdbPagesCommand::ReceivedDevices, this)); |
| 94 } |
| 95 |
| 96 private: |
| 97 friend class base::RefCounted<AdbPagesCommand>; |
| 98 virtual ~AdbPagesCommand() {} |
| 99 |
| 100 void ReceivedDevices(int result, const std::string& response) { |
| 101 if (result != net::OK) { |
| 102 ProcessSerials(); |
| 103 return; |
| 104 } |
| 105 |
| 106 std::vector<std::string> devices; |
| 107 Tokenize(response, "\n", &devices); |
| 108 for (size_t i = 0; i < devices.size(); ++i) { |
| 109 std::vector<std::string> tokens; |
| 110 Tokenize(devices[i], "\t ", &tokens); |
| 111 std::string serial = tokens[0]; |
| 112 serials_.push_back(serial); |
| 113 } |
| 114 |
| 115 ProcessSerials(); |
| 116 } |
| 117 |
| 118 void ProcessSerials() { |
| 119 if (serials_.size() == 0) { |
| 120 BrowserThread::PostTask( |
| 121 BrowserThread::UI, FROM_HERE, |
| 122 base::Bind(&AdbPagesCommand::Respond, this)); |
| 123 return; |
| 124 } |
| 125 |
| 126 AdbClientSocket::AdbQuery( |
| 127 kAdbPort, |
| 128 base::StringPrintf(kDeviceModelCommand, serials_.back().c_str()), |
| 129 base::Bind(&AdbPagesCommand::ReceivedModel, this)); |
| 130 } |
| 131 |
| 132 void ReceivedModel(int result, const std::string& response) { |
| 133 if (result != net::OK) { |
| 134 serials_.pop_back(); |
| 135 ProcessSerials(); |
| 136 return; |
| 137 } |
| 138 |
| 139 AdbClientSocket::HttpQuery( |
| 140 kAdbPort, serials_.back(), kDevToolsChannelName, kPageListRequest, |
| 141 base::Bind(&AdbPagesCommand::ReceivedPages, this, response)); |
| 142 } |
| 143 |
| 144 void ReceivedPages(const std::string& model, |
| 145 int result, |
| 146 const std::string& response) { |
| 147 std::string serial = serials_.back(); |
| 148 serials_.pop_back(); |
| 149 if (result < 0) { |
| 150 ProcessSerials(); |
| 151 return; |
| 152 } |
| 153 |
| 154 std::string body = response.substr(result); |
| 155 scoped_ptr<base::Value> value(base::JSONReader::Read(body)); |
| 156 base::ListValue* list_value; |
| 157 if (!value || !value->GetAsList(&list_value)) { |
| 158 ProcessSerials(); |
| 159 return; |
| 160 } |
| 161 |
| 162 base::Value* item; |
| 163 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
| 164 list_value->Get(i, &item); |
| 165 base::DictionaryValue* dict; |
| 166 if (!item || !item->GetAsDictionary(&dict)) |
| 167 continue; |
| 168 pages_->push_back( |
| 169 new DevToolsAdbBridge::RemotePage(serial, model, *dict)); |
| 170 } |
| 171 ProcessSerials(); |
| 172 } |
| 173 |
| 174 void Respond() { |
| 175 callback_.Run(net::OK, pages_.release()); |
| 176 } |
| 177 |
| 178 PagesCallback callback_; |
| 179 std::vector<std::string> serials_; |
| 180 scoped_ptr<DevToolsAdbBridge::RemotePages> pages_; |
| 181 }; |
| 182 |
| 183 class AdbAttachCommand : public base::RefCounted<AdbAttachCommand> { |
| 184 public: |
| 185 explicit AdbAttachCommand(scoped_refptr<DevToolsAdbBridge::RemotePage> page) |
| 186 : page_(page) { |
| 187 } |
| 188 |
| 189 void Run() { |
| 190 AdbClientSocket::HttpQuery( |
| 191 kAdbPort, page_->serial(), kDevToolsChannelName, |
| 192 base::StringPrintf(kWebSocketUpgradeRequest, |
| 193 page_->debug_url().c_str()), |
| 194 base::Bind(&AdbAttachCommand::Handle, this)); |
| 195 } |
| 196 |
| 197 private: |
| 198 friend class base::RefCounted<AdbAttachCommand>; |
| 199 virtual ~AdbAttachCommand() {} |
| 200 |
| 201 void Handle(int result, net::TCPClientSocket* socket) { |
| 202 if (result != net::OK || socket == NULL) |
| 203 return; |
| 204 |
| 205 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 206 base::Bind(&AdbAttachCommand::OpenDevToolsWindow, this, socket)); |
| 207 } |
| 208 |
| 209 void OpenDevToolsWindow(net::TCPClientSocket* socket) { |
| 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 211 scoped_ptr<net::TCPClientSocket> tcp_socket(socket); |
| 212 // TODO(pfeldman): Show DevToolsWindow here. |
| 213 } |
| 214 |
| 215 scoped_refptr<DevToolsAdbBridge::RemotePage> page_; |
| 216 }; |
34 | 217 |
35 } // namespace | 218 } // namespace |
36 | 219 |
37 DevToolsAdbBridge::AgentHost::AgentHost(const std::string& serial, | 220 DevToolsAdbBridge::RemotePage::RemotePage(const std::string& serial, |
38 const std::string& model, | 221 const std::string& model, |
39 const base::DictionaryValue& value) | 222 const base::DictionaryValue& value) |
40 : serial_(serial), | 223 : serial_(serial), |
41 model_(model) { | 224 model_(model) { |
42 value.GetString("id", &id_); | 225 value.GetString("id", &id_); |
| 226 value.GetString("url", &url_); |
43 value.GetString("title", &title_); | 227 value.GetString("title", &title_); |
44 value.GetString("descirption", &description_); | 228 value.GetString("descirption", &description_); |
45 value.GetString("faviconUrl", &favicon_url_); | 229 value.GetString("faviconUrl", &favicon_url_); |
46 value.GetString("webSocketDebuggerUrl", &debug_url_); | 230 value.GetString("webSocketDebuggerUrl", &debug_url_); |
47 } | 231 value.GetString("devtoolsFrontendUrl", &frontend_url_); |
48 | 232 |
49 DevToolsAdbBridge::AgentHost::~AgentHost() { | 233 if (debug_url_.find("ws://") == 0) |
50 } | 234 debug_url_ = debug_url_.substr(5); |
| 235 else |
| 236 debug_url_ = ""; |
| 237 |
| 238 size_t ws_param = frontend_url_.find("?ws"); |
| 239 if (ws_param != std::string::npos) |
| 240 frontend_url_ = frontend_url_.substr(0, ws_param); |
| 241 } |
| 242 |
| 243 DevToolsAdbBridge::RemotePage::~RemotePage() { |
| 244 } |
| 245 |
| 246 DevToolsAdbBridge::RefCountedAdbThread* |
| 247 DevToolsAdbBridge::RefCountedAdbThread::instance_ = NULL; |
51 | 248 |
52 // static | 249 // static |
53 DevToolsAdbBridge* DevToolsAdbBridge::Start() { | 250 scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> |
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 251 DevToolsAdbBridge::RefCountedAdbThread::GetInstance() { |
55 return new DevToolsAdbBridge(); | 252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
56 } | 253 if (!instance_) |
57 | 254 new RefCountedAdbThread(); |
58 void DevToolsAdbBridge::Stop() { | 255 return instance_; |
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 256 } |
60 if (!thread_.get()) { | 257 |
61 ResetHandlerAndReleaseOnUIThread(); | 258 DevToolsAdbBridge::RefCountedAdbThread::RefCountedAdbThread() { |
62 return; | 259 instance_ = this; |
63 } | 260 thread_ = new base::Thread(kDevToolsAdbBridgeThreadName); |
64 BrowserThread::PostTaskAndReply( | 261 base::Thread::Options options; |
| 262 options.message_loop_type = MessageLoop::TYPE_IO; |
| 263 if (!thread_->StartWithOptions(options)) { |
| 264 delete thread_; |
| 265 thread_ = NULL; |
| 266 } |
| 267 } |
| 268 |
| 269 MessageLoop* DevToolsAdbBridge::RefCountedAdbThread::message_loop() { |
| 270 return thread_ ? thread_->message_loop() : NULL; |
| 271 } |
| 272 |
| 273 // static |
| 274 void DevToolsAdbBridge::RefCountedAdbThread::StopThread(base::Thread* thread) { |
| 275 thread->Stop(); |
| 276 } |
| 277 |
| 278 DevToolsAdbBridge::RefCountedAdbThread::~RefCountedAdbThread() { |
| 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 280 instance_ = NULL; |
| 281 if (!thread_) |
| 282 return; |
| 283 // Shut down thread on FILE thread to join into IO. |
| 284 BrowserThread::PostTask( |
65 BrowserThread::FILE, FROM_HERE, | 285 BrowserThread::FILE, FROM_HERE, |
66 base::Bind(&DevToolsAdbBridge::StopHandlerOnFileThread, | 286 base::Bind(&RefCountedAdbThread::StopThread, thread_)); |
67 base::Unretained(this)), | 287 } |
68 base::Bind(&DevToolsAdbBridge::ResetHandlerAndReleaseOnUIThread, | 288 |
69 base::Unretained(this))); | 289 DevToolsAdbBridge::DevToolsAdbBridge(Profile* profile) |
| 290 : profile_(profile), |
| 291 adb_thread_(RefCountedAdbThread::GetInstance()), |
| 292 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 293 has_message_loop_(adb_thread_->message_loop() != NULL) { |
| 294 } |
| 295 |
| 296 DevToolsAdbBridge::~DevToolsAdbBridge() { |
| 297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
70 } | 298 } |
71 | 299 |
72 void DevToolsAdbBridge::Query( | 300 void DevToolsAdbBridge::Query( |
73 const std::string query, | 301 const std::string query, |
74 const Callback& callback) { | 302 const Callback& callback) { |
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
76 | 304 if (!has_message_loop_) { |
77 // There is a race condition in case Query immediately follows start. We | 305 callback.Run(net::ERR_FAILED, "Could not start ADB thread"); |
78 // consider it Ok since query is polling anyways. | 306 return; |
79 if (!thread_.get()) { | 307 } |
80 callback.Run(net::ERR_FAILED, "ADB is not yet connected"); | 308 scoped_refptr<AdbQueryCommand> command(new AdbQueryCommand(query, callback)); |
81 return; | 309 adb_thread_->message_loop()->PostTask(FROM_HERE, |
82 } | 310 base::Bind(&AdbQueryCommand::Run, command)); |
83 thread_->message_loop()->PostTask( | 311 } |
| 312 |
| 313 void DevToolsAdbBridge::Pages(const PagesCallback& callback) { |
| 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 315 if (!has_message_loop_) |
| 316 return; |
| 317 |
| 318 scoped_refptr<AdbPagesCommand> command(new AdbPagesCommand(callback)); |
| 319 adb_thread_->message_loop()->PostTask(FROM_HERE, |
| 320 base::Bind(&AdbPagesCommand::Run, command)); |
| 321 } |
| 322 |
| 323 void DevToolsAdbBridge::Attach(scoped_refptr<RemotePage> page) { |
| 324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 325 if (!has_message_loop_) |
| 326 return; |
| 327 |
| 328 scoped_refptr<AdbAttachCommand> command(new AdbAttachCommand(page)); |
| 329 adb_thread_->message_loop()->PostTask( |
84 FROM_HERE, | 330 FROM_HERE, |
85 base::Bind(&DevToolsAdbBridge::QueryOnHandlerThread, | 331 base::Bind(&AdbAttachCommand::Run, command)); |
86 base::Unretained(this), query, callback)); | 332 } |
87 } | |
88 | |
89 void DevToolsAdbBridge::Devices() { | |
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
91 if (!thread_.get()) | |
92 return; | |
93 | |
94 thread_->message_loop()->PostTask( | |
95 FROM_HERE, | |
96 base::Bind(&DevToolsAdbBridge::DevicesOnHandlerThread, | |
97 base::Unretained(this), | |
98 base::Bind(&DevToolsAdbBridge::PrintHosts, | |
99 base::Unretained(this)))); | |
100 } | |
101 | |
102 DevToolsAdbBridge::DevToolsAdbBridge() { | |
103 thread_.reset(new base::Thread(kDevToolsAdbBridgeThreadName)); | |
104 | |
105 base::Thread::Options options; | |
106 options.message_loop_type = MessageLoop::TYPE_IO; | |
107 if (!thread_->StartWithOptions(options)) | |
108 thread_.reset(); | |
109 } | |
110 | |
111 DevToolsAdbBridge::~DevToolsAdbBridge() { | |
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
113 // Stop() must be called prior to destruction. | |
114 DCHECK(thread_.get() == NULL); | |
115 } | |
116 | |
117 // Runs on FILE thread to make sure that it is serialized against | |
118 // {Start|Stop}HandlerThread and to allow calling pthread_join. | |
119 void DevToolsAdbBridge::StopHandlerOnFileThread() { | |
120 if (!thread_->message_loop()) | |
121 return; | |
122 // Thread::Stop joins the thread. | |
123 thread_->Stop(); | |
124 } | |
125 | |
126 void DevToolsAdbBridge::ResetHandlerAndReleaseOnUIThread() { | |
127 ResetHandlerOnUIThread(); | |
128 delete this; | |
129 } | |
130 | |
131 void DevToolsAdbBridge::ResetHandlerOnUIThread() { | |
132 thread_.reset(); | |
133 } | |
134 | |
135 void DevToolsAdbBridge::QueryOnHandlerThread( | |
136 const std::string query, | |
137 const Callback& callback) { | |
138 AdbClientSocket::AdbQuery(kAdbPort, query, | |
139 base::Bind(&DevToolsAdbBridge::QueryResponseOnHandlerThread, | |
140 base::Unretained(this), callback)); | |
141 } | |
142 | |
143 void DevToolsAdbBridge::QueryResponseOnHandlerThread( | |
144 const Callback& callback, | |
145 int result, | |
146 const std::string& response) { | |
147 BrowserThread::PostTask( | |
148 BrowserThread::UI, FROM_HERE, | |
149 base::Bind(&DevToolsAdbBridge::RespondOnUIThread, base::Unretained(this), | |
150 callback, result, response)); | |
151 } | |
152 | |
153 void DevToolsAdbBridge::DevicesOnHandlerThread( | |
154 const HostsCallback& callback) { | |
155 AdbClientSocket::AdbQuery( | |
156 kAdbPort, kHostDevicesCommand, | |
157 base::Bind(&DevToolsAdbBridge::ReceivedDevices, | |
158 base::Unretained(this), callback)); | |
159 } | |
160 | |
161 void DevToolsAdbBridge::ReceivedDevices( | |
162 const HostsCallback& callback, | |
163 int result, | |
164 const std::string& response) { | |
165 AgentHosts* hosts = new AgentHosts(); | |
166 if (result != net::OK) { | |
167 callback.Run(result, hosts); | |
168 return; | |
169 } | |
170 | |
171 std::vector<std::string> devices; | |
172 Tokenize(response, "\n", &devices); | |
173 std::vector<std::string>* serials = new std::vector<std::string>(); | |
174 for (size_t i = 0; i < devices.size(); ++i) { | |
175 std::vector<std::string> tokens; | |
176 Tokenize(devices[i], "\t ", &tokens); | |
177 std::string serial = tokens[0]; | |
178 serials->push_back(serial); | |
179 } | |
180 | |
181 ProcessSerials(callback, hosts, serials); | |
182 } | |
183 | |
184 void DevToolsAdbBridge::ProcessSerials( | |
185 const HostsCallback& callback, | |
186 AgentHosts* hosts, | |
187 std::vector<std::string>* serials) { | |
188 if (serials->size() == 0) { | |
189 delete serials; | |
190 callback.Run(net::OK, hosts); | |
191 return; | |
192 } | |
193 | |
194 AdbClientSocket::AdbQuery( | |
195 kAdbPort, | |
196 base::StringPrintf(kDeviceModelCommand, serials->back().c_str()), | |
197 base::Bind(&DevToolsAdbBridge::ReceivedModel, base::Unretained(this), | |
198 callback, hosts, serials)); | |
199 } | |
200 | |
201 void DevToolsAdbBridge::ReceivedModel(const HostsCallback& callback, | |
202 AgentHosts* hosts, | |
203 std::vector<std::string>* serials, | |
204 int result, | |
205 const std::string& response) { | |
206 if (result != net::OK) { | |
207 serials->pop_back(); | |
208 ProcessSerials(callback, hosts, serials); | |
209 return; | |
210 } | |
211 | |
212 AdbClientSocket::HttpQuery( | |
213 kAdbPort, serials->back(), kDevToolsChannelName, kPageListQuery, | |
214 base::Bind(&DevToolsAdbBridge::ReceivedPages, base::Unretained(this), | |
215 callback, hosts, serials, response)); | |
216 | |
217 } | |
218 | |
219 void DevToolsAdbBridge::ReceivedPages(const HostsCallback& callback, | |
220 AgentHosts* hosts, | |
221 std::vector<std::string>* serials, | |
222 const std::string& model, | |
223 int result, | |
224 const std::string& response) { | |
225 std::string serial = serials->back(); | |
226 serials->pop_back(); | |
227 if (result != net::OK) { | |
228 ProcessSerials(callback, hosts, serials); | |
229 return; | |
230 } | |
231 | |
232 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); | |
233 base::ListValue* list_value; | |
234 if (!value || !value->GetAsList(&list_value)) { | |
235 ProcessSerials(callback, hosts, serials); | |
236 return; | |
237 } | |
238 | |
239 base::Value* item; | |
240 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
241 list_value->Get(i, &item); | |
242 base::DictionaryValue* dict; | |
243 if (!item || !item->GetAsDictionary(&dict)) | |
244 continue; | |
245 scoped_refptr<AgentHost> host = new AgentHost(serial, model, *dict); | |
246 hosts->push_back(host); | |
247 } | |
248 ProcessSerials(callback, hosts, serials); | |
249 } | |
250 | |
251 void DevToolsAdbBridge::RespondOnUIThread(const Callback& callback, | |
252 int result, | |
253 const std::string& response) { | |
254 callback.Run(result, response); | |
255 } | |
256 | |
257 void DevToolsAdbBridge::PrintHosts(int result, AgentHosts* hosts) { | |
258 for (AgentHosts::iterator it = hosts->begin(); it != hosts->end(); ++it) { | |
259 AgentHost* host = it->get(); | |
260 fprintf(stderr, "HOST %s %s %s %s %s %s %s\n", host->serial().c_str(), | |
261 host->model().c_str(), host->id().c_str(), host->title().c_str(), | |
262 host->description().c_str(), host->favicon_url().c_str(), | |
263 host->debug_url().c_str()); | |
264 } | |
265 } | |
OLD | NEW |