Add more checks to investigate SupervisedUserPrefStore crash at startup.
[chromium-blink-merge.git] / chrome / browser / devtools / device / devtools_android_bridge.cc
blobd6c7a9b97d75724b7ef56fbc4a5ac7165b295d09
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 kNewPageRequest[] = "/json/new";
57 const char kNewPageRequestWithURL[] = "/json/new?%s";
58 const char kActivatePageRequest[] = "/json/activate/%s";
59 const char kBrowserTargetSocket[] = "/devtools/browser";
60 const int kAdbPollingIntervalMs = 1000;
62 const char kUrlParam[] = "url";
63 const char kPageReloadCommand[] = "Page.reload";
64 const char kPageNavigateCommand[] = "Page.navigate";
66 const int kMinVersionNewWithURL = 32;
67 const int kNewPageNavigateDelayMs = 500;
69 bool IsWebRTCDeviceProviderEnabled() {
70 return base::CommandLine::ForCurrentProcess()->HasSwitch(
71 switches::kEnableDevToolsExperiments);
74 } // namespace
76 // DiscoveryRequest -----------------------------------------------------
78 class DevToolsAndroidBridge::DiscoveryRequest
79 : public base::RefCountedThreadSafe<DiscoveryRequest,
80 BrowserThread::DeleteOnUIThread> {
81 public:
82 DiscoveryRequest(AndroidDeviceManager* device_manager,
83 const DeviceListCallback& callback);
84 private:
85 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
86 friend class base::DeleteHelper<DiscoveryRequest>;
87 virtual ~DiscoveryRequest();
89 void ReceivedDevices(const AndroidDeviceManager::Devices& devices);
90 void ReceivedDeviceInfo(scoped_refptr<AndroidDeviceManager::Device> device,
91 const AndroidDeviceManager::DeviceInfo& device_info);
92 void ReceivedVersion(scoped_refptr<RemoteBrowser>,
93 int result,
94 const std::string& response);
95 void ReceivedPages(scoped_refptr<RemoteBrowser>,
96 int result,
97 const std::string& response);
99 DeviceListCallback callback_;
100 CompleteDevices complete_devices_;
103 DevToolsAndroidBridge::DiscoveryRequest::DiscoveryRequest(
104 AndroidDeviceManager* device_manager,
105 const DeviceListCallback& callback)
106 : callback_(callback) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108 device_manager->QueryDevices(
109 base::Bind(&DiscoveryRequest::ReceivedDevices, this));
112 DevToolsAndroidBridge::DiscoveryRequest::~DiscoveryRequest() {
113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114 callback_.Run(complete_devices_);
117 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDevices(
118 const AndroidDeviceManager::Devices& devices) {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120 for (const auto& device : devices) {
121 device->QueryDeviceInfo(
122 base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, device));
126 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDeviceInfo(
127 scoped_refptr<AndroidDeviceManager::Device> device,
128 const AndroidDeviceManager::DeviceInfo& device_info) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130 scoped_refptr<RemoteDevice> remote_device =
131 new RemoteDevice(device->serial(), device_info);
132 complete_devices_.push_back(std::make_pair(device, remote_device));
133 for (RemoteBrowsers::iterator it = remote_device->browsers().begin();
134 it != remote_device->browsers().end(); ++it) {
135 device->SendJsonRequest(
136 (*it)->socket(),
137 kVersionRequest,
138 base::Bind(&DiscoveryRequest::ReceivedVersion, this, *it));
139 device->SendJsonRequest(
140 (*it)->socket(),
141 kPageListRequest,
142 base::Bind(&DiscoveryRequest::ReceivedPages, this, *it));
146 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedVersion(
147 scoped_refptr<RemoteBrowser> browser,
148 int result,
149 const std::string& response) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151 if (result < 0)
152 return;
153 // Parse version, append to package name if available,
154 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
155 base::DictionaryValue* dict;
156 if (value && value->GetAsDictionary(&dict)) {
157 std::string browser_name;
158 if (dict->GetString("Browser", &browser_name)) {
159 std::vector<std::string> parts;
160 Tokenize(browser_name, "/", &parts);
161 if (parts.size() == 2)
162 browser->version_ = parts[1];
163 else
164 browser->version_ = browser_name;
166 std::string package;
167 if (dict->GetString("Android-Package", &package)) {
168 browser->display_name_ =
169 AndroidDeviceManager::GetBrowserName(browser->socket(), package);
174 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedPages(
175 scoped_refptr<RemoteBrowser> browser,
176 int result,
177 const std::string& response) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179 if (result < 0)
180 return;
181 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
182 base::ListValue* list_value;
183 if (value && value->GetAsList(&list_value)) {
184 for (const auto& page_value : *list_value) {
185 base::DictionaryValue* dict;
186 if (page_value->GetAsDictionary(&dict)) {
187 browser->pages_.push_back(
188 new RemotePage(browser->browser_id_, *dict, browser->IsWebView()));
194 // ProtocolCommand ------------------------------------------------------------
196 namespace {
198 class ProtocolCommand
199 : public AndroidDeviceManager::AndroidWebSocket::Delegate {
200 public:
201 ProtocolCommand(
202 scoped_refptr<AndroidDeviceManager::Device> device,
203 const std::string& socket,
204 const std::string& debug_url,
205 const std::string& command,
206 const base::Closure callback);
208 private:
209 void OnSocketOpened() override;
210 void OnFrameRead(const std::string& message) override;
211 void OnSocketClosed() override;
212 ~ProtocolCommand() override;
214 const std::string command_;
215 const base::Closure callback_;
216 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
218 DISALLOW_COPY_AND_ASSIGN(ProtocolCommand);
221 ProtocolCommand::ProtocolCommand(
222 scoped_refptr<AndroidDeviceManager::Device> device,
223 const std::string& socket,
224 const std::string& debug_url,
225 const std::string& command,
226 const base::Closure callback)
227 : command_(command),
228 callback_(callback),
229 web_socket_(device->CreateWebSocket(socket, debug_url, this)) {
232 void ProtocolCommand::OnSocketOpened() {
233 web_socket_->SendFrame(command_);
236 void ProtocolCommand::OnFrameRead(const std::string& message) {
237 delete this;
240 void ProtocolCommand::OnSocketClosed() {
241 delete this;
244 ProtocolCommand::~ProtocolCommand() {
245 if (!callback_.is_null())
246 callback_.Run();
249 } // namespace
251 // static
252 DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() {
253 return Singleton<DevToolsAndroidBridge::Factory>::get();
256 // static
257 DevToolsAndroidBridge* DevToolsAndroidBridge::Factory::GetForProfile(
258 Profile* profile) {
259 return static_cast<DevToolsAndroidBridge*>(GetInstance()->
260 GetServiceForBrowserContext(profile, true));
263 DevToolsAndroidBridge::Factory::Factory()
264 : BrowserContextKeyedServiceFactory(
265 "DevToolsAndroidBridge",
266 BrowserContextDependencyManager::GetInstance()) {
267 if (IsWebRTCDeviceProviderEnabled()) {
268 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
269 DependsOn(SigninManagerFactory::GetInstance());
273 DevToolsAndroidBridge::Factory::~Factory() {}
275 KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
276 content::BrowserContext* context) const {
277 Profile* profile = Profile::FromBrowserContext(context);
279 ProfileOAuth2TokenService* token_service = nullptr;
280 SigninManagerBase* signin_manager = nullptr;
282 if (IsWebRTCDeviceProviderEnabled()) {
283 token_service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
284 signin_manager = SigninManagerFactory::GetForProfile(profile);
287 return new DevToolsAndroidBridge(
288 profile, signin_manager, token_service);
291 // AgentHostDelegate ----------------------------------------------------------
293 class DevToolsAndroidBridge::AgentHostDelegate
294 : public content::DevToolsExternalAgentProxyDelegate,
295 public AndroidDeviceManager::AndroidWebSocket::Delegate {
296 public:
297 static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost(
298 DevToolsAndroidBridge* bridge,
299 const std::string& id,
300 const BrowserId& browser_id,
301 const std::string& debug_url,
302 bool is_web_view);
304 private:
305 AgentHostDelegate(
306 DevToolsAndroidBridge* bridge,
307 const std::string& id,
308 const BrowserId& browser_id,
309 const std::string& debug_url,
310 bool is_web_view);
311 ~AgentHostDelegate() override;
312 void Attach(content::DevToolsExternalAgentProxy* proxy) override;
313 void Detach() override;
314 void SendMessageToBackend(const std::string& message) override;
315 void OnSocketOpened() override;
316 void OnFrameRead(const std::string& message) override;
317 void OnSocketClosed() override;
319 std::string id_;
320 base::WeakPtr<DevToolsAndroidBridge> bridge_;
321 BrowserId browser_id_;
322 std::string debug_url_;
323 bool socket_opened_;
324 bool is_web_view_;
325 std::vector<std::string> pending_messages_;
326 scoped_refptr<AndroidDeviceManager::Device> device_;
327 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
328 content::DevToolsAgentHost* agent_host_;
329 content::DevToolsExternalAgentProxy* proxy_;
330 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
333 // static
334 scoped_refptr<content::DevToolsAgentHost>
335 DevToolsAndroidBridge::AgentHostDelegate::GetOrCreateAgentHost(
336 DevToolsAndroidBridge* bridge,
337 const std::string& id,
338 const BrowserId& browser_id,
339 const std::string& debug_url,
340 bool is_web_view) {
341 DCHECK(BrowserThread::CurrentlyOn(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, is_web_view);
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 bool is_web_view)
360 : id_(id),
361 bridge_(bridge->AsWeakPtr()),
362 browser_id_(browser_id),
363 debug_url_(debug_url),
364 socket_opened_(false),
365 is_web_view_(is_web_view),
366 agent_host_(NULL),
367 proxy_(NULL) {
368 bridge_->host_delegates_[id] = this;
371 DevToolsAndroidBridge::AgentHostDelegate::~AgentHostDelegate() {
372 if (bridge_)
373 bridge_->host_delegates_.erase(id_);
376 void DevToolsAndroidBridge::AgentHostDelegate::Attach(
377 content::DevToolsExternalAgentProxy* proxy) {
378 proxy_ = proxy;
379 content::RecordAction(is_web_view_ ?
380 base::UserMetricsAction("DevTools_InspectAndroidWebView") :
381 base::UserMetricsAction("DevTools_InspectAndroidPage"));
383 // Retain the device so it's not released until AgentHost is detached.
384 if (bridge_)
385 device_ = bridge_->FindDevice(browser_id_.first);
386 if (!device_.get())
387 return;
389 web_socket_.reset(
390 device_->CreateWebSocket(browser_id_.second, debug_url_, this));
393 void DevToolsAndroidBridge::AgentHostDelegate::Detach() {
394 web_socket_.reset();
395 device_ = nullptr;
398 void DevToolsAndroidBridge::AgentHostDelegate::SendMessageToBackend(
399 const std::string& message) {
400 if (socket_opened_)
401 web_socket_->SendFrame(message);
402 else
403 pending_messages_.push_back(message);
406 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketOpened() {
407 socket_opened_ = true;
408 for (std::vector<std::string>::iterator it = pending_messages_.begin();
409 it != pending_messages_.end(); ++it) {
410 SendMessageToBackend(*it);
412 pending_messages_.clear();
415 void DevToolsAndroidBridge::AgentHostDelegate::OnFrameRead(
416 const std::string& message) {
417 if (proxy_)
418 proxy_->DispatchOnClientHost(message);
421 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketClosed() {
422 if (proxy_)
423 proxy_->ConnectionClosed();
426 //// RemotePageTarget ----------------------------------------------
428 class DevToolsAndroidBridge::RemotePageTarget : public DevToolsTargetImpl {
429 public:
430 RemotePageTarget(DevToolsAndroidBridge* bridge,
431 const BrowserId& browser_id,
432 const base::DictionaryValue& value,
433 bool is_web_view_);
434 ~RemotePageTarget() override;
436 // DevToolsTargetImpl overrides.
437 std::string GetId() const override;
438 bool IsAttached() const override;
439 bool Activate() const override;
440 bool Close() const override;
441 void Inspect(Profile* profile) const override;
442 void Reload() const override;
444 void Navigate(const std::string& url, base::Closure callback) const;
446 private:
447 base::WeakPtr<DevToolsAndroidBridge> bridge_;
448 BrowserId browser_id_;
449 std::string debug_url_;
450 std::string frontend_url_;
451 std::string remote_id_;
452 std::string remote_type_;
453 std::string local_id_;
454 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
457 static std::string GetStringProperty(const base::DictionaryValue& value,
458 const std::string& name) {
459 std::string result;
460 value.GetString(name, &result);
461 return result;
464 static std::string BuildUniqueTargetId(
465 const DevToolsAndroidBridge::BrowserId& browser_id,
466 const base::DictionaryValue& value) {
467 return base::StringPrintf("%s:%s:%s", browser_id.first.c_str(),
468 browser_id.second.c_str(), GetStringProperty(value, "id").c_str());
471 static std::string GetFrontendURL(const base::DictionaryValue& value) {
472 std::string frontend_url = GetStringProperty(value, "devtoolsFrontendUrl");
473 size_t ws_param = frontend_url.find("?ws");
474 if (ws_param != std::string::npos)
475 frontend_url = frontend_url.substr(0, ws_param);
476 if (frontend_url.find("http:") == 0)
477 frontend_url = "https:" + frontend_url.substr(5);
478 return frontend_url;
481 static std::string GetDebugURL(const base::DictionaryValue& value) {
482 std::string debug_url = GetStringProperty(value, "webSocketDebuggerUrl");
484 if (debug_url.find("ws://") == 0)
485 debug_url = debug_url.substr(5);
486 else
487 debug_url = std::string();
488 return debug_url;
491 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
492 DevToolsAndroidBridge* bridge,
493 const BrowserId& browser_id,
494 const base::DictionaryValue& value,
495 bool is_web_view)
496 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
497 bridge,
498 BuildUniqueTargetId(browser_id, value),
499 browser_id,
500 GetDebugURL(value),
501 is_web_view)),
502 bridge_(bridge->AsWeakPtr()),
503 browser_id_(browser_id),
504 debug_url_(GetDebugURL(value)),
505 frontend_url_(GetFrontendURL(value)),
506 remote_id_(GetStringProperty(value, "id")),
507 remote_type_(GetStringProperty(value, "type")),
508 local_id_(BuildUniqueTargetId(browser_id, value)) {
509 set_type("adb_page");
510 set_url(GURL(GetStringProperty(value, "url")));
511 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
512 GetStringProperty(value, "title")))));
513 set_description(GetStringProperty(value, "description"));
514 set_favicon_url(GURL(GetStringProperty(value, "faviconUrl")));
515 debug_url_ = GetDebugURL(value);
518 DevToolsAndroidBridge::RemotePageTarget::~RemotePageTarget() {
521 std::string DevToolsAndroidBridge::RemotePageTarget::GetId() const {
522 return local_id_;
525 bool DevToolsAndroidBridge::RemotePageTarget::IsAttached() const {
526 return debug_url_.empty();
529 static void NoOp(int, const std::string&) {}
531 void DevToolsAndroidBridge::RemotePageTarget::Inspect(Profile* profile) const {
532 Activate();
533 bool isWorker = remote_type_ == kTargetTypeWorker ||
534 remote_type_ == kTargetTypeServiceWorker;
535 DevToolsWindow::OpenExternalFrontend(profile, frontend_url_, GetAgentHost(),
536 isWorker);
539 bool DevToolsAndroidBridge::RemotePageTarget::Activate() const {
540 if (!bridge_)
541 return false;
543 std::string request = base::StringPrintf(kActivatePageRequest,
544 remote_id_.c_str());
545 bridge_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
546 return true;
549 bool DevToolsAndroidBridge::RemotePageTarget::Close() const {
550 if (!bridge_)
551 return false;
553 std::string request = base::StringPrintf(kClosePageRequest,
554 remote_id_.c_str());
555 bridge_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
556 return true;
559 void DevToolsAndroidBridge::RemotePageTarget::Reload() const {
560 if (!bridge_)
561 return;
563 bridge_->SendProtocolCommand(browser_id_, debug_url_, kPageReloadCommand,
564 NULL, base::Closure());
567 void DevToolsAndroidBridge::RemotePageTarget::Navigate(
568 const std::string& url,
569 base::Closure callback) const {
570 if (!bridge_)
571 return;
573 scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue);
574 params->SetString(kUrlParam, url);
575 bridge_->SendProtocolCommand(browser_id_, debug_url_, kPageNavigateCommand,
576 params.Pass(), callback);
579 // DevToolsAndroidBridge::RemotePage ------------------------------------------
581 DevToolsAndroidBridge::RemotePage::RemotePage(
582 const BrowserId& browser_id,
583 const base::DictionaryValue& dict,
584 bool is_web_view)
585 : browser_id_(browser_id),
586 frontend_url_(GetFrontendURL(dict)),
587 is_web_view_(is_web_view),
588 dict_(dict.DeepCopy()) {
591 DevToolsAndroidBridge::RemotePage::~RemotePage() {
594 // DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
596 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
597 const std::string& serial,
598 const AndroidDeviceManager::BrowserInfo& browser_info)
599 : browser_id_(std::make_pair(serial, browser_info.socket_name)),
600 display_name_(browser_info.display_name),
601 user_(browser_info.user),
602 type_(browser_info.type) {
605 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() {
606 return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
609 bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() {
610 return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView;
613 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
614 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() {
615 ParsedVersion result;
616 std::vector<std::string> parts;
617 Tokenize(version_, ".", &parts);
618 for (size_t i = 0; i != parts.size(); ++i) {
619 int value = 0;
620 base::StringToInt(parts[i], &value);
621 result.push_back(value);
623 return result;
626 DevToolsTargetImpl*
627 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr<RemotePage> page) {
628 return new RemotePageTarget(this, page->browser_id_, *page->dict_,
629 page->is_web_view_);
632 void DevToolsAndroidBridge::SendJsonRequest(
633 const BrowserId& browser_id,
634 const std::string& request,
635 const JsonRequestCallback& callback) {
636 scoped_refptr<AndroidDeviceManager::Device> device(
637 FindDevice(browser_id.first));
638 if (!device.get()) {
639 callback.Run(net::ERR_FAILED, std::string());
640 return;
642 device->SendJsonRequest(browser_id.second, request, callback);
645 void DevToolsAndroidBridge::SendProtocolCommand(
646 const BrowserId& browser_id,
647 const std::string& debug_url,
648 const std::string& method,
649 scoped_ptr<base::DictionaryValue> params,
650 const base::Closure callback) {
651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
652 if (debug_url.empty())
653 return;
654 scoped_refptr<AndroidDeviceManager::Device> device(
655 FindDevice(browser_id.first));
656 if (!device.get()) {
657 callback.Run();
658 return;
660 new ProtocolCommand(
661 device, browser_id.second, debug_url,
662 DevToolsProtocol::SerializeCommand(1, method, params.Pass()),
663 callback);
666 scoped_refptr<content::DevToolsAgentHost>
667 DevToolsAndroidBridge::GetBrowserAgentHost(
668 scoped_refptr<RemoteBrowser> browser) {
669 return AgentHostDelegate::GetOrCreateAgentHost(
670 this,
671 "adb:" + browser->serial() + ":" + browser->socket(),
672 browser->browser_id_,
673 kBrowserTargetSocket,
674 browser->IsWebView());
677 scoped_refptr<AndroidDeviceManager::Device> DevToolsAndroidBridge::FindDevice(
678 const std::string& serial) {
679 DeviceMap::iterator it = device_map_.find(serial);
680 return it == device_map_.end() ? nullptr : it->second;
683 void DevToolsAndroidBridge::RespondToOpenOnUIThread(
684 scoped_refptr<RemoteBrowser> browser,
685 const RemotePageCallback& callback,
686 int result,
687 const std::string& response) {
688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689 if (result < 0) {
690 callback.Run(NULL);
691 return;
693 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
694 base::DictionaryValue* dict;
695 if (value && value->GetAsDictionary(&dict)) {
696 scoped_refptr<RemotePage> new_page(
697 new RemotePage(browser->browser_id_, *dict, browser->IsWebView()));
698 callback.Run(new_page);
702 void DevToolsAndroidBridge::OpenRemotePage(
703 scoped_refptr<RemoteBrowser> browser,
704 const std::string& input_url,
705 const DevToolsAndroidBridge::RemotePageCallback& callback) {
706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
707 GURL gurl(input_url);
708 if (!gurl.is_valid()) {
709 gurl = GURL("http://" + input_url);
710 if (!gurl.is_valid())
711 return;
713 std::string url = gurl.spec();
714 RemoteBrowser::ParsedVersion parsed_version = browser->GetParsedVersion();
716 if (browser->IsChrome() &&
717 !parsed_version.empty() &&
718 parsed_version[0] >= kMinVersionNewWithURL) {
719 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
720 std::string request =
721 base::StringPrintf(kNewPageRequestWithURL, query.c_str());
722 SendJsonRequest(browser->browser_id_, request,
723 base::Bind(&DevToolsAndroidBridge::RespondToOpenOnUIThread,
724 AsWeakPtr(), browser, callback));
725 } else {
726 SendJsonRequest(browser->browser_id_, kNewPageRequest,
727 base::Bind(&DevToolsAndroidBridge::PageCreatedOnUIThread,
728 AsWeakPtr(), browser, callback, url));
732 void DevToolsAndroidBridge::PageCreatedOnUIThread(
733 scoped_refptr<RemoteBrowser> browser,
734 const RemotePageCallback& callback,
735 const std::string& url,
736 int result,
737 const std::string& response) {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
740 if (result < 0)
741 return;
742 // Navigating too soon after the page creation breaks navigation history
743 // (crbug.com/311014). This can be avoided by adding a moderate delay.
744 BrowserThread::PostDelayedTask(
745 BrowserThread::UI, FROM_HERE,
746 base::Bind(&DevToolsAndroidBridge::NavigatePageOnUIThread,
747 AsWeakPtr(), browser, callback, result, response, url),
748 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
751 void DevToolsAndroidBridge::NavigatePageOnUIThread(
752 scoped_refptr<RemoteBrowser> browser,
753 const RemotePageCallback& callback,
754 int result,
755 const std::string& response,
756 const std::string& url) {
757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
758 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
759 base::DictionaryValue* dict;
761 if (value && value->GetAsDictionary(&dict)) {
762 RemotePageTarget new_page(this, browser->browser_id_, *dict,
763 browser->IsWebView());
764 new_page.Navigate(url,
765 base::Bind(&DevToolsAndroidBridge::RespondToOpenOnUIThread,
766 AsWeakPtr(), browser, callback, result, response));
770 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
773 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
775 DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
776 const std::string& serial,
777 const AndroidDeviceManager::DeviceInfo& device_info)
778 : serial_(serial),
779 model_(device_info.model),
780 connected_(device_info.connected),
781 screen_size_(device_info.screen_size) {
782 for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it =
783 device_info.browser_info.begin();
784 it != device_info.browser_info.end();
785 ++it) {
786 browsers_.push_back(new RemoteBrowser(serial, *it));
790 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
793 // DevToolsAndroidBridge ------------------------------------------------------
795 DevToolsAndroidBridge::DevToolsAndroidBridge(
796 Profile* profile,
797 SigninManagerBase* signin_manager,
798 ProfileOAuth2TokenService* const token_service)
799 : profile_(profile),
800 signin_manager_(signin_manager),
801 token_service_(token_service),
802 device_manager_(AndroidDeviceManager::Create()),
803 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault)),
804 port_forwarding_controller_(new PortForwardingController(profile, this)),
805 weak_factory_(this) {
806 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
807 pref_change_registrar_.Init(profile_->GetPrefs());
808 pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
809 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
810 base::Unretained(this)));
811 CreateDeviceProviders();
814 void DevToolsAndroidBridge::AddDeviceListListener(
815 DeviceListListener* listener) {
816 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
817 bool polling_was_off = !NeedsDeviceListPolling();
818 device_list_listeners_.push_back(listener);
819 if (polling_was_off)
820 StartDeviceListPolling();
823 void DevToolsAndroidBridge::RemoveDeviceListListener(
824 DeviceListListener* listener) {
825 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
826 DeviceListListeners::iterator it = std::find(
827 device_list_listeners_.begin(), device_list_listeners_.end(), listener);
828 DCHECK(it != device_list_listeners_.end());
829 device_list_listeners_.erase(it);
830 if (!NeedsDeviceListPolling())
831 StopDeviceListPolling();
834 void DevToolsAndroidBridge::AddDeviceCountListener(
835 DeviceCountListener* listener) {
836 device_count_listeners_.push_back(listener);
837 if (device_count_listeners_.size() == 1)
838 StartDeviceCountPolling();
841 void DevToolsAndroidBridge::RemoveDeviceCountListener(
842 DeviceCountListener* listener) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
844 DeviceCountListeners::iterator it = std::find(
845 device_count_listeners_.begin(), device_count_listeners_.end(), listener);
846 DCHECK(it != device_count_listeners_.end());
847 device_count_listeners_.erase(it);
848 if (device_count_listeners_.empty())
849 StopDeviceCountPolling();
852 void DevToolsAndroidBridge::AddPortForwardingListener(
853 PortForwardingListener* listener) {
854 bool polling_was_off = !NeedsDeviceListPolling();
855 port_forwarding_listeners_.push_back(listener);
856 if (polling_was_off)
857 StartDeviceListPolling();
860 void DevToolsAndroidBridge::RemovePortForwardingListener(
861 PortForwardingListener* listener) {
862 PortForwardingListeners::iterator it = std::find(
863 port_forwarding_listeners_.begin(),
864 port_forwarding_listeners_.end(),
865 listener);
866 DCHECK(it != port_forwarding_listeners_.end());
867 port_forwarding_listeners_.erase(it);
868 if (!NeedsDeviceListPolling())
869 StopDeviceListPolling();
872 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string& agent_id) {
873 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
874 return host_delegates_.find(agent_id) != host_delegates_.end();
877 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
879 DCHECK(device_list_listeners_.empty());
880 DCHECK(device_count_listeners_.empty());
881 DCHECK(port_forwarding_listeners_.empty());
884 void DevToolsAndroidBridge::StartDeviceListPolling() {
885 device_list_callback_.Reset(
886 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, AsWeakPtr()));
887 RequestDeviceList(device_list_callback_.callback());
890 void DevToolsAndroidBridge::StopDeviceListPolling() {
891 device_list_callback_.Cancel();
892 device_map_.clear();
895 bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
896 return !device_list_listeners_.empty() || !port_forwarding_listeners_.empty();
899 void DevToolsAndroidBridge::RequestDeviceList(
900 const DeviceListCallback& callback) {
901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
903 if (!NeedsDeviceListPolling() ||
904 !callback.Equals(device_list_callback_.callback()))
905 return;
907 new DiscoveryRequest(device_manager_.get(), callback);
910 void DevToolsAndroidBridge::ReceivedDeviceList(
911 const CompleteDevices& complete_devices) {
912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914 device_map_.clear();
915 RemoteDevices remote_devices;
916 for (const auto& pair : complete_devices) {
917 device_map_[pair.first->serial()] = pair.first;
918 remote_devices.push_back(pair.second);
921 DeviceListListeners copy(device_list_listeners_);
922 for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it)
923 (*it)->DeviceListChanged(remote_devices);
925 ForwardingStatus status =
926 port_forwarding_controller_->DeviceListChanged(remote_devices);
927 PortForwardingListeners forwarding_listeners(port_forwarding_listeners_);
928 for (PortForwardingListeners::iterator it = forwarding_listeners.begin();
929 it != forwarding_listeners.end(); ++it) {
930 (*it)->PortStatusChanged(status);
933 if (!NeedsDeviceListPolling())
934 return;
936 task_scheduler_.Run(
937 base::Bind(&DevToolsAndroidBridge::RequestDeviceList,
938 AsWeakPtr(), device_list_callback_.callback()));
941 void DevToolsAndroidBridge::StartDeviceCountPolling() {
942 device_count_callback_.Reset(
943 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, AsWeakPtr()));
944 RequestDeviceCount(device_count_callback_.callback());
947 void DevToolsAndroidBridge::StopDeviceCountPolling() {
948 device_count_callback_.Cancel();
951 void DevToolsAndroidBridge::RequestDeviceCount(
952 const base::Callback<void(int)>& callback) {
953 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
955 if (device_count_listeners_.empty() ||
956 !callback.Equals(device_count_callback_.callback()))
957 return;
959 UsbDeviceProvider::CountDevices(callback);
962 void DevToolsAndroidBridge::ReceivedDeviceCount(int count) {
963 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
965 DeviceCountListeners copy(device_count_listeners_);
966 for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it)
967 (*it)->DeviceCountChanged(count);
969 if (device_count_listeners_.empty())
970 return;
972 task_scheduler_.Run(
973 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount,
974 AsWeakPtr(), device_count_callback_.callback()));
977 // static
978 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure& task) {
979 BrowserThread::PostDelayedTask(
980 BrowserThread::UI,
981 FROM_HERE,
982 task,
983 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
986 void DevToolsAndroidBridge::CreateDeviceProviders() {
987 AndroidDeviceManager::DeviceProviders device_providers;
988 #if defined(DEBUG_DEVTOOLS)
989 RemoteDebuggingServer::EnableTetheringForDebug();
990 // We cannot rely on command line switch here as we might want to connect
991 // to another instance of Chrome. Using hard-coded port number instead.
992 const int kDefaultDebuggingPort = 9222;
993 device_providers.push_back(new SelfAsDeviceProvider(kDefaultDebuggingPort));
994 #endif
995 device_providers.push_back(new AdbDeviceProvider());
997 PrefService* service = profile_->GetPrefs();
998 const PrefService::Preference* pref =
999 service->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled);
1000 const base::Value* pref_value = pref->GetValue();
1002 bool enabled;
1003 if (pref_value->GetAsBoolean(&enabled) && enabled) {
1004 device_providers.push_back(new UsbDeviceProvider(profile_));
1007 if (IsWebRTCDeviceProviderEnabled()) {
1008 device_providers.push_back(
1009 new WebRTCDeviceProvider(profile_, signin_manager_, token_service_));
1012 device_manager_->SetDeviceProviders(device_providers);
1013 if (NeedsDeviceListPolling()) {
1014 StopDeviceListPolling();
1015 StartDeviceListPolling();