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