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