1 // Copyright 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/android_device.h" 6 #include "chrome/browser/devtools/devtools_adb_bridge.h" 7 #include "chrome/browser/devtools/devtools_target_impl.h" 8 #include "chrome/browser/ui/browser.h" 9 #include "chrome/test/base/in_process_browser_test.h" 10 #include "content/public/test/test_utils.h" 11 12 const char kDeviceModelCommand[] = "shell:getprop ro.product.model"; 13 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix"; 14 const char kListProcessesCommand[] = "shell:ps"; 15 const char kListPackagesCommand[] = "shell:pm list packages"; 16 const char kDumpsysCommand[] = "shell:dumpsys window policy"; 17 18 const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n"; 19 const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n"; 20 21 const char kSampleOpenedUnixSockets[] = 22 "Num RefCount Protocol Flags Type St Inode Path\n" 23 "00000000: 00000004 00000000" 24 " 00000000 0002 01 3328 /dev/socket/wpa_wlan0\n" 25 "00000000: 00000002 00000000" 26 " 00010000 0001 01 5394 /dev/socket/vold\n" 27 "00000000: 00000002 00000000" 28 " 00010000 0001 01 11810 @webview_devtools_remote_2425\n" 29 "00000000: 00000002 00000000" 30 " 00010000 0001 01 20893 @chrome_devtools_remote\n" 31 "00000000: 00000002 00000000" 32 " 00010000 0001 01 20894 @chrome_devtools_remote_1002\n" 33 "00000000: 00000002 00000000" 34 " 00010000 0001 01 20895 @noprocess_devtools_remote\n"; 35 36 const char kSampleListProcesses[] = 37 "USER PID PPID VSIZE RSS WCHAN PC NAME\n" 38 "root 1 0 688 508 ffffffff 00000000 S /init\r\n" 39 "u0_a75 2425 123 933736 193024 ffffffff 00000000 S com.sample.feed\r\n" 40 "nfc 741 123 706448 26316 ffffffff 00000000 S com.android.nfc\r\n" 41 "u0_a76 1001 124 111111 222222 ffffffff 00000000 S com.android.chrome\r\n" 42 "u0_a77 1002 125 111111 222222 ffffffff 00000000 S com.chrome.beta\r\n" 43 "u0_a78 1003 126 111111 222222 ffffffff 00000000 S com.noprocess.app\r\n"; 44 45 const char kSampleListPackages[] = 46 "package:com.sample.feed\r\n" 47 "package:com.android.nfc\r\n" 48 "package:com.android.chrome\r\n" 49 "package:com.chrome.beta\r\n" 50 "package:com.google.android.apps.chrome\r\n"; 51 52 const char kSampleDumpsysCommand[] = 53 "WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n" 54 " mSafeMode=false mSystemReady=true mSystemBooted=true\r\n" 55 " mStable=(0,50)-(720,1184)\r\n" // Only mStable parameter is parsed 56 " mForceStatusBar=false mForceStatusBarFromKeyguard=false\r\n"; 57 58 char kSampleChromeVersion[] = "{\n" 59 " \"Browser\": \"Chrome/32.0.1679.0\",\n" 60 " \"Protocol-Version\": \"1.0\",\n" 61 " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " 62 "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n" 63 " \"WebKit-Version\": \"537.36 (@160162)\"\n" 64 "}"; 65 66 char kSampleChromeBetaVersion[] = "{\n" 67 " \"Browser\": \"Chrome/31.0.1599.0\",\n" 68 " \"Protocol-Version\": \"1.0\",\n" 69 " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " 70 "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n" 71 " \"WebKit-Version\": \"537.36 (@160162)\"\n" 72 "}"; 73 74 char kSampleWebViewVersion[] = "{\n" 75 " \"Browser\": \"Version/4.0\",\n" 76 " \"Protocol-Version\": \"1.0\",\n" 77 " \"User-Agent\": \"Mozilla/5.0 (Linux; Android 4.3; Build/KRS74B) " 78 "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Safari/537.36\",\n" 79 " \"WebKit-Version\": \"537.36 (@157588)\"\n" 80 "}"; 81 82 char kSampleChromePages[] = "[ {\n" 83 " \"description\": \"\",\n" 84 " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?" 85 "ws=/devtools/page/755DE5C9-D49F-811D-0693-51B8E15C80D2\",\n" 86 " \"id\": \"755DE5C9-D49F-811D-0693-51B8E15C80D2\",\n" 87 " \"title\": \"The Chromium Projects\",\n" 88 " \"type\": \"page\",\n" 89 " \"url\": \"http://www.chromium.org/\",\n" 90 " \"webSocketDebuggerUrl\": \"" 91 "ws:///devtools/page/755DE5C9-D49F-811D-0693-51B8E15C80D2\"\n" 92 "} ]"; 93 94 char kSampleChromeBetaPages[] = "[]"; 95 96 char kSampleWebViewPages[] = "[ {\n" 97 " \"description\": \"{\\\"attached\\\":false,\\\"empty\\\":false," 98 "\\\"height\\\":1173,\\\"screenX\\\":0,\\\"screenY\\\":0," 99 "\\\"visible\\\":true,\\\"width\\\":800}\",\n" 100 " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/" 101 "serve_rev/@157588/devtools.html?ws=" 102 "/devtools/page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n" 103 " \"faviconUrl\": \"http://chromium.org/favicon.ico\",\n" 104 " \"id\": \"3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n" 105 " \"thumbnailUrl\": \"/thumb/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n" 106 " \"title\": \"Blink - The Chromium Projects\",\n" 107 " \"type\": \"page\",\n" 108 " \"url\": \"http://www.chromium.org/blink\",\n" 109 " \"webSocketDebuggerUrl\": \"ws:///devtools/" 110 "page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\"\n" 111 "}, {\n" 112 " \"description\": \"{\\\"attached\\\":true,\\\"empty\\\":true," 113 "\\\"screenX\\\":0,\\\"screenY\\\":33,\\\"visible\\\":false}\",\n" 114 " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/" 115 "serve_rev/@157588/devtools.html?ws=" 116 "/devtools/page/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n" 117 " \"faviconUrl\": \"\",\n" 118 " \"id\": \"44681551-ADFD-2411-076B-3AB14C1C60E2\",\n" 119 " \"thumbnailUrl\": \"/thumb/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n" 120 " \"title\": \"More Activity\",\n" 121 " \"type\": \"page\",\n" 122 " \"url\": \"about:blank\",\n" 123 " \"webSocketDebuggerUrl\": \"ws:///devtools/page/" 124 "44681551-ADFD-2411-076B-3AB14C1C60E2\"\n" 125 "}]"; 126 127 class MockDeviceImpl : public AndroidDevice { 128 public: 129 MockDeviceImpl(const std::string& serial, int index, 130 bool connected, const char* device_model) 131 : AndroidDevice(serial, connected), 132 device_model_(device_model) 133 {} 134 135 virtual void RunCommand(const std::string& command, 136 const CommandCallback& callback) OVERRIDE { 137 const char* response; 138 139 if (command == kDeviceModelCommand) { 140 response = device_model_; 141 } else if (command == kOpenedUnixSocketsCommand) { 142 response = kSampleOpenedUnixSockets; 143 } else if (command == kListProcessesCommand) { 144 response = kSampleListProcesses; 145 } else if (command == kListPackagesCommand) { 146 response = kSampleListPackages; 147 } else if (command == kDumpsysCommand) { 148 response = kSampleDumpsysCommand; 149 } else { 150 NOTREACHED(); 151 return; 152 } 153 154 base::MessageLoop::current()->PostTask( FROM_HERE, 155 base::Bind(&MockDeviceImpl::RunCommandCallback, 156 this, callback, 0, response)); 157 } 158 159 void RunCommandCallback(const CommandCallback& callback, int result, 160 const std::string& response) { 161 callback.Run(result, response); 162 } 163 164 virtual void OpenSocket(const std::string& name, 165 const SocketCallback& callback) OVERRIDE { 166 NOTREACHED(); 167 } 168 169 virtual void HttpQuery(const std::string& la_name, 170 const std::string& request, 171 const CommandCallback& callback) OVERRIDE { 172 const char* response; 173 174 if (la_name == "chrome_devtools_remote") { 175 if (request == kVersionRequest) { 176 response = kSampleChromeVersion; 177 } else if (request == kPageListRequest) { 178 response = kSampleChromePages; 179 } else { 180 NOTREACHED(); 181 return; 182 } 183 } else if (la_name == "chrome_devtools_remote_1002") { 184 if (request == kVersionRequest) { 185 response = kSampleChromeBetaVersion; 186 } else if (request == kPageListRequest) { 187 response = kSampleChromeBetaPages; 188 } else { 189 NOTREACHED(); 190 return; 191 } 192 } else if (la_name.find("noprocess_devtools_remote") == 0) { 193 if (request == kVersionRequest) { 194 response = "{}"; 195 } else if (request == kPageListRequest) { 196 response = "[]"; 197 } else { 198 NOTREACHED(); 199 return; 200 } 201 } else if (la_name == "webview_devtools_remote_2425") { 202 if (request == kVersionRequest) { 203 response = kSampleWebViewVersion; 204 } else if (request == kPageListRequest) { 205 response = kSampleWebViewPages; 206 } else { 207 NOTREACHED(); 208 return; 209 } 210 } else { 211 NOTREACHED(); 212 return; 213 } 214 215 base::MessageLoop::current()->PostTask( FROM_HERE, 216 base::Bind(&MockDeviceImpl::RunCommandCallback, 217 this, callback, 0, response)); 218 } 219 220 virtual void HttpUpgrade(const std::string& la_name, 221 const std::string& request, 222 const SocketCallback& callback) { 223 NOTREACHED(); 224 } 225 226 virtual void HttpQueryCallback(const CommandCallback& next, int code, 227 const std::string& result) { 228 NOTREACHED(); 229 } 230 231 private: 232 virtual ~MockDeviceImpl() 233 {} 234 235 const char* device_model_; 236 }; 237 238 class MockDeviceProvider : public AndroidDeviceProvider { 239 virtual ~MockDeviceProvider() 240 {} 241 242 virtual void QueryDevices(const QueryDevicesCallback& callback) OVERRIDE { 243 AndroidDeviceProvider::AndroidDevices devices; 244 devices.push_back(new MockDeviceImpl("FirstDevice", 0, true, "Nexus 6")); 245 devices.push_back(new MockDeviceImpl("SecondDevice", 1, false, "Nexus 8")); 246 callback.Run(devices); 247 } 248 }; 249 250 static scoped_refptr<DevToolsAdbBridge::RemoteBrowser> 251 FindBrowserByDisplayName(DevToolsAdbBridge::RemoteBrowsers browsers, 252 const std::string& name) { 253 for (DevToolsAdbBridge::RemoteBrowsers::iterator it = browsers.begin(); 254 it != browsers.end(); ++it) 255 if ((*it)->display_name() == name) 256 return *it; 257 return NULL; 258 } 259 260 class DevToolsAdbBridgeTest : public InProcessBrowserTest, 261 public DevToolsAdbBridge::Listener { 262 typedef DevToolsAdbBridge::RemoteDevices::const_iterator rdci; 263 typedef DevToolsAdbBridge::RemoteBrowsers::const_iterator rbci; 264 public: 265 virtual void RemoteDevicesChanged( 266 DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE{ 267 devices_ = *devices; 268 runner_->Quit(); 269 } 270 271 void CheckDevices() { 272 ASSERT_EQ(2U, devices_.size()); 273 274 scoped_refptr<DevToolsAdbBridge::RemoteDevice> connected = 275 devices_[0]->IsConnected() ? devices_[0] : devices_[1]; 276 277 scoped_refptr<DevToolsAdbBridge::RemoteDevice> not_connected = 278 devices_[0]->IsConnected() ? devices_[1] : devices_[0]; 279 280 ASSERT_TRUE(connected->IsConnected()); 281 ASSERT_FALSE(not_connected->IsConnected()); 282 283 ASSERT_EQ(720, connected->screen_size().width()); 284 ASSERT_EQ(1184, connected->screen_size().height()); 285 286 ASSERT_EQ("FirstDevice", connected->GetSerial()); 287 ASSERT_EQ("Nexus 6", connected->GetModel()); 288 289 ASSERT_EQ("SecondDevice", not_connected->GetSerial()); 290 ASSERT_EQ("Offline", not_connected->GetModel()); 291 292 const DevToolsAdbBridge::RemoteBrowsers& browsers = connected->browsers(); 293 ASSERT_EQ(4U, browsers.size()); 294 295 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> chrome = 296 FindBrowserByDisplayName(browsers, "Chrome"); 297 ASSERT_TRUE(chrome); 298 299 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> chrome_beta = 300 FindBrowserByDisplayName(browsers, "Chrome Beta"); 301 ASSERT_TRUE(chrome_beta); 302 303 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> chromium = 304 FindBrowserByDisplayName(browsers, "Chromium"); 305 ASSERT_FALSE(chromium); 306 307 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> webview = 308 FindBrowserByDisplayName(browsers, "WebView in com.sample.feed"); 309 ASSERT_TRUE(webview); 310 311 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> noprocess = 312 FindBrowserByDisplayName(browsers, "Noprocess"); 313 ASSERT_TRUE(noprocess); 314 315 ASSERT_EQ("32.0.1679.0", chrome->version()); 316 ASSERT_EQ("31.0.1599.0", chrome_beta->version()); 317 ASSERT_EQ("4.0", webview->version()); 318 319 std::vector<DevToolsTargetImpl*> chrome_pages = 320 chrome->CreatePageTargets(); 321 std::vector<DevToolsTargetImpl*> chrome_beta_pages = 322 chrome_beta->CreatePageTargets(); 323 std::vector<DevToolsTargetImpl*> webview_pages = 324 webview->CreatePageTargets(); 325 326 ASSERT_EQ(1U, chrome_pages.size()); 327 ASSERT_EQ(0U, chrome_beta_pages.size()); 328 ASSERT_EQ(2U, webview_pages.size()); 329 330 // Check that we have non-empty description for webview pages. 331 ASSERT_EQ(0U, chrome_pages[0]->GetDescription().size()); 332 ASSERT_NE(0U, webview_pages[0]->GetDescription().size()); 333 ASSERT_NE(0U, webview_pages[1]->GetDescription().size()); 334 335 ASSERT_EQ(GURL("http://www.chromium.org/"), chrome_pages[0]->GetUrl()); 336 ASSERT_EQ("The Chromium Projects", chrome_pages[0]->GetTitle()); 337 338 STLDeleteElements(&chrome_pages); 339 STLDeleteElements(&webview_pages); 340 } 341 342 void init() { 343 runner_ = new content::MessageLoopRunner; 344 } 345 346 protected: 347 scoped_refptr<content::MessageLoopRunner> runner_; 348 DevToolsAdbBridge::RemoteDevices devices_; 349 }; 350 351 IN_PROC_BROWSER_TEST_F(DevToolsAdbBridgeTest, DiscoverAndroidBrowsers) { 352 init(); 353 354 scoped_refptr<DevToolsAdbBridge> adb_bridge = 355 DevToolsAdbBridge::Factory::GetForProfile(browser()->profile()); 356 357 DevToolsAdbBridge::DeviceProviders providers; 358 providers.push_back(new MockDeviceProvider()); 359 360 adb_bridge->set_device_providers(providers); 361 362 if (!adb_bridge) { 363 FAIL() << "Failed to get DevToolsAdbBridge."; 364 } 365 366 adb_bridge->AddListener(this); 367 368 runner_->Run(); 369 370 CheckDevices(); 371 } 372