• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/devtools/devtools_adb_bridge.h"
6 
7 #include <map>
8 #include <vector>
9 
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/json/json_reader.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/memory/singleton.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread.h"
24 #include "base/values.h"
25 #include "chrome/browser/devtools/adb/android_rsa.h"
26 #include "chrome/browser/devtools/adb_client_socket.h"
27 #include "chrome/browser/devtools/adb_web_socket.h"
28 #include "chrome/browser/devtools/devtools_protocol.h"
29 #include "chrome/browser/devtools/devtools_target_impl.h"
30 #include "chrome/browser/devtools/devtools_window.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
33 #include "content/public/browser/devtools_agent_host.h"
34 #include "content/public/browser/devtools_client_host.h"
35 #include "content/public/browser/devtools_external_agent_proxy.h"
36 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
37 #include "content/public/browser/devtools_manager.h"
38 #include "content/public/browser/user_metrics.h"
39 #include "crypto/rsa_private_key.h"
40 #include "net/base/escape.h"
41 #include "net/base/net_errors.h"
42 
43 using content::BrowserThread;
44 
45 namespace {
46 
47 const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
48 const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
49 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
50 const char kListProcessesCommand[] = "shell:ps";
51 const char kDumpsysCommand[] = "shell:dumpsys window policy";
52 const char kDumpsysScreenSizePrefix[] = "mStable=";
53 
54 const char kUnknownModel[] = "Offline";
55 
56 const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
57 const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
58 const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
59 const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
60 const char kNewPageRequestWithURL[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
61 const char kActivatePageRequest[] =
62     "GET /json/activate/%s HTTP/1.1\r\n\r\n";
63 const int kAdbPollingIntervalMs = 1000;
64 
65 const char kUrlParam[] = "url";
66 const char kPageReloadCommand[] = "Page.reload";
67 const char kPageNavigateCommand[] = "Page.navigate";
68 
69 const char kChromeDefaultName[] = "Chrome";
70 const char kChromeDefaultActivity[] = "com.google.android.apps.chrome.Main";
71 const char kChromeDefaultSocket[] = "chrome_devtools_remote";
72 const int kMinVersionNewWithURL = 32;
73 const int kNewPageNavigateDelayMs = 500;
74 
75 const char kWebViewSocketPrefix[] = "webview_devtools_remote";
76 const char kWebViewNameTemplate[] = "WebView in %s";
77 
78 #if defined(DEBUG_DEVTOOLS)
79 const char kLocalChrome[] = "Local Chrome";
80 #endif  // defined(DEBUG_DEVTOOLS)
81 
82 typedef DevToolsAdbBridge::Callback Callback;
83 typedef std::vector<scoped_refptr<AndroidDevice> >
84     AndroidDevices;
85 typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;
86 
87 
88 struct BrowserDescriptor {
89   const char* package;
90   const char* launch_activity;
91   const char* socket;
92   const char* display_name;
93 };
94 
95 const BrowserDescriptor kBrowserDescriptors[] = {
96   {
97     "com.android.chrome",
98     kChromeDefaultActivity,
99     kChromeDefaultSocket,
100     kChromeDefaultName
101   },
102   {
103     "com.chrome.beta",
104     kChromeDefaultActivity,
105     kChromeDefaultSocket,
106     "Chrome Beta"
107   },
108   {
109     "com.google.android.apps.chrome_dev",
110     kChromeDefaultActivity,
111     kChromeDefaultSocket,
112     "Chrome Dev"
113   },
114   {
115     "com.google.android.apps.chrome",
116     kChromeDefaultActivity,
117     kChromeDefaultSocket,
118     "Chromium"
119   },
120   {
121     "org.chromium.content_shell_apk",
122     "org.chromium.content_shell_apk.ContentShellActivity",
123     "content_shell_devtools_remote",
124     "Content Shell"
125   },
126   {
127     "org.chromium.chrome.testshell",
128     "org.chromium.chrome.testshell.ChromiumTestShellActivity",
129     "chromium_testshell_devtools_remote",
130     "Chromium Test Shell"
131   },
132   {
133     "org.chromium.android_webview.shell",
134     "org.chromium.android_webview.shell.AwShellActivity",
135     "webview_devtools_remote",
136     "WebView Test Shell"
137   }
138 };
139 
FindBrowserDescriptor(const std::string & package)140 const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
141   int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
142   for (int i = 0; i < count; i++)
143     if (kBrowserDescriptors[i].package == package)
144       return &kBrowserDescriptors[i];
145   return NULL;
146 }
147 
148 typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
149 
FindInstalledBrowserPackages(const std::string & response)150 static DescriptorMap FindInstalledBrowserPackages(
151     const std::string& response) {
152   // Parse 'pm list packages' output which on Android looks like this:
153   //
154   // package:com.android.chrome
155   // package:com.chrome.beta
156   // package:com.example.app
157   //
158   DescriptorMap package_to_descriptor;
159   const std::string package_prefix = "package:";
160   std::vector<std::string> entries;
161   Tokenize(response, "'\r\n", &entries);
162   for (size_t i = 0; i < entries.size(); ++i) {
163     if (entries[i].find(package_prefix) != 0)
164       continue;
165     std::string package = entries[i].substr(package_prefix.size());
166     const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
167     if (!descriptor)
168       continue;
169     package_to_descriptor[descriptor->package] = descriptor;
170   }
171   return package_to_descriptor;
172 }
173 
174 typedef std::map<std::string, std::string> StringMap;
175 
MapProcessesToPackages(const std::string & response,StringMap & pid_to_package,StringMap & package_to_pid)176 static void MapProcessesToPackages(const std::string& response,
177                                    StringMap& pid_to_package,
178                                    StringMap& package_to_pid) {
179   // Parse 'ps' output which on Android looks like this:
180   //
181   // USER PID PPID VSIZE RSS WCHAN PC ? NAME
182   //
183   std::vector<std::string> entries;
184   Tokenize(response, "\n", &entries);
185   for (size_t i = 1; i < entries.size(); ++i) {
186     std::vector<std::string> fields;
187     Tokenize(entries[i], " \r", &fields);
188     if (fields.size() < 9)
189       continue;
190     std::string pid = fields[1];
191     std::string package = fields[8];
192     pid_to_package[pid] = package;
193     package_to_pid[package] = pid;
194   }
195 }
196 
197 typedef std::map<std::string,
198                  scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > BrowserMap;
199 
MapSocketsToProcesses(const std::string & response,const std::string & channel_pattern)200 static StringMap MapSocketsToProcesses(const std::string& response,
201                                        const std::string& channel_pattern) {
202   // Parse 'cat /proc/net/unix' output which on Android looks like this:
203   //
204   // Num       RefCount Protocol Flags    Type St Inode Path
205   // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
206   // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
207   // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
208   //
209   // We need to find records with paths starting from '@' (abstract socket)
210   // and containing the channel pattern ("_devtools_remote").
211   StringMap socket_to_pid;
212   std::vector<std::string> entries;
213   Tokenize(response, "\n", &entries);
214   for (size_t i = 1; i < entries.size(); ++i) {
215     std::vector<std::string> fields;
216     Tokenize(entries[i], " \r", &fields);
217     if (fields.size() < 8)
218       continue;
219     if (fields[3] != "00010000" || fields[5] != "01")
220       continue;
221     std::string path_field = fields[7];
222     if (path_field.size() < 1 || path_field[0] != '@')
223       continue;
224     size_t socket_name_pos = path_field.find(channel_pattern);
225     if (socket_name_pos == std::string::npos)
226       continue;
227 
228     std::string socket = path_field.substr(1);
229 
230     std::string pid;
231     size_t socket_name_end = socket_name_pos + channel_pattern.size();
232     if (socket_name_end < path_field.size() &&
233         path_field[socket_name_end] == '_') {
234       pid = path_field.substr(socket_name_end + 1);
235     }
236     socket_to_pid[socket] = pid;
237   }
238   return socket_to_pid;
239 }
240 
241 // AdbPagesCommand ------------------------------------------------------------
242 
243 class AdbPagesCommand : public base::RefCountedThreadSafe<
244     AdbPagesCommand,
245     BrowserThread::DeleteOnUIThread> {
246  public:
247   typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;
248 
249   AdbPagesCommand(
250       scoped_refptr<RefCountedAdbThread> adb_thread,
251       const DevToolsAdbBridge::DeviceProviders& device_providers,
252       const Callback& callback);
253 
254  private:
255   friend struct BrowserThread::DeleteOnThread<
256       BrowserThread::UI>;
257   friend class base::DeleteHelper<AdbPagesCommand>;
258 
259   virtual ~AdbPagesCommand();
260   void ProcessDeviceProviders();
261   void ReceivedDevices(const AndroidDevices& devices);
262 
263   void ProcessSerials();
264   void ReceivedModel(int result, const std::string& response);
265   void ReceivedDumpsys(int result, const std::string& response);
266   void ReceivedPackages(int result, const std::string& response);
267   void ReceivedProcesses(
268       const std::string& packages_response,
269       int result,
270       const std::string& processes_response);
271   void ReceivedSockets(
272       const std::string& packages_response,
273       const std::string& processes_response,
274       int result,
275       const std::string& sockets_response);
276   void ProcessSockets();
277   void ReceivedVersion(int result, const std::string& response);
278   void ReceivedPages(int result, const std::string& response);
279 
current_device() const280   scoped_refptr<AndroidDevice> current_device() const {
281     return devices_.back();
282   }
283 
current_browser() const284   scoped_refptr<DevToolsAdbBridge::RemoteBrowser> current_browser() const {
285     return browsers_.back();
286   }
287 
288   void NextBrowser();
289   void NextDevice();
290 
291   void Respond();
292 
293   void CreateBrowsers(const std::string& packages_response,
294                       const std::string& processes_response,
295                       const std::string& sockets_response);
296 
297   void ParseDumpsysResponse(const std::string& response);
298   void ParseScreenSize(const std::string& str);
299 
300   scoped_refptr<RefCountedAdbThread> adb_thread_;
301   Callback callback_;
302   AndroidDevices devices_;
303   DevToolsAdbBridge::RemoteBrowsers browsers_;
304   scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
305   DevToolsAdbBridge::DeviceProviders device_providers_;
306 };
307 
AdbPagesCommand(scoped_refptr<RefCountedAdbThread> adb_thread,const DevToolsAdbBridge::DeviceProviders & device_providers,const Callback & callback)308 AdbPagesCommand::AdbPagesCommand(
309     scoped_refptr<RefCountedAdbThread> adb_thread,
310     const DevToolsAdbBridge::DeviceProviders& device_providers,
311     const Callback& callback)
312     : adb_thread_(adb_thread),
313       callback_(callback),
314       device_providers_(device_providers){
315   remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());
316 
317   ProcessDeviceProviders();
318 }
319 
~AdbPagesCommand()320 AdbPagesCommand::~AdbPagesCommand() {
321   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 }
323 
ProcessDeviceProviders()324 void AdbPagesCommand::ProcessDeviceProviders() {
325   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
326   if (device_providers_.empty()) {
327     adb_thread_->message_loop()->PostTask(
328               FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
329     return;
330   }
331 
332   const scoped_refptr<AndroidDeviceProvider>& device_provider =
333       device_providers_.back();
334 
335   device_provider->QueryDevices(
336       base::Bind(&AdbPagesCommand::ReceivedDevices, this));
337 }
338 
ReceivedDevices(const AndroidDevices & devices)339 void AdbPagesCommand::ReceivedDevices(const AndroidDevices& devices) {
340   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341   DCHECK(!device_providers_.empty());
342   device_providers_.pop_back();
343 
344   devices_.insert(devices_.end(), devices.begin(), devices.end());
345 
346   if (!device_providers_.empty()) {
347     ProcessDeviceProviders();
348   } else {
349     adb_thread_->message_loop()->PostTask(
350           FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
351   }
352 }
353 
ProcessSerials()354 void AdbPagesCommand::ProcessSerials() {
355   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
356   if (devices_.size() == 0) {
357     BrowserThread::PostTask(
358         BrowserThread::UI, FROM_HERE,
359         base::Bind(&AdbPagesCommand::Respond, this));
360     return;
361   }
362 
363   scoped_refptr<AndroidDevice> device = current_device();
364 #if defined(DEBUG_DEVTOOLS)
365   // For desktop remote debugging.
366   if (device->serial().empty()) {
367     device->set_model(kLocalChrome);
368     remote_devices_->push_back(
369         new DevToolsAdbBridge::RemoteDevice(device));
370     scoped_refptr<DevToolsAdbBridge::RemoteBrowser> remote_browser =
371         new DevToolsAdbBridge::RemoteBrowser(
372             adb_thread_, device, std::string());
373     remote_browser->set_display_name(kChromeDefaultName);
374     remote_devices_->back()->AddBrowser(remote_browser);
375     browsers_.push_back(remote_browser);
376     device->HttpQuery(
377         std::string(), kVersionRequest,
378         base::Bind(&AdbPagesCommand::ReceivedVersion, this));
379     return;
380   }
381 #endif  // defined(DEBUG_DEVTOOLS)
382 
383   if (device->is_connected()) {
384     device->RunCommand(kDeviceModelCommand,
385                        base::Bind(&AdbPagesCommand::ReceivedModel, this));
386   } else {
387     device->set_model(kUnknownModel);
388     remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
389     NextDevice();
390   }
391 }
392 
ReceivedModel(int result,const std::string & response)393 void AdbPagesCommand::ReceivedModel(int result, const std::string& response) {
394   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
395   if (result < 0) {
396     NextDevice();
397     return;
398   }
399   scoped_refptr<AndroidDevice> device = current_device();
400   device->set_model(response);
401   remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
402   device->RunCommand(kDumpsysCommand,
403                      base::Bind(&AdbPagesCommand::ReceivedDumpsys, this));
404 }
405 
ReceivedDumpsys(int result,const std::string & response)406 void AdbPagesCommand::ReceivedDumpsys(int result,
407                                       const std::string& response) {
408   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
409   if (result >= 0)
410     ParseDumpsysResponse(response);
411 
412   current_device()->RunCommand(
413       kInstalledChromePackagesCommand,
414       base::Bind(&AdbPagesCommand::ReceivedPackages, this));
415 }
416 
ReceivedPackages(int result,const std::string & packages_response)417 void AdbPagesCommand::ReceivedPackages(int result,
418                                        const std::string& packages_response) {
419   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
420   if (result < 0) {
421     NextDevice();
422     return;
423   }
424   current_device()->RunCommand(
425       kListProcessesCommand,
426       base::Bind(&AdbPagesCommand::ReceivedProcesses, this, packages_response));
427 }
428 
ReceivedProcesses(const std::string & packages_response,int result,const std::string & processes_response)429 void AdbPagesCommand::ReceivedProcesses(
430     const std::string& packages_response,
431     int result,
432     const std::string& processes_response) {
433   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
434   if (result < 0) {
435     NextDevice();
436     return;
437   }
438   current_device()->RunCommand(
439       kOpenedUnixSocketsCommand,
440       base::Bind(&AdbPagesCommand::ReceivedSockets,
441                  this,
442                  packages_response,
443                  processes_response));
444 }
445 
ReceivedSockets(const std::string & packages_response,const std::string & processes_response,int result,const std::string & sockets_response)446 void AdbPagesCommand::ReceivedSockets(
447     const std::string& packages_response,
448     const std::string& processes_response,
449     int result,
450     const std::string& sockets_response) {
451   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
452   if (result >= 0)
453     CreateBrowsers(packages_response, processes_response, sockets_response);
454   ProcessSockets();
455 }
456 
ProcessSockets()457 void AdbPagesCommand::ProcessSockets() {
458   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
459   if (browsers_.size() == 0) {
460     NextDevice();
461     return;
462   }
463 
464   if (!current_device()->serial().empty() &&
465      current_browser()->socket().empty()) {
466     NextBrowser();
467     return;
468   }
469   current_device()->HttpQuery(
470       current_browser()->socket(),
471       kVersionRequest,
472       base::Bind(&AdbPagesCommand::ReceivedVersion, this));
473 }
474 
ReceivedVersion(int result,const std::string & response)475 void AdbPagesCommand::ReceivedVersion(int result,
476                                       const std::string& response) {
477   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
478   if (result < 0) {
479     NextBrowser();
480     return;
481   }
482 
483   // Parse version, append to package name if available,
484   scoped_ptr<base::Value> value(base::JSONReader::Read(response));
485   base::DictionaryValue* dict;
486   if (value && value->GetAsDictionary(&dict)) {
487     std::string browser;
488     if (dict->GetString("Browser", &browser)) {
489       std::vector<std::string> parts;
490       Tokenize(browser, "/", &parts);
491       if (parts.size() == 2)
492         current_browser()->set_version(parts[1]);
493       else
494         current_browser()->set_version(browser);
495     }
496     std::string package;
497     if (dict->GetString("Android-Package", &package)) {
498       const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
499       if (descriptor)
500         current_browser()->set_display_name(descriptor->display_name);
501     }
502   }
503 
504   current_device()->HttpQuery(
505       current_browser()->socket(),
506       kPageListRequest,
507       base::Bind(&AdbPagesCommand::ReceivedPages, this));
508 }
509 
ReceivedPages(int result,const std::string & response)510 void AdbPagesCommand::ReceivedPages(int result,
511                                     const std::string& response) {
512   DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
513   if (result >= 0) {
514     scoped_ptr<base::Value> value(base::JSONReader::Read(response));
515     base::ListValue* list_value;
516     if (value && value->GetAsList(&list_value))
517       current_browser()->SetPageDescriptors(*list_value);
518   }
519   NextBrowser();
520 }
521 
NextBrowser()522 void AdbPagesCommand::NextBrowser() {
523   browsers_.pop_back();
524   ProcessSockets();
525 }
526 
NextDevice()527 void AdbPagesCommand::NextDevice() {
528   devices_.pop_back();
529   ProcessSerials();
530 }
531 
Respond()532 void AdbPagesCommand::Respond() {
533   callback_.Run(remote_devices_.release());
534 }
535 
CreateBrowsers(const std::string & packages_response,const std::string & processes_response,const std::string & sockets_response)536 void AdbPagesCommand::CreateBrowsers(
537     const std::string& packages_response,
538     const std::string& processes_response,
539     const std::string& sockets_response) {
540   DescriptorMap package_to_descriptor =
541       FindInstalledBrowserPackages(packages_response);
542 
543   StringMap pid_to_package;
544   StringMap package_to_pid;
545   MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
546 
547   const std::string channel_pattern =
548       base::StringPrintf(kDevToolsChannelNameFormat, "");
549 
550   StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
551                                                   channel_pattern);
552 
553   scoped_refptr<DevToolsAdbBridge::RemoteDevice> remote_device =
554       remote_devices_->back();
555 
556   // Create RemoteBrowser instances.
557   BrowserMap package_to_running_browser;
558   BrowserMap socket_to_unnamed_browser;
559   for (StringMap::iterator it = socket_to_pid.begin();
560       it != socket_to_pid.end(); ++it) {
561     std::string socket = it->first;
562     std::string pid = it->second;
563 
564     scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser =
565         new DevToolsAdbBridge::RemoteBrowser(
566             adb_thread_, remote_device->device(), socket);
567 
568     StringMap::iterator pit = pid_to_package.find(pid);
569     if (pit != pid_to_package.end()) {
570       std::string package = pit->second;
571       package_to_running_browser[package] = browser;
572       const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
573       if (descriptor) {
574         browser->set_display_name(descriptor->display_name);
575       } else if (socket.find(kWebViewSocketPrefix) == 0) {
576         browser->set_display_name(
577             base::StringPrintf(kWebViewNameTemplate, package.c_str()));
578       } else {
579         browser->set_display_name(package);
580       }
581     } else {
582       // Set fallback display name.
583       std::string name = socket.substr(0, socket.find(channel_pattern));
584       name[0] = base::ToUpperASCII(name[0]);
585       browser->set_display_name(name);
586 
587       socket_to_unnamed_browser[socket] = browser;
588     }
589     remote_device->AddBrowser(browser);
590   }
591 
592   browsers_ = remote_device->browsers();
593 
594   // Find installed packages not mapped to browsers.
595   typedef std::multimap<std::string, const BrowserDescriptor*>
596       DescriptorMultimap;
597   DescriptorMultimap socket_to_descriptor;
598   for (DescriptorMap::iterator it = package_to_descriptor.begin();
599       it != package_to_descriptor.end(); ++it) {
600     std::string package = it->first;
601     const BrowserDescriptor* descriptor = it->second;
602 
603     if (package_to_running_browser.find(package) !=
604         package_to_running_browser.end())
605       continue;  // This package is already mapped to a browser.
606 
607     if (package_to_pid.find(package) != package_to_pid.end()) {
608       // This package is running but not mapped to a browser.
609       socket_to_descriptor.insert(
610           DescriptorMultimap::value_type(descriptor->socket, descriptor));
611       continue;
612     }
613   }
614 
615   // Try naming remaining unnamed browsers.
616   for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
617       it != socket_to_descriptor.end(); ++it) {
618     std::string socket = it->first;
619     const BrowserDescriptor* descriptor = it->second;
620 
621     if (socket_to_descriptor.count(socket) != 1)
622       continue;  // No definitive match.
623 
624     BrowserMap::iterator bit = socket_to_unnamed_browser.find(socket);
625     if (bit != socket_to_unnamed_browser.end())
626       bit->second->set_display_name(descriptor->display_name);
627   }
628 }
629 
ParseDumpsysResponse(const std::string & response)630 void AdbPagesCommand::ParseDumpsysResponse(const std::string& response) {
631   std::vector<std::string> lines;
632   Tokenize(response, "\r", &lines);
633   for (size_t i = 0; i < lines.size(); ++i) {
634     std::string line = lines[i];
635     size_t pos = line.find(kDumpsysScreenSizePrefix);
636     if (pos != std::string::npos) {
637       ParseScreenSize(
638           line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
639       break;
640     }
641   }
642 }
643 
ParseScreenSize(const std::string & str)644 void AdbPagesCommand::ParseScreenSize(const std::string& str) {
645   std::vector<std::string> pairs;
646   Tokenize(str, "-", &pairs);
647   if (pairs.size() != 2)
648     return;
649 
650   int width;
651   int height;
652   std::vector<std::string> numbers;
653   Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
654   if (numbers.size() != 2 ||
655       !base::StringToInt(numbers[0], &width) ||
656       !base::StringToInt(numbers[1], &height))
657     return;
658 
659   remote_devices_->back()->set_screen_size(gfx::Size(width, height));
660 }
661 
662 
663 // AdbProtocolCommand ---------------------------------------------------------
664 
665 class AdbProtocolCommand : public AdbWebSocket::Delegate {
666  public:
667   AdbProtocolCommand(
668       scoped_refptr<RefCountedAdbThread> adb_thread,
669       scoped_refptr<AndroidDevice> device,
670       const std::string& socket_name,
671       const std::string& debug_url,
672       const std::string& command);
673 
674  private:
675   virtual void OnSocketOpened() OVERRIDE;
676   virtual void OnFrameRead(const std::string& message) OVERRIDE;
677   virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
678   virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
679 
680   scoped_refptr<RefCountedAdbThread> adb_thread_;
681   const std::string command_;
682   scoped_refptr<AdbWebSocket> web_socket_;
683 
684   DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand);
685 };
686 
AdbProtocolCommand(scoped_refptr<RefCountedAdbThread> adb_thread,scoped_refptr<AndroidDevice> device,const std::string & socket_name,const std::string & debug_url,const std::string & command)687 AdbProtocolCommand::AdbProtocolCommand(
688     scoped_refptr<RefCountedAdbThread> adb_thread,
689     scoped_refptr<AndroidDevice> device,
690     const std::string& socket_name,
691     const std::string& debug_url,
692     const std::string& command)
693     : adb_thread_(adb_thread),
694       command_(command) {
695   web_socket_ = new AdbWebSocket(
696       device, socket_name, debug_url, adb_thread_->message_loop(), this);
697 }
698 
OnSocketOpened()699 void AdbProtocolCommand::OnSocketOpened() {
700   web_socket_->SendFrame(command_);
701   web_socket_->Disconnect();
702 }
703 
OnFrameRead(const std::string & message)704 void AdbProtocolCommand::OnFrameRead(const std::string& message) {}
705 
OnSocketClosed(bool closed_by_device)706 void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
707   delete this;
708 }
709 
ProcessIncomingMessage(const std::string & message)710 bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
711   return false;
712 }
713 
714 }  // namespace
715 
716 const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
717 
718 class AgentHostDelegate;
719 
720 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
721 
722 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
723     LAZY_INSTANCE_INITIALIZER;
724 
Wrapper()725 DevToolsAdbBridge::Wrapper::Wrapper() {
726   bridge_ = new DevToolsAdbBridge();
727 }
728 
~Wrapper()729 DevToolsAdbBridge::Wrapper::~Wrapper() {
730 }
731 
Get()732 DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
733   return bridge_.get();
734 }
735 
736 // static
GetInstance()737 DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
738   return Singleton<DevToolsAdbBridge::Factory>::get();
739 }
740 
741 // static
GetForProfile(Profile * profile)742 DevToolsAdbBridge* DevToolsAdbBridge::Factory::GetForProfile(
743     Profile* profile) {
744   DevToolsAdbBridge::Wrapper* wrapper =
745       static_cast<DevToolsAdbBridge::Wrapper*>(GetInstance()->
746           GetServiceForBrowserContext(profile, true));
747   return wrapper ? wrapper->Get() : NULL;
748 }
749 
Factory()750 DevToolsAdbBridge::Factory::Factory()
751     : BrowserContextKeyedServiceFactory(
752           "DevToolsAdbBridge",
753           BrowserContextDependencyManager::GetInstance()) {}
754 
~Factory()755 DevToolsAdbBridge::Factory::~Factory() {}
756 
757 BrowserContextKeyedService*
BuildServiceInstanceFor(content::BrowserContext * context) const758 DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
759     content::BrowserContext* context) const {
760   return new DevToolsAdbBridge::Wrapper();
761 }
762 
763 
764 // AgentHostDelegate ----------------------------------------------------------
765 
766 class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
767                           public AdbWebSocket::Delegate {
768  public:
Create(const std::string & id,scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,const std::string & debug_url,const std::string & frontend_url,Profile * profile)769    static void Create(const std::string& id,
770                       scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
771                       const std::string& debug_url,
772                       const std::string& frontend_url,
773                       Profile* profile) {
774     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
775     AgentHostDelegates::iterator it =
776         g_host_delegates.Get().find(id);
777     if (it != g_host_delegates.Get().end()) {
778       it->second->OpenFrontend();
779     } else if (!frontend_url.empty()) {
780       new AgentHostDelegate(
781           id, browser->device(), browser->socket(), debug_url,
782           frontend_url, browser->adb_thread()->message_loop(), profile);
783     }
784   }
785 
786  private:
AgentHostDelegate(const std::string & id,scoped_refptr<AndroidDevice> device,const std::string & socket_name,const std::string & debug_url,const std::string & frontend_url,base::MessageLoop * adb_message_loop,Profile * profile)787   AgentHostDelegate(
788       const std::string& id,
789       scoped_refptr<AndroidDevice> device,
790       const std::string& socket_name,
791       const std::string& debug_url,
792       const std::string& frontend_url,
793       base::MessageLoop* adb_message_loop,
794       Profile* profile)
795       : id_(id),
796         frontend_url_(frontend_url),
797         adb_message_loop_(adb_message_loop),
798         profile_(profile) {
799     web_socket_ = new AdbWebSocket(
800         device, socket_name, debug_url, adb_message_loop, this);
801     g_host_delegates.Get()[id] = this;
802 
803     if (socket_name.find(kWebViewSocketPrefix) == 0) {
804       content::RecordAction(
805           content::UserMetricsAction("DevTools_InspectAndroidWebView"));
806     } else {
807       content::RecordAction(
808           content::UserMetricsAction("DevTools_InspectAndroidPage"));
809     }
810   }
811 
OpenFrontend()812   void OpenFrontend() {
813     if (!proxy_)
814       return;
815     DevToolsWindow::OpenExternalFrontend(
816         profile_, frontend_url_, proxy_->GetAgentHost().get());
817   }
818 
~AgentHostDelegate()819   virtual ~AgentHostDelegate() {
820     g_host_delegates.Get().erase(id_);
821   }
822 
Attach()823   virtual void Attach() OVERRIDE {}
824 
Detach()825   virtual void Detach() OVERRIDE {
826     web_socket_->Disconnect();
827   }
828 
SendMessageToBackend(const std::string & message)829   virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
830     web_socket_->SendFrame(message);
831   }
832 
OnSocketOpened()833   virtual void OnSocketOpened() OVERRIDE {
834     proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
835     OpenFrontend();
836   }
837 
OnFrameRead(const std::string & message)838   virtual void OnFrameRead(const std::string& message) OVERRIDE {
839     proxy_->DispatchOnClientHost(message);
840   }
841 
OnSocketClosed(bool closed_by_device)842   virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
843     if (proxy_ && closed_by_device)
844       proxy_->ConnectionClosed();
845     delete this;
846   }
847 
ProcessIncomingMessage(const std::string & message)848   virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE {
849     return false;
850   }
851 
852   const std::string id_;
853   const std::string frontend_url_;
854   base::MessageLoop* adb_message_loop_;
855   Profile* profile_;
856 
857   scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
858   scoped_refptr<AdbWebSocket> web_socket_;
859   DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
860 };
861 
862 //// RemotePageTarget ----------------------------------------------
863 
864 class RemotePageTarget : public DevToolsTargetImpl {
865  public:
866   RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
867                    const base::DictionaryValue& value);
868   virtual ~RemotePageTarget();
869 
870   // content::DevToolsTarget overrides:
871   virtual bool IsAttached() const OVERRIDE;
872   virtual bool Activate() const OVERRIDE;
873   virtual bool Close() const OVERRIDE;
874 
875   // DevToolsTargetImpl overrides:
876   virtual void Inspect(Profile* profile) const OVERRIDE;
877   virtual void Reload() const OVERRIDE;
878 
879   void Navigate(const std::string& url) const;
880 
881  private:
882   scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser_;
883   std::string debug_url_;
884   std::string frontend_url_;
885   std::string agent_id_;
886   DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
887 };
888 
RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,const base::DictionaryValue & value)889 RemotePageTarget::RemotePageTarget(
890     scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
891     const base::DictionaryValue& value)
892     : browser_(browser) {
893   type_ = "adb_page";
894   value.GetString("id", &id_);
895   std::string url;
896   value.GetString("url", &url);
897   url_ = GURL(url);
898   value.GetString("title", &title_);
899   title_ = UTF16ToUTF8(net::UnescapeForHTML(UTF8ToUTF16(title_)));
900   value.GetString("description", &description_);
901   std::string favicon_url;
902   value.GetString("faviconUrl", &favicon_url);
903   favicon_url_ = GURL(favicon_url);
904   value.GetString("webSocketDebuggerUrl", &debug_url_);
905   value.GetString("devtoolsFrontendUrl", &frontend_url_);
906 
907   if (id_.empty() && !debug_url_.empty())  {
908     // Target id is not available until Chrome 26. Use page id at the end of
909     // debug_url_ instead. For attached targets the id will remain empty.
910     std::vector<std::string> parts;
911     Tokenize(debug_url_, "/", &parts);
912     id_ = parts[parts.size()-1];
913   }
914 
915   if (debug_url_.find("ws://") == 0)
916     debug_url_ = debug_url_.substr(5);
917   else
918     debug_url_ = "";
919 
920   size_t ws_param = frontend_url_.find("?ws");
921   if (ws_param != std::string::npos)
922     frontend_url_ = frontend_url_.substr(0, ws_param);
923   if (frontend_url_.find("http:") == 0)
924     frontend_url_ = "https:" + frontend_url_.substr(5);
925 
926   agent_id_ = base::StringPrintf("%s:%s:%s",
927       browser_->device()->serial().c_str(),
928       browser_->socket().c_str(),
929       id_.c_str());
930 }
931 
~RemotePageTarget()932 RemotePageTarget::~RemotePageTarget() {
933 }
934 
IsAttached() const935 bool RemotePageTarget::IsAttached() const {
936   return debug_url_.empty();
937 }
938 
Inspect(Profile * profile) const939 void RemotePageTarget::Inspect(Profile* profile) const {
940   std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
941   base::Closure inspect_callback = base::Bind(&AgentHostDelegate::Create,
942       id_, browser_, debug_url_, frontend_url_, profile);
943   browser_->SendJsonRequest(request, inspect_callback);
944 }
945 
Activate() const946 bool RemotePageTarget::Activate() const {
947   std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
948   browser_->SendJsonRequest(request, base::Closure());
949   return true;
950 }
951 
Close() const952 bool RemotePageTarget::Close() const {
953   if (IsAttached())
954     return false;
955   std::string request = base::StringPrintf(kClosePageRequest, id_.c_str());
956   browser_->SendJsonRequest(request, base::Closure());
957   return true;
958 }
959 
Reload() const960 void RemotePageTarget::Reload() const {
961   browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
962 }
963 
Navigate(const std::string & url) const964 void RemotePageTarget::Navigate(const std::string& url) const {
965   base::DictionaryValue params;
966   params.SetString(kUrlParam, url);
967   browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, &params);
968 }
969 
970 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
971 
RemoteBrowser(scoped_refptr<RefCountedAdbThread> adb_thread,scoped_refptr<AndroidDevice> device,const std::string & socket)972 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
973     scoped_refptr<RefCountedAdbThread> adb_thread,
974     scoped_refptr<AndroidDevice> device,
975     const std::string& socket)
976     : adb_thread_(adb_thread),
977       device_(device),
978       socket_(socket),
979       page_descriptors_(new base::ListValue()) {
980 }
981 
IsChrome() const982 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
983   return socket_.find(kChromeDefaultSocket) == 0;
984 }
985 
986 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
GetParsedVersion() const987 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
988   ParsedVersion result;
989   std::vector<std::string> parts;
990   Tokenize(version_, ".", &parts);
991   for (size_t i = 0; i != parts.size(); ++i) {
992     int value = 0;
993     base::StringToInt(parts[i], &value);
994     result.push_back(value);
995   }
996   return result;
997 }
998 
999 std::vector<DevToolsTargetImpl*>
CreatePageTargets()1000 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
1001   std::vector<DevToolsTargetImpl*> result;
1002   for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
1003     base::Value* item;
1004     page_descriptors_->Get(i, &item);
1005     if (!item)
1006       continue;
1007     base::DictionaryValue* dict;
1008     if (!item->GetAsDictionary(&dict))
1009       continue;
1010     result.push_back(new RemotePageTarget(this, *dict));
1011   }
1012   return result;
1013 }
1014 
SetPageDescriptors(const base::ListValue & list)1015 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
1016     const base::ListValue& list) {
1017   page_descriptors_.reset(list.DeepCopy());
1018 }
1019 
RespondOnUIThread(base::Closure callback,int,const std::string &)1020 static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
1021   if (!callback.is_null())
1022     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
1023 }
1024 
SendJsonRequest(const std::string & request,base::Closure callback)1025 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
1026     const std::string& request, base::Closure callback) {
1027   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1028   adb_thread_->message_loop()->PostTask(FROM_HERE,
1029       base::Bind(&AndroidDevice::HttpQuery, device_, socket_, request,
1030           base::Bind(&RespondOnUIThread, callback)));
1031 }
1032 
SendProtocolCommand(const std::string & debug_url,const std::string & method,base::DictionaryValue * params)1033 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
1034     const std::string& debug_url,
1035     const std::string& method,
1036     base::DictionaryValue* params) {
1037   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1038   if (debug_url.empty())
1039     return;
1040   DevToolsProtocol::Command command(1, method, params);
1041   new AdbProtocolCommand(
1042       adb_thread_, device_, socket_, debug_url, command.Serialize());
1043 }
1044 
NoOp(int,const std::string &)1045 static void NoOp(int, const std::string&) {}
1046 
Open(const std::string & input_url)1047 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string& input_url) {
1048   GURL gurl(input_url);
1049   if (!gurl.is_valid()) {
1050     gurl = GURL("http://" + input_url);
1051     if (!gurl.is_valid())
1052      return;
1053   }
1054   std::string url = gurl.spec();
1055 
1056   ParsedVersion parsed_version = GetParsedVersion();
1057   if (IsChrome() &&
1058       !parsed_version.empty() &&
1059       parsed_version[0] >= kMinVersionNewWithURL) {
1060     std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
1061     std::string request =
1062         base::StringPrintf(kNewPageRequestWithURL, query.c_str());
1063     adb_thread_->message_loop()->PostTask(FROM_HERE,
1064         base::Bind(&AndroidDevice::HttpQuery,
1065             device_, socket_, request, base::Bind(&NoOp)));
1066   } else {
1067     adb_thread_->message_loop()->PostTask(FROM_HERE,
1068         base::Bind(&AndroidDevice::HttpQuery,
1069             device_, socket_, kNewPageRequest,
1070             base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread, this, url)));
1071   }
1072 }
1073 
PageCreatedOnHandlerThread(const std::string & url,int result,const std::string & response)1074 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
1075     const std::string& url, int result, const std::string& response) {
1076   if (result < 0)
1077     return;
1078   // Navigating too soon after the page creation breaks navigation history
1079   // (crbug.com/311014). This can be avoided by adding a moderate delay.
1080   BrowserThread::PostDelayedTask(
1081       BrowserThread::UI, FROM_HERE,
1082       base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, response, url),
1083       base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
1084 }
1085 
PageCreatedOnUIThread(const std::string & response,const std::string & url)1086 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
1087     const std::string& response, const std::string& url) {
1088   scoped_ptr<base::Value> value(base::JSONReader::Read(response));
1089   base::DictionaryValue* dict;
1090   if (value && value->GetAsDictionary(&dict)) {
1091     RemotePageTarget new_page(this, *dict);
1092     new_page.Navigate(url);
1093   }
1094 }
1095 
~RemoteBrowser()1096 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1097 }
1098 
1099 
1100 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
1101 
RemoteDevice(scoped_refptr<AndroidDevice> device)1102 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1103     scoped_refptr<AndroidDevice> device)
1104     : device_(device) {
1105 }
1106 
GetSerial()1107 std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
1108   return device_->serial();
1109 }
1110 
GetModel()1111 std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
1112   return device_->model();
1113 }
1114 
IsConnected()1115 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1116   return device_->is_connected();
1117 }
1118 
AddBrowser(scoped_refptr<RemoteBrowser> browser)1119 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1120     scoped_refptr<RemoteBrowser> browser) {
1121   browsers_.push_back(browser);
1122 }
1123 
~RemoteDevice()1124 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1125 }
1126 
1127 
1128 // DevToolsAdbBridge ----------------------------------------------------------
1129 
DevToolsAdbBridge()1130 DevToolsAdbBridge::DevToolsAdbBridge()
1131     : adb_thread_(RefCountedAdbThread::GetInstance()),
1132       has_message_loop_(adb_thread_->message_loop() != NULL) {
1133 }
1134 
AddListener(Listener * listener)1135 void DevToolsAdbBridge::AddListener(Listener* listener) {
1136   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1137   if (listeners_.empty())
1138     RequestRemoteDevices();
1139   listeners_.push_back(listener);
1140 }
1141 
RemoveListener(Listener * listener)1142 void DevToolsAdbBridge::RemoveListener(Listener* listener) {
1143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1144   Listeners::iterator it =
1145       std::find(listeners_.begin(), listeners_.end(), listener);
1146   DCHECK(it != listeners_.end());
1147   listeners_.erase(it);
1148 }
1149 
HasDevToolsWindow(const std::string & agent_id)1150 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string& agent_id) {
1151   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1152   return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
1153 }
1154 
~DevToolsAdbBridge()1155 DevToolsAdbBridge::~DevToolsAdbBridge() {
1156   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1157   DCHECK(listeners_.empty());
1158 }
1159 
RequestRemoteDevices()1160 void DevToolsAdbBridge::RequestRemoteDevices() {
1161   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1162   if (!has_message_loop_)
1163     return;
1164 
1165   new AdbPagesCommand(
1166       adb_thread_, device_providers_,
1167       base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
1168 }
1169 
ReceivedRemoteDevices(RemoteDevices * devices_ptr)1170 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
1171   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1172 
1173   scoped_ptr<RemoteDevices> devices(devices_ptr);
1174 
1175   Listeners copy(listeners_);
1176   for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
1177     (*it)->RemoteDevicesChanged(devices.get());
1178 
1179   if (listeners_.empty())
1180     return;
1181 
1182   BrowserThread::PostDelayedTask(
1183       BrowserThread::UI,
1184       FROM_HERE,
1185       base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
1186       base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
1187 }
1188