Android Chromoting: Remove exit-fullscreen button.
[chromium-blink-merge.git] / chrome / browser / devtools / device / devtools_android_bridge.cc
blobdeaf9d6b5b2832664a786b4c245cf23ada6dd411
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.
5 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
7 #include <map>
8 #include <vector>
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/memory/singleton.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/prefs/pref_service.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/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/device/webrtc/webrtc_device_provider.h"
30 #include "chrome/browser/devtools/devtools_protocol.h"
31 #include "chrome/browser/devtools/devtools_target_impl.h"
32 #include "chrome/browser/devtools/devtools_window.h"
33 #include "chrome/browser/devtools/remote_debugging_server.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
36 #include "chrome/browser/signin/signin_manager_factory.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/pref_names.h"
39 #include "components/keyed_service/content/browser_context_dependency_manager.h"
40 #include "components/signin/core/browser/profile_oauth2_token_service.h"
41 #include "components/signin/core/browser/signin_manager.h"
42 #include "content/public/browser/devtools_agent_host.h"
43 #include "content/public/browser/devtools_external_agent_proxy.h"
44 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
45 #include "content/public/browser/user_metrics.h"
46 #include "net/base/escape.h"
47 #include "net/base/net_errors.h"
49 using content::BrowserThread;
51 namespace {
53 const char kPageListRequest[] = "/json";
54 const char kVersionRequest[] = "/json/version";
55 const char kClosePageRequest[] = "/json/close/%s";
56 const char kNewPageRequestWithURL[] = "/json/new?%s";
57 const char kActivatePageRequest[] = "/json/activate/%s";
58 const char kBrowserTargetSocket[] = "/devtools/browser";
59 const int kAdbPollingIntervalMs = 1000;
61 const char kPageReloadCommand[] = "Page.reload";
63 const char kWebViewSocketPrefix[] = "webview_devtools_remote";
65 bool IsWebRTCDeviceProviderEnabled() {
66 return base::CommandLine::ForCurrentProcess()->HasSwitch(
67 switches::kEnableDevToolsExperiments);
70 bool BrowserIdFromString(const std::string& browser_id_str,
71 DevToolsAndroidBridge::BrowserId* browser_id) {
72 size_t colon_pos = browser_id_str.find(':');
73 if (colon_pos == std::string::npos)
74 return false;
75 browser_id->first = browser_id_str.substr(0, colon_pos);
76 browser_id->second = browser_id_str.substr(colon_pos + 1);
77 return true;
80 } // namespace
82 // DiscoveryRequest -----------------------------------------------------
84 class DevToolsAndroidBridge::DiscoveryRequest
85 : public base::RefCountedThreadSafe<DiscoveryRequest,
86 BrowserThread::DeleteOnUIThread> {
87 public:
88 DiscoveryRequest(AndroidDeviceManager* device_manager,
89 const DeviceListCallback& callback);
90 private:
91 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
92 friend class base::DeleteHelper<DiscoveryRequest>;
93 virtual ~DiscoveryRequest();
95 void ReceivedDevices(const AndroidDeviceManager::Devices& devices);
96 void ReceivedDeviceInfo(scoped_refptr<AndroidDeviceManager::Device> device,
97 const AndroidDeviceManager::DeviceInfo& device_info);
98 void ReceivedVersion(scoped_refptr<RemoteBrowser>,
99 int result,
100 const std::string& response);
101 void ReceivedPages(scoped_refptr<RemoteBrowser>,
102 int result,
103 const std::string& response);
105 DeviceListCallback callback_;
106 CompleteDevices complete_devices_;
109 DevToolsAndroidBridge::DiscoveryRequest::DiscoveryRequest(
110 AndroidDeviceManager* device_manager,
111 const DeviceListCallback& callback)
112 : callback_(callback) {
113 DCHECK_CURRENTLY_ON(BrowserThread::UI);
114 device_manager->QueryDevices(
115 base::Bind(&DiscoveryRequest::ReceivedDevices, this));
118 DevToolsAndroidBridge::DiscoveryRequest::~DiscoveryRequest() {
119 DCHECK_CURRENTLY_ON(BrowserThread::UI);
120 callback_.Run(complete_devices_);
123 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDevices(
124 const AndroidDeviceManager::Devices& devices) {
125 DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 for (const auto& device : devices) {
127 device->QueryDeviceInfo(
128 base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, device));
132 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDeviceInfo(
133 scoped_refptr<AndroidDeviceManager::Device> device,
134 const AndroidDeviceManager::DeviceInfo& device_info) {
135 DCHECK_CURRENTLY_ON(BrowserThread::UI);
136 scoped_refptr<RemoteDevice> remote_device =
137 new RemoteDevice(device->serial(), device_info);
138 complete_devices_.push_back(std::make_pair(device, remote_device));
139 for (RemoteBrowsers::iterator it = remote_device->browsers().begin();
140 it != remote_device->browsers().end(); ++it) {
141 device->SendJsonRequest(
142 (*it)->socket(),
143 kVersionRequest,
144 base::Bind(&DiscoveryRequest::ReceivedVersion, this, *it));
145 device->SendJsonRequest(
146 (*it)->socket(),
147 kPageListRequest,
148 base::Bind(&DiscoveryRequest::ReceivedPages, this, *it));
152 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedVersion(
153 scoped_refptr<RemoteBrowser> browser,
154 int result,
155 const std::string& response) {
156 DCHECK_CURRENTLY_ON(BrowserThread::UI);
157 if (result < 0)
158 return;
159 // Parse version, append to package name if available,
160 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
161 base::DictionaryValue* dict;
162 if (value && value->GetAsDictionary(&dict)) {
163 std::string browser_name;
164 if (dict->GetString("Browser", &browser_name)) {
165 std::vector<std::string> parts;
166 Tokenize(browser_name, "/", &parts);
167 if (parts.size() == 2)
168 browser->version_ = parts[1];
169 else
170 browser->version_ = browser_name;
172 std::string package;
173 if (dict->GetString("Android-Package", &package)) {
174 browser->display_name_ =
175 AndroidDeviceManager::GetBrowserName(browser->socket(), package);
180 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedPages(
181 scoped_refptr<RemoteBrowser> browser,
182 int result,
183 const std::string& response) {
184 DCHECK_CURRENTLY_ON(BrowserThread::UI);
185 if (result < 0)
186 return;
187 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
188 base::ListValue* list_value;
189 if (value && value->GetAsList(&list_value)) {
190 for (const auto& page_value : *list_value) {
191 base::DictionaryValue* dict;
192 if (page_value->GetAsDictionary(&dict))
193 browser->pages_.push_back(new RemotePage(browser->browser_id_, *dict));
198 // ProtocolCommand ------------------------------------------------------------
200 namespace {
202 class ProtocolCommand
203 : public AndroidDeviceManager::AndroidWebSocket::Delegate {
204 public:
205 ProtocolCommand(
206 scoped_refptr<AndroidDeviceManager::Device> device,
207 const std::string& socket,
208 const std::string& debug_url,
209 const std::string& command,
210 const base::Closure callback);
212 private:
213 void OnSocketOpened() override;
214 void OnFrameRead(const std::string& message) override;
215 void OnSocketClosed() override;
216 ~ProtocolCommand() override;
218 const std::string command_;
219 const base::Closure callback_;
220 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
222 DISALLOW_COPY_AND_ASSIGN(ProtocolCommand);
225 ProtocolCommand::ProtocolCommand(
226 scoped_refptr<AndroidDeviceManager::Device> device,
227 const std::string& socket,
228 const std::string& debug_url,
229 const std::string& command,
230 const base::Closure callback)
231 : command_(command),
232 callback_(callback),
233 web_socket_(device->CreateWebSocket(socket, debug_url, this)) {
236 void ProtocolCommand::OnSocketOpened() {
237 web_socket_->SendFrame(command_);
240 void ProtocolCommand::OnFrameRead(const std::string& message) {
241 delete this;
244 void ProtocolCommand::OnSocketClosed() {
245 delete this;
248 ProtocolCommand::~ProtocolCommand() {
249 if (!callback_.is_null())
250 callback_.Run();
253 } // namespace
255 // static
256 DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() {
257 return Singleton<DevToolsAndroidBridge::Factory>::get();
260 // static
261 DevToolsAndroidBridge* DevToolsAndroidBridge::Factory::GetForProfile(
262 Profile* profile) {
263 return static_cast<DevToolsAndroidBridge*>(GetInstance()->
264 GetServiceForBrowserContext(profile, true));
267 DevToolsAndroidBridge::Factory::Factory()
268 : BrowserContextKeyedServiceFactory(
269 "DevToolsAndroidBridge",
270 BrowserContextDependencyManager::GetInstance()) {
271 if (IsWebRTCDeviceProviderEnabled()) {
272 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
273 DependsOn(SigninManagerFactory::GetInstance());
277 DevToolsAndroidBridge::Factory::~Factory() {}
279 KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
280 content::BrowserContext* context) const {
281 Profile* profile = Profile::FromBrowserContext(context);
283 ProfileOAuth2TokenService* token_service = nullptr;
284 SigninManagerBase* signin_manager = nullptr;
286 if (IsWebRTCDeviceProviderEnabled()) {
287 token_service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
288 signin_manager = SigninManagerFactory::GetForProfile(profile);
291 return new DevToolsAndroidBridge(
292 profile, signin_manager, token_service);
295 // AgentHostDelegate ----------------------------------------------------------
297 class DevToolsAndroidBridge::AgentHostDelegate
298 : public content::DevToolsExternalAgentProxyDelegate,
299 public AndroidDeviceManager::AndroidWebSocket::Delegate {
300 public:
301 static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost(
302 DevToolsAndroidBridge* bridge,
303 const std::string& id,
304 const BrowserId& browser_id,
305 const std::string& debug_url);
307 private:
308 AgentHostDelegate(
309 DevToolsAndroidBridge* bridge,
310 const std::string& id,
311 const BrowserId& browser_id,
312 const std::string& debug_url);
313 ~AgentHostDelegate() override;
314 void Attach(content::DevToolsExternalAgentProxy* proxy) override;
315 void Detach() override;
316 void SendMessageToBackend(const std::string& message) override;
317 void OnSocketOpened() override;
318 void OnFrameRead(const std::string& message) override;
319 void OnSocketClosed() override;
321 std::string id_;
322 base::WeakPtr<DevToolsAndroidBridge> bridge_;
323 BrowserId browser_id_;
324 std::string debug_url_;
325 bool socket_opened_;
326 std::vector<std::string> pending_messages_;
327 scoped_refptr<AndroidDeviceManager::Device> device_;
328 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
329 content::DevToolsAgentHost* agent_host_;
330 content::DevToolsExternalAgentProxy* proxy_;
331 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
334 // static
335 scoped_refptr<content::DevToolsAgentHost>
336 DevToolsAndroidBridge::AgentHostDelegate::GetOrCreateAgentHost(
337 DevToolsAndroidBridge* bridge,
338 const std::string& id,
339 const BrowserId& browser_id,
340 const std::string& debug_url) {
341 DCHECK_CURRENTLY_ON(BrowserThread::UI);
342 AgentHostDelegates::iterator it = bridge->host_delegates_.find(id);
343 if (it != bridge->host_delegates_.end())
344 return it->second->agent_host_;
346 AgentHostDelegate* delegate =
347 new AgentHostDelegate(bridge, id, browser_id, debug_url);
348 scoped_refptr<content::DevToolsAgentHost> result =
349 content::DevToolsAgentHost::Create(delegate);
350 delegate->agent_host_ = result.get();
351 return result;
354 DevToolsAndroidBridge::AgentHostDelegate::AgentHostDelegate(
355 DevToolsAndroidBridge* bridge,
356 const std::string& id,
357 const BrowserId& browser_id,
358 const std::string& debug_url)
359 : id_(id),
360 bridge_(bridge->AsWeakPtr()),
361 browser_id_(browser_id),
362 debug_url_(debug_url),
363 socket_opened_(false),
364 agent_host_(NULL),
365 proxy_(NULL) {
366 bridge_->host_delegates_[id] = this;
369 DevToolsAndroidBridge::AgentHostDelegate::~AgentHostDelegate() {
370 if (bridge_)
371 bridge_->host_delegates_.erase(id_);
374 void DevToolsAndroidBridge::AgentHostDelegate::Attach(
375 content::DevToolsExternalAgentProxy* proxy) {
376 proxy_ = proxy;
377 content::RecordAction(browser_id_.second.find(kWebViewSocketPrefix) == 0 ?
378 base::UserMetricsAction("DevTools_InspectAndroidWebView") :
379 base::UserMetricsAction("DevTools_InspectAndroidPage"));
381 // Retain the device so it's not released until AgentHost is detached.
382 if (bridge_)
383 device_ = bridge_->FindDevice(browser_id_.first);
384 if (!device_.get())
385 return;
387 web_socket_.reset(
388 device_->CreateWebSocket(browser_id_.second, debug_url_, this));
391 void DevToolsAndroidBridge::AgentHostDelegate::Detach() {
392 web_socket_.reset();
393 device_ = nullptr;
396 void DevToolsAndroidBridge::AgentHostDelegate::SendMessageToBackend(
397 const std::string& message) {
398 if (socket_opened_)
399 web_socket_->SendFrame(message);
400 else
401 pending_messages_.push_back(message);
404 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketOpened() {
405 socket_opened_ = true;
406 for (std::vector<std::string>::iterator it = pending_messages_.begin();
407 it != pending_messages_.end(); ++it) {
408 SendMessageToBackend(*it);
410 pending_messages_.clear();
413 void DevToolsAndroidBridge::AgentHostDelegate::OnFrameRead(
414 const std::string& message) {
415 if (proxy_)
416 proxy_->DispatchOnClientHost(message);
419 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketClosed() {
420 if (proxy_)
421 proxy_->ConnectionClosed();
424 //// RemotePageTarget ----------------------------------------------
426 class DevToolsAndroidBridge::RemotePageTarget : public DevToolsTargetImpl {
427 public:
428 RemotePageTarget(DevToolsAndroidBridge* bridge,
429 const BrowserId& browser_id,
430 const base::DictionaryValue& value);
431 ~RemotePageTarget() override;
433 // DevToolsTargetImpl overrides.
434 std::string GetId() const override;
435 bool IsAttached() const override;
436 bool Activate() const override;
437 bool Close() const override;
438 void Inspect(Profile* profile) const override;
439 void Reload() const override;
441 private:
442 base::WeakPtr<DevToolsAndroidBridge> bridge_;
443 BrowserId browser_id_;
444 std::string debug_url_;
445 std::string frontend_url_;
446 std::string remote_id_;
447 std::string remote_type_;
448 std::string local_id_;
449 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
452 static std::string GetStringProperty(const base::DictionaryValue& value,
453 const std::string& name) {
454 std::string result;
455 value.GetString(name, &result);
456 return result;
459 static std::string BuildUniqueTargetId(
460 const DevToolsAndroidBridge::BrowserId& browser_id,
461 const base::DictionaryValue& value) {
462 return base::StringPrintf("%s:%s:%s", browser_id.first.c_str(),
463 browser_id.second.c_str(), GetStringProperty(value, "id").c_str());
466 static std::string GetFrontendURL(const base::DictionaryValue& value) {
467 std::string frontend_url = GetStringProperty(value, "devtoolsFrontendUrl");
468 size_t ws_param = frontend_url.find("?ws");
469 if (ws_param != std::string::npos)
470 frontend_url = frontend_url.substr(0, ws_param);
471 if (frontend_url.find("http:") == 0)
472 frontend_url = "https:" + frontend_url.substr(5);
473 return frontend_url;
476 static std::string GetDebugURL(const base::DictionaryValue& value) {
477 std::string debug_url = GetStringProperty(value, "webSocketDebuggerUrl");
479 if (debug_url.find("ws://") == 0)
480 debug_url = debug_url.substr(5);
481 else
482 debug_url = std::string();
483 return debug_url;
486 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
487 DevToolsAndroidBridge* bridge,
488 const BrowserId& browser_id,
489 const base::DictionaryValue& value)
490 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
491 bridge,
492 BuildUniqueTargetId(browser_id, value),
493 browser_id,
494 GetDebugURL(value))),
495 bridge_(bridge->AsWeakPtr()),
496 browser_id_(browser_id),
497 debug_url_(GetDebugURL(value)),
498 frontend_url_(GetFrontendURL(value)),
499 remote_id_(GetStringProperty(value, "id")),
500 remote_type_(GetStringProperty(value, "type")),
501 local_id_(BuildUniqueTargetId(browser_id, value)) {
502 set_type("adb_page");
503 set_url(GURL(GetStringProperty(value, "url")));
504 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
505 GetStringProperty(value, "title")))));
506 set_description(GetStringProperty(value, "description"));
507 set_favicon_url(GURL(GetStringProperty(value, "faviconUrl")));
508 debug_url_ = GetDebugURL(value);
511 DevToolsAndroidBridge::RemotePageTarget::~RemotePageTarget() {
514 std::string DevToolsAndroidBridge::RemotePageTarget::GetId() const {
515 return local_id_;
518 bool DevToolsAndroidBridge::RemotePageTarget::IsAttached() const {
519 return debug_url_.empty();
522 static void NoOp(int, const std::string&) {}
524 void DevToolsAndroidBridge::RemotePageTarget::Inspect(Profile* profile) const {
525 Activate();
526 bool isWorker = remote_type_ == kTargetTypeWorker ||
527 remote_type_ == kTargetTypeServiceWorker;
528 DevToolsWindow::OpenExternalFrontend(profile, frontend_url_, GetAgentHost(),
529 isWorker);
532 bool DevToolsAndroidBridge::RemotePageTarget::Activate() const {
533 if (!bridge_)
534 return false;
536 std::string request = base::StringPrintf(kActivatePageRequest,
537 remote_id_.c_str());
538 bridge_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
539 return true;
542 bool DevToolsAndroidBridge::RemotePageTarget::Close() const {
543 if (!bridge_)
544 return false;
546 std::string request = base::StringPrintf(kClosePageRequest,
547 remote_id_.c_str());
548 bridge_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
549 return true;
552 void DevToolsAndroidBridge::RemotePageTarget::Reload() const {
553 if (!bridge_)
554 return;
556 bridge_->SendProtocolCommand(browser_id_, debug_url_, kPageReloadCommand,
557 NULL, base::Closure());
560 // DevToolsAndroidBridge::RemotePage ------------------------------------------
562 DevToolsAndroidBridge::RemotePage::RemotePage(const BrowserId& browser_id,
563 const base::DictionaryValue& dict)
564 : browser_id_(browser_id),
565 frontend_url_(GetFrontendURL(dict)),
566 dict_(dict.DeepCopy()) {
569 DevToolsAndroidBridge::RemotePage::~RemotePage() {
572 // DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
574 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
575 const std::string& serial,
576 const AndroidDeviceManager::BrowserInfo& browser_info)
577 : browser_id_(std::make_pair(serial, browser_info.socket_name)),
578 display_name_(browser_info.display_name),
579 user_(browser_info.user),
580 type_(browser_info.type) {
583 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() {
584 return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
587 std::string DevToolsAndroidBridge::RemoteBrowser::GetId() {
588 return serial() + ":" + socket();
591 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
592 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() {
593 ParsedVersion result;
594 std::vector<std::string> parts;
595 Tokenize(version_, ".", &parts);
596 for (size_t i = 0; i != parts.size(); ++i) {
597 int value = 0;
598 base::StringToInt(parts[i], &value);
599 result.push_back(value);
601 return result;
604 DevToolsTargetImpl*
605 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr<RemotePage> page) {
606 return new RemotePageTarget(this, page->browser_id_, *page->dict_);
609 void DevToolsAndroidBridge::SendJsonRequest(
610 const BrowserId& browser_id,
611 const std::string& request,
612 const JsonRequestCallback& callback) {
613 scoped_refptr<AndroidDeviceManager::Device> device(
614 FindDevice(browser_id.first));
615 if (!device.get()) {
616 callback.Run(net::ERR_FAILED, std::string());
617 return;
619 device->SendJsonRequest(browser_id.second, request, callback);
622 void DevToolsAndroidBridge::SendProtocolCommand(
623 const BrowserId& browser_id,
624 const std::string& debug_url,
625 const std::string& method,
626 scoped_ptr<base::DictionaryValue> params,
627 const base::Closure callback) {
628 DCHECK_CURRENTLY_ON(BrowserThread::UI);
629 if (debug_url.empty())
630 return;
631 scoped_refptr<AndroidDeviceManager::Device> device(
632 FindDevice(browser_id.first));
633 if (!device.get()) {
634 callback.Run();
635 return;
637 new ProtocolCommand(
638 device, browser_id.second, debug_url,
639 DevToolsProtocol::SerializeCommand(1, method, params.Pass()),
640 callback);
643 scoped_refptr<content::DevToolsAgentHost>
644 DevToolsAndroidBridge::GetBrowserAgentHost(
645 scoped_refptr<RemoteBrowser> browser) {
646 return AgentHostDelegate::GetOrCreateAgentHost(
647 this,
648 "adb:" + browser->serial() + ":" + browser->socket(),
649 browser->browser_id_,
650 kBrowserTargetSocket);
653 void DevToolsAndroidBridge::SendJsonRequest(
654 const std::string& browser_id_str,
655 const std::string& url,
656 const JsonRequestCallback& callback) {
657 BrowserId browser_id;
658 if (!BrowserIdFromString(browser_id_str, &browser_id)) {
659 callback.Run(net::ERR_FAILED, std::string());
660 return;
662 SendJsonRequest(browser_id, url, callback);
665 scoped_refptr<AndroidDeviceManager::Device> DevToolsAndroidBridge::FindDevice(
666 const std::string& serial) {
667 DeviceMap::iterator it = device_map_.find(serial);
668 return it == device_map_.end() ? nullptr : it->second;
671 void DevToolsAndroidBridge::OpenRemotePage(scoped_refptr<RemoteBrowser> browser,
672 const std::string& input_url) {
673 DCHECK_CURRENTLY_ON(BrowserThread::UI);
674 GURL gurl(input_url);
675 if (!gurl.is_valid()) {
676 gurl = GURL("http://" + input_url);
677 if (!gurl.is_valid())
678 return;
680 std::string url = gurl.spec();
681 RemoteBrowser::ParsedVersion parsed_version = browser->GetParsedVersion();
683 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
684 std::string request =
685 base::StringPrintf(kNewPageRequestWithURL, query.c_str());
686 SendJsonRequest(browser->browser_id_, request, base::Bind(&NoOp));
689 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
692 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
694 DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
695 const std::string& serial,
696 const AndroidDeviceManager::DeviceInfo& device_info)
697 : serial_(serial),
698 model_(device_info.model),
699 connected_(device_info.connected),
700 screen_size_(device_info.screen_size) {
701 for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it =
702 device_info.browser_info.begin();
703 it != device_info.browser_info.end();
704 ++it) {
705 browsers_.push_back(new RemoteBrowser(serial, *it));
709 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
712 // DevToolsAndroidBridge ------------------------------------------------------
714 DevToolsAndroidBridge::DevToolsAndroidBridge(
715 Profile* profile,
716 SigninManagerBase* signin_manager,
717 ProfileOAuth2TokenService* const token_service)
718 : profile_(profile),
719 signin_manager_(signin_manager),
720 token_service_(token_service),
721 device_manager_(AndroidDeviceManager::Create()),
722 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault)),
723 port_forwarding_controller_(new PortForwardingController(profile, this)),
724 weak_factory_(this) {
725 DCHECK_CURRENTLY_ON(BrowserThread::UI);
726 pref_change_registrar_.Init(profile_->GetPrefs());
727 pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
728 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
729 base::Unretained(this)));
730 CreateDeviceProviders();
733 void DevToolsAndroidBridge::AddDeviceListListener(
734 DeviceListListener* listener) {
735 DCHECK_CURRENTLY_ON(BrowserThread::UI);
736 bool polling_was_off = !NeedsDeviceListPolling();
737 device_list_listeners_.push_back(listener);
738 if (polling_was_off)
739 StartDeviceListPolling();
742 void DevToolsAndroidBridge::RemoveDeviceListListener(
743 DeviceListListener* listener) {
744 DCHECK_CURRENTLY_ON(BrowserThread::UI);
745 DeviceListListeners::iterator it = std::find(
746 device_list_listeners_.begin(), device_list_listeners_.end(), listener);
747 DCHECK(it != device_list_listeners_.end());
748 device_list_listeners_.erase(it);
749 if (!NeedsDeviceListPolling())
750 StopDeviceListPolling();
753 void DevToolsAndroidBridge::AddDeviceCountListener(
754 DeviceCountListener* listener) {
755 device_count_listeners_.push_back(listener);
756 if (device_count_listeners_.size() == 1)
757 StartDeviceCountPolling();
760 void DevToolsAndroidBridge::RemoveDeviceCountListener(
761 DeviceCountListener* listener) {
762 DCHECK_CURRENTLY_ON(BrowserThread::UI);
763 DeviceCountListeners::iterator it = std::find(
764 device_count_listeners_.begin(), device_count_listeners_.end(), listener);
765 DCHECK(it != device_count_listeners_.end());
766 device_count_listeners_.erase(it);
767 if (device_count_listeners_.empty())
768 StopDeviceCountPolling();
771 void DevToolsAndroidBridge::AddPortForwardingListener(
772 PortForwardingListener* listener) {
773 bool polling_was_off = !NeedsDeviceListPolling();
774 port_forwarding_listeners_.push_back(listener);
775 if (polling_was_off)
776 StartDeviceListPolling();
779 void DevToolsAndroidBridge::RemovePortForwardingListener(
780 PortForwardingListener* listener) {
781 PortForwardingListeners::iterator it = std::find(
782 port_forwarding_listeners_.begin(),
783 port_forwarding_listeners_.end(),
784 listener);
785 DCHECK(it != port_forwarding_listeners_.end());
786 port_forwarding_listeners_.erase(it);
787 if (!NeedsDeviceListPolling())
788 StopDeviceListPolling();
791 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string& agent_id) {
792 DCHECK_CURRENTLY_ON(BrowserThread::UI);
793 return host_delegates_.find(agent_id) != host_delegates_.end();
796 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
797 DCHECK_CURRENTLY_ON(BrowserThread::UI);
798 DCHECK(device_list_listeners_.empty());
799 DCHECK(device_count_listeners_.empty());
800 DCHECK(port_forwarding_listeners_.empty());
803 void DevToolsAndroidBridge::StartDeviceListPolling() {
804 device_list_callback_.Reset(
805 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, AsWeakPtr()));
806 RequestDeviceList(device_list_callback_.callback());
809 void DevToolsAndroidBridge::StopDeviceListPolling() {
810 device_list_callback_.Cancel();
811 device_map_.clear();
814 bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
815 return !device_list_listeners_.empty() || !port_forwarding_listeners_.empty();
818 void DevToolsAndroidBridge::RequestDeviceList(
819 const DeviceListCallback& callback) {
820 DCHECK_CURRENTLY_ON(BrowserThread::UI);
822 if (!NeedsDeviceListPolling() ||
823 !callback.Equals(device_list_callback_.callback()))
824 return;
826 new DiscoveryRequest(device_manager_.get(), callback);
829 void DevToolsAndroidBridge::ReceivedDeviceList(
830 const CompleteDevices& complete_devices) {
831 DCHECK_CURRENTLY_ON(BrowserThread::UI);
833 device_map_.clear();
834 RemoteDevices remote_devices;
835 for (const auto& pair : complete_devices) {
836 device_map_[pair.first->serial()] = pair.first;
837 remote_devices.push_back(pair.second);
840 DeviceListListeners copy(device_list_listeners_);
841 for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it)
842 (*it)->DeviceListChanged(remote_devices);
844 ForwardingStatus status =
845 port_forwarding_controller_->DeviceListChanged(remote_devices);
846 PortForwardingListeners forwarding_listeners(port_forwarding_listeners_);
847 for (PortForwardingListeners::iterator it = forwarding_listeners.begin();
848 it != forwarding_listeners.end(); ++it) {
849 (*it)->PortStatusChanged(status);
852 if (!NeedsDeviceListPolling())
853 return;
855 task_scheduler_.Run(
856 base::Bind(&DevToolsAndroidBridge::RequestDeviceList,
857 AsWeakPtr(), device_list_callback_.callback()));
860 void DevToolsAndroidBridge::StartDeviceCountPolling() {
861 device_count_callback_.Reset(
862 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, AsWeakPtr()));
863 RequestDeviceCount(device_count_callback_.callback());
866 void DevToolsAndroidBridge::StopDeviceCountPolling() {
867 device_count_callback_.Cancel();
870 void DevToolsAndroidBridge::RequestDeviceCount(
871 const base::Callback<void(int)>& callback) {
872 DCHECK_CURRENTLY_ON(BrowserThread::UI);
874 if (device_count_listeners_.empty() ||
875 !callback.Equals(device_count_callback_.callback()))
876 return;
878 UsbDeviceProvider::CountDevices(callback);
881 void DevToolsAndroidBridge::ReceivedDeviceCount(int count) {
882 DCHECK_CURRENTLY_ON(BrowserThread::UI);
884 DeviceCountListeners copy(device_count_listeners_);
885 for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it)
886 (*it)->DeviceCountChanged(count);
888 if (device_count_listeners_.empty())
889 return;
891 task_scheduler_.Run(
892 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount,
893 AsWeakPtr(), device_count_callback_.callback()));
896 // static
897 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure& task) {
898 BrowserThread::PostDelayedTask(
899 BrowserThread::UI,
900 FROM_HERE,
901 task,
902 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
905 void DevToolsAndroidBridge::CreateDeviceProviders() {
906 AndroidDeviceManager::DeviceProviders device_providers;
907 #if defined(DEBUG_DEVTOOLS)
908 RemoteDebuggingServer::EnableTetheringForDebug();
909 // We cannot rely on command line switch here as we might want to connect
910 // to another instance of Chrome. Using hard-coded port number instead.
911 const int kDefaultDebuggingPort = 9222;
912 device_providers.push_back(new SelfAsDeviceProvider(kDefaultDebuggingPort));
913 #endif
914 device_providers.push_back(new AdbDeviceProvider());
916 PrefService* service = profile_->GetPrefs();
917 const PrefService::Preference* pref =
918 service->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled);
919 const base::Value* pref_value = pref->GetValue();
921 bool enabled;
922 if (pref_value->GetAsBoolean(&enabled) && enabled) {
923 device_providers.push_back(new UsbDeviceProvider(profile_));
926 if (IsWebRTCDeviceProviderEnabled()) {
927 device_providers.push_back(
928 new WebRTCDeviceProvider(profile_, signin_manager_, token_service_));
931 device_manager_->SetDeviceProviders(device_providers);
932 if (NeedsDeviceListPolling()) {
933 StopDeviceListPolling();
934 StartDeviceListPolling();