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