1// Copyright 2014 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/device/devtools_android_bridge.h"
6
7#include <map>
8#include <vector>
9
10#include "base/base64.h"
11#include "base/bind.h"
12#include "base/compiler_specific.h"
13#include "base/json/json_reader.h"
14#include "base/lazy_instance.h"
15#include "base/message_loop/message_loop.h"
16#include "base/prefs/pref_service.h"
17#include "base/strings/string_number_conversions.h"
18#include "base/strings/string_util.h"
19#include "base/strings/stringprintf.h"
20#include "base/strings/utf_string_conversions.h"
21#include "base/threading/thread.h"
22#include "base/values.h"
23#include "chrome/browser/devtools/browser_list_tabcontents_provider.h"
24#include "chrome/browser/devtools/device/adb/adb_device_info_query.h"
25#include "chrome/browser/devtools/device/adb/adb_device_provider.h"
26#include "chrome/browser/devtools/device/port_forwarding_controller.h"
27#include "chrome/browser/devtools/device/self_device_provider.h"
28#include "chrome/browser/devtools/device/usb/usb_device_provider.h"
29#include "chrome/browser/devtools/devtools_protocol.h"
30#include "chrome/browser/devtools/devtools_target_impl.h"
31#include "chrome/browser/devtools/devtools_window.h"
32#include "chrome/browser/profiles/profile.h"
33#include "chrome/common/pref_names.h"
34#include "components/keyed_service/content/browser_context_dependency_manager.h"
35#include "content/public/browser/devtools_agent_host.h"
36#include "content/public/browser/devtools_external_agent_proxy.h"
37#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
38#include "content/public/browser/user_metrics.h"
39#include "net/base/escape.h"
40
41using content::BrowserThread;
42
43namespace {
44
45const char kPageListRequest[] = "/json";
46const char kVersionRequest[] = "/json/version";
47const char kClosePageRequest[] = "/json/close/%s";
48const char kNewPageRequest[] = "/json/new";
49const char kNewPageRequestWithURL[] = "/json/new?%s";
50const char kActivatePageRequest[] = "/json/activate/%s";
51const char kBrowserTargetSocket[] = "/devtools/browser";
52const int kAdbPollingIntervalMs = 1000;
53
54const char kUrlParam[] = "url";
55const char kPageReloadCommand[] = "Page.reload";
56const char kPageNavigateCommand[] = "Page.navigate";
57
58const int kMinVersionNewWithURL = 32;
59const int kNewPageNavigateDelayMs = 500;
60
61// DiscoveryRequest -----------------------------------------------------
62
63class DiscoveryRequest : public base::RefCountedThreadSafe<
64    DiscoveryRequest,
65    BrowserThread::DeleteOnUIThread> {
66 public:
67  typedef AndroidDeviceManager::Device Device;
68  typedef AndroidDeviceManager::Devices Devices;
69  typedef AndroidDeviceManager::DeviceInfo DeviceInfo;
70  typedef DevToolsAndroidBridge::RemoteDevice RemoteDevice;
71  typedef DevToolsAndroidBridge::RemoteDevices RemoteDevices;
72  typedef DevToolsAndroidBridge::RemoteBrowser RemoteBrowser;
73  typedef DevToolsAndroidBridge::RemoteBrowsers RemoteBrowsers;
74  typedef base::Callback<void(const RemoteDevices&)> DiscoveryCallback;
75
76  DiscoveryRequest(AndroidDeviceManager* device_manager,
77                   const DiscoveryCallback& callback);
78 private:
79  friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
80  friend class base::DeleteHelper<DiscoveryRequest>;
81  virtual ~DiscoveryRequest();
82
83  void ReceivedDevices(const Devices& devices);
84  void ReceivedDeviceInfo(scoped_refptr<Device> device,
85                          const DeviceInfo& device_info);
86  void ReceivedVersion(scoped_refptr<RemoteBrowser>,
87                       int result,
88                       const std::string& response);
89  void ReceivedPages(scoped_refptr<RemoteBrowser>,
90                     int result,
91                     const std::string& response);
92
93  DiscoveryCallback callback_;
94  RemoteDevices remote_devices_;
95};
96
97DiscoveryRequest::DiscoveryRequest(
98    AndroidDeviceManager* device_manager,
99    const DiscoveryCallback& callback)
100    : callback_(callback) {
101  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102  device_manager->QueryDevices(
103      base::Bind(&DiscoveryRequest::ReceivedDevices, this));
104}
105
106DiscoveryRequest::~DiscoveryRequest() {
107  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108  callback_.Run(remote_devices_);
109}
110
111void DiscoveryRequest::ReceivedDevices(const Devices& devices) {
112  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113  for (Devices::const_iterator it = devices.begin();
114       it != devices.end(); ++it) {
115    (*it)->QueryDeviceInfo(
116        base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, *it));
117  }
118}
119
120void DiscoveryRequest::ReceivedDeviceInfo(scoped_refptr<Device> device,
121                                          const DeviceInfo& device_info) {
122  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123  scoped_refptr<RemoteDevice> remote_device =
124      new RemoteDevice(device, device_info);
125  remote_devices_.push_back(remote_device);
126  for (RemoteBrowsers::iterator it = remote_device->browsers().begin();
127       it != remote_device->browsers().end(); ++it) {
128    (*it)->SendJsonRequest(
129        kVersionRequest,
130        base::Bind(&DiscoveryRequest::ReceivedVersion, this, *it));
131    (*it)->SendJsonRequest(
132        kPageListRequest,
133        base::Bind(&DiscoveryRequest::ReceivedPages, this, *it));
134  }
135}
136
137void DiscoveryRequest::ReceivedVersion(scoped_refptr<RemoteBrowser> browser,
138                                       int result,
139                                       const std::string& response) {
140  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
141  if (result < 0)
142    return;
143  // Parse version, append to package name if available,
144  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
145  base::DictionaryValue* dict;
146  if (value && value->GetAsDictionary(&dict)) {
147    std::string browser_name;
148    if (dict->GetString("Browser", &browser_name)) {
149      std::vector<std::string> parts;
150      Tokenize(browser_name, "/", &parts);
151      if (parts.size() == 2)
152        browser->set_version(parts[1]);
153      else
154        browser->set_version(browser_name);
155    }
156    std::string package;
157    if (dict->GetString("Android-Package", &package)) {
158      browser->set_display_name(
159          AdbDeviceInfoQuery::GetDisplayName(browser->socket(), package));
160    }
161  }
162}
163
164void DiscoveryRequest::ReceivedPages(scoped_refptr<RemoteBrowser> browser,
165                                     int result,
166                                     const std::string& response) {
167  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168  if (result < 0)
169    return;
170  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
171  base::ListValue* list_value;
172  if (value && value->GetAsList(&list_value))
173    browser->SetPageDescriptors(*list_value);
174}
175
176// ProtocolCommand ------------------------------------------------------------
177
178class ProtocolCommand
179    : public DevToolsAndroidBridge::AndroidWebSocket::Delegate {
180 public:
181  ProtocolCommand(
182      scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
183      const std::string& debug_url,
184      const std::string& command,
185      const base::Closure callback);
186
187 private:
188  virtual void OnSocketOpened() OVERRIDE;
189  virtual void OnFrameRead(const std::string& message) OVERRIDE;
190  virtual void OnSocketClosed() OVERRIDE;
191  virtual ~ProtocolCommand();
192
193  const std::string command_;
194  const base::Closure callback_;
195  scoped_ptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
196
197  DISALLOW_COPY_AND_ASSIGN(ProtocolCommand);
198};
199
200ProtocolCommand::ProtocolCommand(
201    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
202    const std::string& debug_url,
203    const std::string& command,
204    const base::Closure callback)
205    : command_(command),
206      callback_(callback),
207      web_socket_(browser->CreateWebSocket(debug_url, this)) {
208}
209
210void ProtocolCommand::OnSocketOpened() {
211  web_socket_->SendFrame(command_);
212}
213
214void ProtocolCommand::OnFrameRead(const std::string& message) {
215  delete this;
216}
217
218void ProtocolCommand::OnSocketClosed() {
219  delete this;
220}
221
222ProtocolCommand::~ProtocolCommand() {
223  if (!callback_.is_null())
224    callback_.Run();
225}
226
227}  // namespace
228
229class AgentHostDelegate;
230
231typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
232
233base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
234    LAZY_INSTANCE_INITIALIZER;
235
236DevToolsAndroidBridge::Wrapper::Wrapper(content::BrowserContext* context) {
237  bridge_ = new DevToolsAndroidBridge(Profile::FromBrowserContext(context));
238}
239
240DevToolsAndroidBridge::Wrapper::~Wrapper() {
241}
242
243DevToolsAndroidBridge* DevToolsAndroidBridge::Wrapper::Get() {
244  return bridge_.get();
245}
246
247// static
248DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() {
249  return Singleton<DevToolsAndroidBridge::Factory>::get();
250}
251
252// static
253DevToolsAndroidBridge* DevToolsAndroidBridge::Factory::GetForProfile(
254    Profile* profile) {
255  DevToolsAndroidBridge::Wrapper* wrapper =
256      static_cast<DevToolsAndroidBridge::Wrapper*>(GetInstance()->
257          GetServiceForBrowserContext(profile, true));
258  return wrapper ? wrapper->Get() : NULL;
259}
260
261DevToolsAndroidBridge::Factory::Factory()
262    : BrowserContextKeyedServiceFactory(
263          "DevToolsAndroidBridge",
264          BrowserContextDependencyManager::GetInstance()) {}
265
266DevToolsAndroidBridge::Factory::~Factory() {}
267
268KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
269    content::BrowserContext* context) const {
270  return new DevToolsAndroidBridge::Wrapper(context);
271}
272
273
274// AgentHostDelegate ----------------------------------------------------------
275
276class AgentHostDelegate
277    : public content::DevToolsExternalAgentProxyDelegate,
278      public DevToolsAndroidBridge::AndroidWebSocket::Delegate {
279 public:
280  static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost(
281      const std::string& id,
282      scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
283      const std::string& debug_url);
284
285 private:
286  AgentHostDelegate(
287      const std::string& id,
288      scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
289      const std::string& debug_url);
290  virtual ~AgentHostDelegate();
291  virtual void Attach(content::DevToolsExternalAgentProxy* proxy) OVERRIDE;
292  virtual void Detach() OVERRIDE;
293  virtual void SendMessageToBackend(
294      const std::string& message) OVERRIDE;
295  virtual void OnSocketOpened() OVERRIDE;
296  virtual void OnFrameRead(const std::string& message) OVERRIDE;
297  virtual void OnSocketClosed() OVERRIDE;
298
299  const std::string id_;
300  scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
301  const std::string debug_url_;
302  bool socket_opened_;
303  bool is_web_view_;
304  std::vector<std::string> pending_messages_;
305  scoped_ptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
306  content::DevToolsAgentHost* agent_host_;
307  content::DevToolsExternalAgentProxy* proxy_;
308  DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
309};
310
311// static
312scoped_refptr<content::DevToolsAgentHost>
313AgentHostDelegate::GetOrCreateAgentHost(
314    const std::string& id,
315    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
316    const std::string& debug_url) {
317  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318  AgentHostDelegates::iterator it = g_host_delegates.Get().find(id);
319  if (it != g_host_delegates.Get().end())
320    return it->second->agent_host_;
321
322  AgentHostDelegate* delegate = new AgentHostDelegate(id, browser, debug_url);
323  scoped_refptr<content::DevToolsAgentHost> result =
324      content::DevToolsAgentHost::Create(delegate);
325  delegate->agent_host_ = result.get();
326  return result;
327}
328
329AgentHostDelegate::AgentHostDelegate(
330    const std::string& id,
331    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
332    const std::string& debug_url)
333    : id_(id),
334      browser_(browser),
335      debug_url_(debug_url),
336      socket_opened_(false),
337      is_web_view_(browser->IsWebView()),
338      agent_host_(NULL),
339      proxy_(NULL) {
340  g_host_delegates.Get()[id] = this;
341}
342
343AgentHostDelegate::~AgentHostDelegate() {
344  g_host_delegates.Get().erase(id_);
345}
346
347void AgentHostDelegate::Attach(content::DevToolsExternalAgentProxy* proxy) {
348  proxy_ = proxy;
349  content::RecordAction(base::UserMetricsAction(is_web_view_ ?
350      "DevTools_InspectAndroidWebView" : "DevTools_InspectAndroidPage"));
351  web_socket_.reset(browser_->CreateWebSocket(debug_url_, this));
352}
353
354void AgentHostDelegate::Detach() {
355  web_socket_.reset();
356}
357
358void AgentHostDelegate::SendMessageToBackend(const std::string& message) {
359  if (socket_opened_)
360    web_socket_->SendFrame(message);
361  else
362    pending_messages_.push_back(message);
363}
364
365void AgentHostDelegate::OnSocketOpened() {
366  socket_opened_ = true;
367  for (std::vector<std::string>::iterator it = pending_messages_.begin();
368       it != pending_messages_.end(); ++it) {
369    SendMessageToBackend(*it);
370  }
371  pending_messages_.clear();
372}
373
374void AgentHostDelegate::OnFrameRead(const std::string& message) {
375  if (proxy_)
376      proxy_->DispatchOnClientHost(message);
377}
378
379void AgentHostDelegate::OnSocketClosed() {
380  if (proxy_)
381    proxy_->ConnectionClosed();
382}
383
384//// RemotePageTarget ----------------------------------------------
385
386class RemotePageTarget : public DevToolsTargetImpl,
387                         public DevToolsAndroidBridge::RemotePage {
388 public:
389  RemotePageTarget(scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
390                   const base::DictionaryValue& value);
391  virtual ~RemotePageTarget();
392
393  // DevToolsAndroidBridge::RemotePage implementation.
394  virtual DevToolsTargetImpl* GetTarget() OVERRIDE;
395  virtual std::string GetFrontendURL() OVERRIDE;
396
397  // DevToolsTargetImpl overrides.
398  virtual std::string GetId() const OVERRIDE;
399  virtual bool IsAttached() const OVERRIDE;
400  virtual bool Activate() const OVERRIDE;
401  virtual bool Close() const OVERRIDE;
402  virtual void Inspect(Profile* profile) const OVERRIDE;
403  virtual void Reload() const OVERRIDE;
404
405  void Navigate(const std::string& url, base::Closure callback) const;
406
407 private:
408  scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
409  std::string debug_url_;
410  std::string frontend_url_;
411  std::string remote_id_;
412  std::string remote_type_;
413  std::string local_id_;
414  DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
415};
416
417static std::string GetStringProperty(const base::DictionaryValue& value,
418                                     const std::string& name) {
419  std::string result;
420  value.GetString(name, &result);
421  return result;
422}
423
424static std::string BuildUniqueTargetId(
425    DevToolsAndroidBridge::RemoteBrowser* browser,
426    const base::DictionaryValue& value) {
427  return base::StringPrintf("%s:%s:%s", browser->serial().c_str(),
428      browser->socket().c_str(), GetStringProperty(value, "id").c_str());
429}
430
431static std::string GetDebugURL(const base::DictionaryValue& value) {
432  std::string debug_url = GetStringProperty(value, "webSocketDebuggerUrl");
433
434  if (debug_url.find("ws://") == 0)
435    debug_url = debug_url.substr(5);
436  else
437    debug_url = "";
438  return debug_url;
439}
440
441RemotePageTarget::RemotePageTarget(
442    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
443    const base::DictionaryValue& value)
444    : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
445                             BuildUniqueTargetId(browser.get(), value),
446                             browser, GetDebugURL(value))),
447      browser_(browser),
448      debug_url_(GetDebugURL(value)),
449      remote_id_(GetStringProperty(value, "id")),
450      remote_type_(GetStringProperty(value, "type")),
451      local_id_(BuildUniqueTargetId(browser.get(), value)) {
452  set_type("adb_page");
453  set_url(GURL(GetStringProperty(value, "url")));
454  set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
455      GetStringProperty(value, "title")))));
456  set_description(GetStringProperty(value, "description"));
457  set_favicon_url(GURL(GetStringProperty(value, "faviconUrl")));
458  debug_url_ = GetDebugURL(value);
459  frontend_url_ = GetStringProperty(value, "devtoolsFrontendUrl");
460
461  size_t ws_param = frontend_url_.find("?ws");
462  if (ws_param != std::string::npos)
463    frontend_url_ = frontend_url_.substr(0, ws_param);
464  if (frontend_url_.find("http:") == 0)
465    frontend_url_ = "https:" + frontend_url_.substr(5);
466}
467
468RemotePageTarget::~RemotePageTarget() {
469}
470
471DevToolsTargetImpl* RemotePageTarget::GetTarget() {
472  return this;
473}
474
475std::string RemotePageTarget::GetFrontendURL() {
476  return frontend_url_;
477}
478
479std::string RemotePageTarget::GetId() const {
480  return local_id_;
481}
482
483bool RemotePageTarget::IsAttached() const {
484  return debug_url_.empty();
485}
486
487static void NoOp(int, const std::string&) {}
488
489void RemotePageTarget::Inspect(Profile* profile) const {
490  Activate();
491  bool isWorker = remote_type_ == kTargetTypeWorker ||
492                  remote_type_ == kTargetTypeServiceWorker;
493  DevToolsWindow::OpenExternalFrontend(profile, frontend_url_, GetAgentHost(),
494                                       isWorker);
495}
496
497bool RemotePageTarget::Activate() const {
498  std::string request = base::StringPrintf(kActivatePageRequest,
499                                           remote_id_.c_str());
500  browser_->SendJsonRequest(request, base::Bind(&NoOp));
501  return true;
502}
503
504bool RemotePageTarget::Close() const {
505  std::string request = base::StringPrintf(kClosePageRequest,
506                                           remote_id_.c_str());
507  browser_->SendJsonRequest(request, base::Bind(&NoOp));
508  return true;
509}
510
511void RemotePageTarget::Reload() const {
512  browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL,
513                                base::Closure());
514}
515
516void RemotePageTarget::Navigate(const std::string& url,
517                                base::Closure callback) const {
518  base::DictionaryValue params;
519  params.SetString(kUrlParam, url);
520  browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, &params,
521                                callback);
522}
523
524// DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
525
526DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
527    scoped_refptr<Device> device,
528    const AndroidDeviceManager::BrowserInfo& browser_info)
529    : device_(device),
530      socket_(browser_info.socket_name),
531      display_name_(browser_info.display_name),
532      type_(browser_info.type),
533      page_descriptors_(new base::ListValue()) {
534}
535
536bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() const {
537  return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
538}
539
540bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() const {
541  return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView;
542}
543
544DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
545DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() const {
546  ParsedVersion result;
547  std::vector<std::string> parts;
548  Tokenize(version_, ".", &parts);
549  for (size_t i = 0; i != parts.size(); ++i) {
550    int value = 0;
551    base::StringToInt(parts[i], &value);
552    result.push_back(value);
553  }
554  return result;
555}
556
557std::vector<DevToolsAndroidBridge::RemotePage*>
558DevToolsAndroidBridge::RemoteBrowser::CreatePages() {
559  std::vector<DevToolsAndroidBridge::RemotePage*> result;
560  for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
561    base::Value* item;
562    page_descriptors_->Get(i, &item);
563    if (!item)
564      continue;
565    base::DictionaryValue* dict;
566    if (!item->GetAsDictionary(&dict))
567      continue;
568    result.push_back(new RemotePageTarget(this, *dict));
569  }
570  return result;
571}
572
573void DevToolsAndroidBridge::RemoteBrowser::SetPageDescriptors(
574    const base::ListValue& list) {
575  page_descriptors_.reset(list.DeepCopy());
576}
577
578static void RespondOnUIThread(
579    const DevToolsAndroidBridge::JsonRequestCallback& callback,
580    int result,
581    const std::string& response) {
582  if (callback.is_null())
583    return;
584  BrowserThread::PostTask(
585      BrowserThread::UI, FROM_HERE, base::Bind(callback, result, response));
586}
587
588void DevToolsAndroidBridge::RemoteBrowser::SendJsonRequest(
589    const std::string& request, const JsonRequestCallback& callback) {
590  device_->SendJsonRequest(socket_, request, callback);
591}
592
593void DevToolsAndroidBridge::RemoteBrowser::SendProtocolCommand(
594    const std::string& debug_url,
595    const std::string& method,
596    base::DictionaryValue* params,
597    const base::Closure callback) {
598  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
599  if (debug_url.empty())
600    return;
601  DevToolsProtocol::Command command(1, method, params);
602  new ProtocolCommand(this, debug_url, command.Serialize(), callback);
603}
604
605void DevToolsAndroidBridge::RemoteBrowser::Open(
606    const std::string& url,
607    const DevToolsAndroidBridge::RemotePageCallback& callback) {
608  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609  InnerOpen(url, base::Bind(&RemoteBrowser::RespondToOpenOnUIThread,
610                            this, callback));
611}
612
613scoped_refptr<content::DevToolsAgentHost>
614DevToolsAndroidBridge::RemoteBrowser::GetAgentHost() {
615  return AgentHostDelegate::GetOrCreateAgentHost(
616      "adb:" + device_->serial() + ":" + socket_, this, kBrowserTargetSocket);
617}
618
619DevToolsAndroidBridge::AndroidWebSocket*
620DevToolsAndroidBridge::RemoteBrowser::CreateWebSocket(
621    const std::string& url,
622    DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate) {
623  return device_->CreateWebSocket(socket_, url, delegate);
624}
625
626void DevToolsAndroidBridge::RemoteBrowser::RespondToOpenOnUIThread(
627    const DevToolsAndroidBridge::RemotePageCallback& callback,
628    int result,
629    const std::string& response) {
630  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
631  if (result < 0) {
632    callback.Run(NULL);
633    return;
634  }
635  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
636  base::DictionaryValue* dict;
637  if (value && value->GetAsDictionary(&dict)) {
638    RemotePageTarget* new_page = new RemotePageTarget(this, *dict);
639    callback.Run(new_page);
640  }
641}
642
643void DevToolsAndroidBridge::RemoteBrowser::InnerOpen(
644    const std::string& input_url,
645    const JsonRequestCallback& callback) {
646  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
647  GURL gurl(input_url);
648  if (!gurl.is_valid()) {
649    gurl = GURL("http://" + input_url);
650    if (!gurl.is_valid())
651     return;
652  }
653  std::string url = gurl.spec();
654
655  ParsedVersion parsed_version = GetParsedVersion();
656  if (IsChrome() &&
657      !parsed_version.empty() &&
658      parsed_version[0] >= kMinVersionNewWithURL) {
659    std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
660    std::string request =
661        base::StringPrintf(kNewPageRequestWithURL, query.c_str());
662    SendJsonRequest(request, callback);
663  } else {
664    SendJsonRequest(kNewPageRequest,
665        base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this,
666                   callback, url));
667  }
668}
669
670void DevToolsAndroidBridge::RemoteBrowser::PageCreatedOnUIThread(
671    const JsonRequestCallback& callback,
672    const std::string& url, int result, const std::string& response) {
673  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
674
675  if (result < 0)
676    return;
677  // Navigating too soon after the page creation breaks navigation history
678  // (crbug.com/311014). This can be avoided by adding a moderate delay.
679  BrowserThread::PostDelayedTask(
680      BrowserThread::UI, FROM_HERE,
681      base::Bind(&RemoteBrowser::NavigatePageOnUIThread,
682                 this, callback, result, response, url),
683      base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
684}
685
686void DevToolsAndroidBridge::RemoteBrowser::NavigatePageOnUIThread(
687    const JsonRequestCallback& callback,
688    int result, const std::string& response, const std::string& url) {
689  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
690  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
691  base::DictionaryValue* dict;
692
693  if (value && value->GetAsDictionary(&dict)) {
694    RemotePageTarget new_page(this, *dict);
695    new_page.Navigate(url,
696        base::Bind(&RespondOnUIThread, callback, result, response));
697  }
698}
699
700DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
701}
702
703// DevToolsAndroidBridge::RemoteDevice ----------------------------------------
704
705DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
706    scoped_refptr<AndroidDeviceManager::Device> device,
707    const AndroidDeviceManager::DeviceInfo& device_info)
708    : device_(device),
709      model_(device_info.model),
710      connected_(device_info.connected),
711      screen_size_(device_info.screen_size) {
712  for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it =
713      device_info.browser_info.begin();
714      it != device_info.browser_info.end();
715      ++it) {
716    browsers_.push_back(new DevToolsAndroidBridge::RemoteBrowser(device, *it));
717  }
718}
719
720void DevToolsAndroidBridge::RemoteDevice::OpenSocket(
721    const std::string& socket_name,
722    const AndroidDeviceManager::SocketCallback& callback) {
723  device_->OpenSocket(socket_name, callback);
724}
725
726DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
727}
728
729// DevToolsAndroidBridge ------------------------------------------------------
730
731DevToolsAndroidBridge::DevToolsAndroidBridge(Profile* profile)
732    : profile_(profile),
733      device_manager_(AndroidDeviceManager::Create()),
734      task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault)),
735      port_forwarding_controller_(new PortForwardingController(profile)) {
736  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
737  pref_change_registrar_.Init(profile_->GetPrefs());
738  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
739      base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
740                 base::Unretained(this)));
741  CreateDeviceProviders();
742}
743
744void DevToolsAndroidBridge::AddDeviceListListener(
745    DeviceListListener* listener) {
746  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
747  bool polling_was_off = !NeedsDeviceListPolling();
748  device_list_listeners_.push_back(listener);
749  if (polling_was_off)
750    StartDeviceListPolling();
751}
752
753void DevToolsAndroidBridge::RemoveDeviceListListener(
754    DeviceListListener* listener) {
755  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
756  DeviceListListeners::iterator it = std::find(
757      device_list_listeners_.begin(), device_list_listeners_.end(), listener);
758  DCHECK(it != device_list_listeners_.end());
759  device_list_listeners_.erase(it);
760  if (!NeedsDeviceListPolling())
761    StopDeviceListPolling();
762}
763
764void DevToolsAndroidBridge::AddDeviceCountListener(
765    DeviceCountListener* listener) {
766  device_count_listeners_.push_back(listener);
767  if (device_count_listeners_.size() == 1)
768    StartDeviceCountPolling();
769}
770
771void DevToolsAndroidBridge::RemoveDeviceCountListener(
772    DeviceCountListener* listener) {
773  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
774  DeviceCountListeners::iterator it = std::find(
775      device_count_listeners_.begin(), device_count_listeners_.end(), listener);
776  DCHECK(it != device_count_listeners_.end());
777  device_count_listeners_.erase(it);
778  if (device_count_listeners_.empty())
779    StopDeviceCountPolling();
780}
781
782void DevToolsAndroidBridge::AddPortForwardingListener(
783    PortForwardingListener* listener) {
784  bool polling_was_off = !NeedsDeviceListPolling();
785  port_forwarding_listeners_.push_back(listener);
786  if (polling_was_off)
787    StartDeviceListPolling();
788}
789
790void DevToolsAndroidBridge::RemovePortForwardingListener(
791    PortForwardingListener* listener) {
792  PortForwardingListeners::iterator it = std::find(
793      port_forwarding_listeners_.begin(),
794      port_forwarding_listeners_.end(),
795      listener);
796  DCHECK(it != port_forwarding_listeners_.end());
797  port_forwarding_listeners_.erase(it);
798  if (!NeedsDeviceListPolling())
799    StopDeviceListPolling();
800}
801
802// static
803bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string& agent_id) {
804  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
805  return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
806}
807
808DevToolsAndroidBridge::~DevToolsAndroidBridge() {
809  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
810  DCHECK(device_list_listeners_.empty());
811  DCHECK(device_count_listeners_.empty());
812  DCHECK(port_forwarding_listeners_.empty());
813}
814
815void DevToolsAndroidBridge::StartDeviceListPolling() {
816  device_list_callback_.Reset(
817    base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, this));
818  RequestDeviceList(device_list_callback_.callback());
819}
820
821void DevToolsAndroidBridge::StopDeviceListPolling() {
822  device_list_callback_.Cancel();
823  devices_.clear();
824}
825
826bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
827  return !device_list_listeners_.empty() || !port_forwarding_listeners_.empty();
828}
829
830void DevToolsAndroidBridge::RequestDeviceList(
831    const base::Callback<void(const RemoteDevices&)>& callback) {
832  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833
834  if (!NeedsDeviceListPolling() ||
835      !callback.Equals(device_list_callback_.callback()))
836    return;
837
838  new DiscoveryRequest(device_manager_.get(), callback);
839}
840
841void DevToolsAndroidBridge::ReceivedDeviceList(const RemoteDevices& devices) {
842  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
843
844  DeviceListListeners copy(device_list_listeners_);
845  for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it)
846    (*it)->DeviceListChanged(devices);
847
848  DevicesStatus status =
849      port_forwarding_controller_->DeviceListChanged(devices);
850  PortForwardingListeners forwarding_listeners(port_forwarding_listeners_);
851  for (PortForwardingListeners::iterator it = forwarding_listeners.begin();
852       it != forwarding_listeners.end(); ++it) {
853    (*it)->PortStatusChanged(status);
854  }
855
856  if (!NeedsDeviceListPolling())
857    return;
858
859  devices_ = devices;
860
861  task_scheduler_.Run(
862      base::Bind(&DevToolsAndroidBridge::RequestDeviceList,
863                 this, device_list_callback_.callback()));
864}
865
866void DevToolsAndroidBridge::StartDeviceCountPolling() {
867  device_count_callback_.Reset(
868      base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, this));
869  RequestDeviceCount(device_count_callback_.callback());
870}
871
872void DevToolsAndroidBridge::StopDeviceCountPolling() {
873  device_count_callback_.Cancel();
874}
875
876void DevToolsAndroidBridge::RequestDeviceCount(
877    const base::Callback<void(int)>& callback) {
878  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
879
880  if (device_count_listeners_.empty() ||
881      !callback.Equals(device_count_callback_.callback()))
882    return;
883
884  UsbDeviceProvider::CountDevices(callback);
885}
886
887void DevToolsAndroidBridge::ReceivedDeviceCount(int count) {
888  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
889
890  DeviceCountListeners copy(device_count_listeners_);
891  for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it)
892    (*it)->DeviceCountChanged(count);
893
894  if (device_count_listeners_.empty())
895     return;
896
897  task_scheduler_.Run(
898      base::Bind(&DevToolsAndroidBridge::RequestDeviceCount,
899                 this, device_count_callback_.callback()));
900}
901
902// static
903void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure& task) {
904  BrowserThread::PostDelayedTask(
905      BrowserThread::UI,
906      FROM_HERE,
907      task,
908      base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
909}
910
911void DevToolsAndroidBridge::CreateDeviceProviders() {
912  AndroidDeviceManager::DeviceProviders device_providers;
913#if defined(DEBUG_DEVTOOLS)
914  BrowserListTabContentsProvider::EnableTethering();
915  // We cannot rely on command line switch here as we might want to connect
916  // to another instance of Chrome. Using hard-coded port number instead.
917  const int kDefaultDebuggingPort = 9222;
918  device_providers.push_back(new SelfAsDeviceProvider(kDefaultDebuggingPort));
919#endif
920  device_providers.push_back(new AdbDeviceProvider());
921
922  PrefService* service = profile_->GetPrefs();
923  const PrefService::Preference* pref =
924      service->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled);
925  const base::Value* pref_value = pref->GetValue();
926
927  bool enabled;
928  if (pref_value->GetAsBoolean(&enabled) && enabled) {
929    device_providers.push_back(new UsbDeviceProvider(profile_));
930  }
931  device_manager_->SetDeviceProviders(device_providers);
932  if (NeedsDeviceListPolling()) {
933    StopDeviceListPolling();
934    StartDeviceListPolling();
935  }
936}
937