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