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 41 using content::BrowserThread; 42 43 namespace { 44 45 const char kPageListRequest[] = "/json"; 46 const char kVersionRequest[] = "/json/version"; 47 const char kClosePageRequest[] = "/json/close/%s"; 48 const char kNewPageRequest[] = "/json/new"; 49 const char kNewPageRequestWithURL[] = "/json/new?%s"; 50 const char kActivatePageRequest[] = "/json/activate/%s"; 51 const char kBrowserTargetSocket[] = "/devtools/browser"; 52 const int kAdbPollingIntervalMs = 1000; 53 54 const char kUrlParam[] = "url"; 55 const char kPageReloadCommand[] = "Page.reload"; 56 const char kPageNavigateCommand[] = "Page.navigate"; 57 58 const int kMinVersionNewWithURL = 32; 59 const int kNewPageNavigateDelayMs = 500; 60 61 // DiscoveryRequest ----------------------------------------------------- 62 63 class 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 97 DiscoveryRequest::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 106 DiscoveryRequest::~DiscoveryRequest() { 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 108 callback_.Run(remote_devices_); 109 } 110 111 void 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 120 void 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 137 void 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 164 void 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 178 class 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 200 ProtocolCommand::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 210 void ProtocolCommand::OnSocketOpened() { 211 web_socket_->SendFrame(command_); 212 } 213 214 void ProtocolCommand::OnFrameRead(const std::string& message) { 215 delete this; 216 } 217 218 void ProtocolCommand::OnSocketClosed() { 219 delete this; 220 } 221 222 ProtocolCommand::~ProtocolCommand() { 223 if (!callback_.is_null()) 224 callback_.Run(); 225 } 226 227 } // namespace 228 229 class AgentHostDelegate; 230 231 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates; 232 233 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates = 234 LAZY_INSTANCE_INITIALIZER; 235 236 DevToolsAndroidBridge::Wrapper::Wrapper(content::BrowserContext* context) { 237 bridge_ = new DevToolsAndroidBridge(Profile::FromBrowserContext(context)); 238 } 239 240 DevToolsAndroidBridge::Wrapper::~Wrapper() { 241 } 242 243 DevToolsAndroidBridge* DevToolsAndroidBridge::Wrapper::Get() { 244 return bridge_.get(); 245 } 246 247 // static 248 DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() { 249 return Singleton<DevToolsAndroidBridge::Factory>::get(); 250 } 251 252 // static 253 DevToolsAndroidBridge* 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 261 DevToolsAndroidBridge::Factory::Factory() 262 : BrowserContextKeyedServiceFactory( 263 "DevToolsAndroidBridge", 264 BrowserContextDependencyManager::GetInstance()) {} 265 266 DevToolsAndroidBridge::Factory::~Factory() {} 267 268 KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor( 269 content::BrowserContext* context) const { 270 return new DevToolsAndroidBridge::Wrapper(context); 271 } 272 273 274 // AgentHostDelegate ---------------------------------------------------------- 275 276 class 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 312 scoped_refptr<content::DevToolsAgentHost> 313 AgentHostDelegate::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 329 AgentHostDelegate::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 343 AgentHostDelegate::~AgentHostDelegate() { 344 g_host_delegates.Get().erase(id_); 345 } 346 347 void 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 354 void AgentHostDelegate::Detach() { 355 web_socket_.reset(); 356 } 357 358 void 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 365 void 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 374 void AgentHostDelegate::OnFrameRead(const std::string& message) { 375 if (proxy_) 376 proxy_->DispatchOnClientHost(message); 377 } 378 379 void AgentHostDelegate::OnSocketClosed() { 380 if (proxy_) 381 proxy_->ConnectionClosed(); 382 } 383 384 //// RemotePageTarget ---------------------------------------------- 385 386 class 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 417 static 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 424 static 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 431 static 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 441 RemotePageTarget::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 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 local_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 bool isWorker = remote_type_ == kTargetTypeWorker || 492 remote_type_ == kTargetTypeServiceWorker; 493 DevToolsWindow::OpenExternalFrontend(profile, frontend_url_, GetAgentHost(), 494 isWorker); 495 } 496 497 bool 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 504 bool 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 511 void RemotePageTarget::Reload() const { 512 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL, 513 base::Closure()); 514 } 515 516 void 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, ¶ms, 521 callback); 522 } 523 524 // DevToolsAndroidBridge::RemoteBrowser --------------------------------------- 525 526 DevToolsAndroidBridge::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 536 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() const { 537 return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome; 538 } 539 540 bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() const { 541 return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView; 542 } 543 544 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion 545 DevToolsAndroidBridge::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 557 std::vector<DevToolsAndroidBridge::RemotePage*> 558 DevToolsAndroidBridge::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 573 void DevToolsAndroidBridge::RemoteBrowser::SetPageDescriptors( 574 const base::ListValue& list) { 575 page_descriptors_.reset(list.DeepCopy()); 576 } 577 578 static 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 588 void DevToolsAndroidBridge::RemoteBrowser::SendJsonRequest( 589 const std::string& request, const JsonRequestCallback& callback) { 590 device_->SendJsonRequest(socket_, request, callback); 591 } 592 593 void 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 605 void 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 613 scoped_refptr<content::DevToolsAgentHost> 614 DevToolsAndroidBridge::RemoteBrowser::GetAgentHost() { 615 return AgentHostDelegate::GetOrCreateAgentHost( 616 "adb:" + device_->serial() + ":" + socket_, this, kBrowserTargetSocket); 617 } 618 619 DevToolsAndroidBridge::AndroidWebSocket* 620 DevToolsAndroidBridge::RemoteBrowser::CreateWebSocket( 621 const std::string& url, 622 DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate) { 623 return device_->CreateWebSocket(socket_, url, delegate); 624 } 625 626 void 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 643 void 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 670 void 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 686 void 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 700 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() { 701 } 702 703 // DevToolsAndroidBridge::RemoteDevice ---------------------------------------- 704 705 DevToolsAndroidBridge::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 720 void DevToolsAndroidBridge::RemoteDevice::OpenSocket( 721 const std::string& socket_name, 722 const AndroidDeviceManager::SocketCallback& callback) { 723 device_->OpenSocket(socket_name, callback); 724 } 725 726 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() { 727 } 728 729 // DevToolsAndroidBridge ------------------------------------------------------ 730 731 DevToolsAndroidBridge::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 744 void 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 753 void 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 764 void DevToolsAndroidBridge::AddDeviceCountListener( 765 DeviceCountListener* listener) { 766 device_count_listeners_.push_back(listener); 767 if (device_count_listeners_.size() == 1) 768 StartDeviceCountPolling(); 769 } 770 771 void 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 782 void 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 790 void 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 803 bool 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 808 DevToolsAndroidBridge::~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 815 void DevToolsAndroidBridge::StartDeviceListPolling() { 816 device_list_callback_.Reset( 817 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, this)); 818 RequestDeviceList(device_list_callback_.callback()); 819 } 820 821 void DevToolsAndroidBridge::StopDeviceListPolling() { 822 device_list_callback_.Cancel(); 823 devices_.clear(); 824 } 825 826 bool DevToolsAndroidBridge::NeedsDeviceListPolling() { 827 return !device_list_listeners_.empty() || !port_forwarding_listeners_.empty(); 828 } 829 830 void 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 841 void 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 866 void DevToolsAndroidBridge::StartDeviceCountPolling() { 867 device_count_callback_.Reset( 868 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, this)); 869 RequestDeviceCount(device_count_callback_.callback()); 870 } 871 872 void DevToolsAndroidBridge::StopDeviceCountPolling() { 873 device_count_callback_.Cancel(); 874 } 875 876 void 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 887 void 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 903 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure& task) { 904 BrowserThread::PostDelayedTask( 905 BrowserThread::UI, 906 FROM_HERE, 907 task, 908 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs)); 909 } 910 911 void 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