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
12const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
13const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
14const char kListProcessesCommand[] = "shell:ps";
15const char kListPackagesCommand[] = "shell:pm list packages";
16const char kDumpsysCommand[] = "shell:dumpsys window policy";
17
18const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
19const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
20
21const 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
36const 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
45const 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
52const 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
58char 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
66char 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
74char 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
82char 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
94char kSampleChromeBetaPages[] = "[]";
95
96char 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
127class 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
238class 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
250static scoped_refptr<DevToolsAdbBridge::RemoteBrowser>
251FindBrowserByDisplayName(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
260class DevToolsAdbBridgeTest : public InProcessBrowserTest,
261                               public DevToolsAdbBridge::Listener {
262  typedef DevToolsAdbBridge::RemoteDevices::const_iterator rdci;
263  typedef DevToolsAdbBridge::RemoteBrowsers::const_iterator rbci;
264public:
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
346protected:
347  scoped_refptr<content::MessageLoopRunner> runner_;
348  DevToolsAdbBridge::RemoteDevices devices_;
349};
350
351IN_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