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
43using content::BrowserThread;
44
45namespace {
46
47const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
48const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
49const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
50const char kListProcessesCommand[] = "shell:ps";
51const char kDumpsysCommand[] = "shell:dumpsys window policy";
52const char kDumpsysScreenSizePrefix[] = "mStable=";
53
54const char kUnknownModel[] = "Offline";
55
56const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
57const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
58const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
59const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
60const char kNewPageRequestWithURL[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
61const char kActivatePageRequest[] =
62    "GET /json/activate/%s HTTP/1.1\r\n\r\n";
63const int kAdbPollingIntervalMs = 1000;
64
65const char kUrlParam[] = "url";
66const char kPageReloadCommand[] = "Page.reload";
67const char kPageNavigateCommand[] = "Page.navigate";
68
69const char kChromeDefaultName[] = "Chrome";
70const char kChromeDefaultActivity[] = "com.google.android.apps.chrome.Main";
71const char kChromeDefaultSocket[] = "chrome_devtools_remote";
72const int kMinVersionNewWithURL = 32;
73const int kNewPageNavigateDelayMs = 500;
74
75const char kWebViewSocketPrefix[] = "webview_devtools_remote";
76const char kWebViewNameTemplate[] = "WebView in %s";
77
78#if defined(DEBUG_DEVTOOLS)
79const char kLocalChrome[] = "Local Chrome";
80#endif  // defined(DEBUG_DEVTOOLS)
81
82typedef DevToolsAdbBridge::Callback Callback;
83typedef std::vector<scoped_refptr<AndroidDevice> >
84    AndroidDevices;
85typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;
86
87
88struct BrowserDescriptor {
89  const char* package;
90  const char* launch_activity;
91  const char* socket;
92  const char* display_name;
93};
94
95const 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
140const 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
148typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
149
150static 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
174typedef std::map<std::string, std::string> StringMap;
175
176static 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
197typedef std::map<std::string,
198                 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > BrowserMap;
199
200static 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
243class 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
280  scoped_refptr<AndroidDevice> current_device() const {
281    return devices_.back();
282  }
283
284  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
308AdbPagesCommand::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
320AdbPagesCommand::~AdbPagesCommand() {
321  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322}
323
324void 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
339void 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
354void 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
393void 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
406void 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
417void 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
429void 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
446void 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
457void 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
475void 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
510void 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
522void AdbPagesCommand::NextBrowser() {
523  browsers_.pop_back();
524  ProcessSockets();
525}
526
527void AdbPagesCommand::NextDevice() {
528  devices_.pop_back();
529  ProcessSerials();
530}
531
532void AdbPagesCommand::Respond() {
533  callback_.Run(remote_devices_.release());
534}
535
536void 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
630void 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
644void 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
665class 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
687AdbProtocolCommand::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
699void AdbProtocolCommand::OnSocketOpened() {
700  web_socket_->SendFrame(command_);
701  web_socket_->Disconnect();
702}
703
704void AdbProtocolCommand::OnFrameRead(const std::string& message) {}
705
706void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
707  delete this;
708}
709
710bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
711  return false;
712}
713
714}  // namespace
715
716const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
717
718class AgentHostDelegate;
719
720typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
721
722base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
723    LAZY_INSTANCE_INITIALIZER;
724
725DevToolsAdbBridge::Wrapper::Wrapper() {
726  bridge_ = new DevToolsAdbBridge();
727}
728
729DevToolsAdbBridge::Wrapper::~Wrapper() {
730}
731
732DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
733  return bridge_.get();
734}
735
736// static
737DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
738  return Singleton<DevToolsAdbBridge::Factory>::get();
739}
740
741// static
742DevToolsAdbBridge* 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
750DevToolsAdbBridge::Factory::Factory()
751    : BrowserContextKeyedServiceFactory(
752          "DevToolsAdbBridge",
753          BrowserContextDependencyManager::GetInstance()) {}
754
755DevToolsAdbBridge::Factory::~Factory() {}
756
757BrowserContextKeyedService*
758DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
759    content::BrowserContext* context) const {
760  return new DevToolsAdbBridge::Wrapper();
761}
762
763
764// AgentHostDelegate ----------------------------------------------------------
765
766class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
767                          public AdbWebSocket::Delegate {
768 public:
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:
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
812  void OpenFrontend() {
813    if (!proxy_)
814      return;
815    DevToolsWindow::OpenExternalFrontend(
816        profile_, frontend_url_, proxy_->GetAgentHost().get());
817  }
818
819  virtual ~AgentHostDelegate() {
820    g_host_delegates.Get().erase(id_);
821  }
822
823  virtual void Attach() OVERRIDE {}
824
825  virtual void Detach() OVERRIDE {
826    web_socket_->Disconnect();
827  }
828
829  virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
830    web_socket_->SendFrame(message);
831  }
832
833  virtual void OnSocketOpened() OVERRIDE {
834    proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
835    OpenFrontend();
836  }
837
838  virtual void OnFrameRead(const std::string& message) OVERRIDE {
839    proxy_->DispatchOnClientHost(message);
840  }
841
842  virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
843    if (proxy_ && closed_by_device)
844      proxy_->ConnectionClosed();
845    delete this;
846  }
847
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
864class 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
889RemotePageTarget::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
932RemotePageTarget::~RemotePageTarget() {
933}
934
935bool RemotePageTarget::IsAttached() const {
936  return debug_url_.empty();
937}
938
939void 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
946bool RemotePageTarget::Activate() const {
947  std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
948  browser_->SendJsonRequest(request, base::Closure());
949  return true;
950}
951
952bool 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
960void RemotePageTarget::Reload() const {
961  browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
962}
963
964void 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
972DevToolsAdbBridge::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
982bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
983  return socket_.find(kChromeDefaultSocket) == 0;
984}
985
986DevToolsAdbBridge::RemoteBrowser::ParsedVersion
987DevToolsAdbBridge::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
999std::vector<DevToolsTargetImpl*>
1000DevToolsAdbBridge::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
1015void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
1016    const base::ListValue& list) {
1017  page_descriptors_.reset(list.DeepCopy());
1018}
1019
1020static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
1021  if (!callback.is_null())
1022    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
1023}
1024
1025void 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
1033void 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
1045static void NoOp(int, const std::string&) {}
1046
1047void 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
1074void 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
1086void 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
1096DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1097}
1098
1099
1100// DevToolsAdbBridge::RemoteDevice --------------------------------------------
1101
1102DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1103    scoped_refptr<AndroidDevice> device)
1104    : device_(device) {
1105}
1106
1107std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
1108  return device_->serial();
1109}
1110
1111std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
1112  return device_->model();
1113}
1114
1115bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1116  return device_->is_connected();
1117}
1118
1119void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1120    scoped_refptr<RemoteBrowser> browser) {
1121  browsers_.push_back(browser);
1122}
1123
1124DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1125}
1126
1127
1128// DevToolsAdbBridge ----------------------------------------------------------
1129
1130DevToolsAdbBridge::DevToolsAdbBridge()
1131    : adb_thread_(RefCountedAdbThread::GetInstance()),
1132      has_message_loop_(adb_thread_->message_loop() != NULL) {
1133}
1134
1135void DevToolsAdbBridge::AddListener(Listener* listener) {
1136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1137  if (listeners_.empty())
1138    RequestRemoteDevices();
1139  listeners_.push_back(listener);
1140}
1141
1142void 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
1150bool 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
1155DevToolsAdbBridge::~DevToolsAdbBridge() {
1156  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1157  DCHECK(listeners_.empty());
1158}
1159
1160void 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
1170void 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