OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 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. |
| 4 |
| 5 #include <algorithm> |
| 6 |
| 7 #include "base/strings/utf_string_conversions.h" |
| 8 #include "chrome/browser/devtools/device/devtools_android_bridge.h" |
| 9 #include "chrome/browser/devtools/device/usb/android_usb_device.h" |
| 10 #include "chrome/browser/devtools/device/usb/usb_device_provider.h" |
| 11 #include "chrome/browser/ui/browser.h" |
| 12 #include "chrome/test/base/in_process_browser_test.h" |
| 13 #include "components/usb_service/usb_device.h" |
| 14 #include "components/usb_service/usb_device_handle.h" |
| 15 #include "components/usb_service/usb_interface.h" |
| 16 #include "components/usb_service/usb_service.h" |
| 17 #include "content/public/browser/browser_thread.h" |
| 18 #include "content/public/test/test_utils.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" |
| 20 |
| 21 using content::BrowserThread; |
| 22 using namespace usb_service; |
| 23 namespace { |
| 24 const int kClass = 0xff; |
| 25 const int kSubclass = 0x42; |
| 26 const int kProtocol = 0x1; |
| 27 |
| 28 const uint32 kMaxPayload = 4096; |
| 29 const uint32 kVersion = 0x01000000; |
| 30 |
| 31 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix"; |
| 32 const char kDeviceModelCommand[] = "shell:getprop ro.product.model"; |
| 33 const char kDumpsysCommand[] = "shell:dumpsys window policy"; |
| 34 const char kListProcessesCommand[] = "shell:ps"; |
| 35 const char kInstalledChromePackagesCommand[] = "shell:pm list packages"; |
| 36 const char kDeviceModel[] = "Nexus 5"; |
| 37 const char kDeviceSerial[] = "Sample serial"; |
| 38 |
| 39 const char kSampleOpenedUnixSockets[] = |
| 40 "Num RefCount Protocol Flags Type St Inode Path\n" |
| 41 "00000000: 00000004 00000000" |
| 42 " 00000000 0002 01 3328 /dev/socket/wpa_wlan0\n" |
| 43 "00000000: 00000002 00000000" |
| 44 " 00010000 0001 01 5394 /dev/socket/vold\n"; |
| 45 |
| 46 const char kSampleListProcesses[] = |
| 47 "USER PID PPID VSIZE RSS WCHAN PC NAME\n" |
| 48 "root 1 0 688 508 ffffffff 00000000 S /init\r\n" |
| 49 "u0_a75 2425 123 933736 193024 ffffffff 00000000 S com.sample.feed\r\n" |
| 50 "nfc 741 123 706448 26316 ffffffff 00000000 S com.android.nfc\r\n" |
| 51 "u0_a76 1001 124 111111 222222 ffffffff 00000000 S com.android.chrome\r\n" |
| 52 "u0_a78 1003 126 111111 222222 ffffffff 00000000 S com.noprocess.app\r\n"; |
| 53 |
| 54 const char kSampleListPackages[] = |
| 55 "package:com.sample.feed\r\n" |
| 56 "package:com.android.nfc\r\n" |
| 57 "package:com.android.chrome\r\n" |
| 58 "package:com.chrome.beta\r\n" |
| 59 "package:com.google.android.apps.chrome\r\n"; |
| 60 |
| 61 const char kSampleDumpsys[] = |
| 62 "WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n" |
| 63 " mSafeMode=false mSystemReady=true mSystemBooted=true\r\n" |
| 64 " mStable=(0,50)-(720,1184)\r\n" // Only mStable parameter is parsed |
| 65 " mForceStatusBar=false mForceStatusBarFromKeyguard=false\r\n"; |
| 66 |
| 67 const char* GetMockShellResponse(std::string command) { |
| 68 if (command == kDeviceModelCommand) { |
| 69 return kDeviceModel; |
| 70 } else if (command == kOpenedUnixSocketsCommand) { |
| 71 return kSampleOpenedUnixSockets; |
| 72 } else if (command == kDumpsysCommand) { |
| 73 return kSampleDumpsys; |
| 74 } else if (command == kListProcessesCommand) { |
| 75 return kSampleListProcesses; |
| 76 } else if (command == kInstalledChromePackagesCommand) { |
| 77 return kSampleListPackages; |
| 78 } |
| 79 |
| 80 DCHECK(false) << "Should not be reached"; |
| 81 |
| 82 return ""; |
| 83 } |
| 84 |
| 85 class MockUsbEndpointDescriptor : public UsbEndpointDescriptor { |
| 86 public: |
| 87 virtual int GetAddress() const OVERRIDE { return address_; } |
| 88 |
| 89 virtual UsbEndpointDirection GetDirection() const OVERRIDE { |
| 90 return direction_; |
| 91 } |
| 92 |
| 93 virtual int GetMaximumPacketSize() const OVERRIDE { |
| 94 return maximum_packet_size_; |
| 95 } |
| 96 |
| 97 virtual UsbSynchronizationType GetSynchronizationType() const OVERRIDE { |
| 98 return usb_synchronization_type_; |
| 99 } |
| 100 |
| 101 virtual UsbTransferType GetTransferType() const OVERRIDE { |
| 102 return usb_transfer_type_; |
| 103 } |
| 104 virtual UsbUsageType GetUsageType() const OVERRIDE { return usb_usage_type_; } |
| 105 |
| 106 virtual int GetPollingInterval() const OVERRIDE { return polling_interval_; } |
| 107 |
| 108 int address_; |
| 109 UsbEndpointDirection direction_; |
| 110 int maximum_packet_size_; |
| 111 UsbSynchronizationType usb_synchronization_type_; |
| 112 UsbTransferType usb_transfer_type_; |
| 113 UsbUsageType usb_usage_type_; |
| 114 int polling_interval_; |
| 115 |
| 116 private: |
| 117 virtual ~MockUsbEndpointDescriptor() {} |
| 118 }; |
| 119 |
| 120 class MockUsbInterfaceAltSettingDescriptor |
| 121 : public UsbInterfaceAltSettingDescriptor { |
| 122 public: |
| 123 MockUsbInterfaceAltSettingDescriptor(int interface_number, |
| 124 int alternate_setting) |
| 125 : interface_number_(interface_number), |
| 126 alternate_setting_(alternate_setting) {} |
| 127 |
| 128 virtual size_t GetNumEndpoints() const OVERRIDE { |
| 129 // See IsAndroidInterface function in android_usb_device.cc |
| 130 return 2; |
| 131 } |
| 132 |
| 133 virtual scoped_refptr<const UsbEndpointDescriptor> GetEndpoint( |
| 134 size_t index) const OVERRIDE { |
| 135 EXPECT_GT(static_cast<size_t>(2), index); |
| 136 MockUsbEndpointDescriptor* result = new MockUsbEndpointDescriptor(); |
| 137 result->address_ = index + 1; |
| 138 result->usb_transfer_type_ = USB_TRANSFER_BULK; |
| 139 result->direction_ = |
| 140 ((index == 0) ? USB_DIRECTION_INBOUND : USB_DIRECTION_OUTBOUND); |
| 141 result->maximum_packet_size_ = 1 << 20; // 1Mb maximum packet size |
| 142 return result; |
| 143 } |
| 144 |
| 145 virtual int GetInterfaceNumber() const OVERRIDE { return interface_number_; } |
| 146 |
| 147 virtual int GetAlternateSetting() const OVERRIDE { |
| 148 return alternate_setting_; |
| 149 } |
| 150 |
| 151 virtual int GetInterfaceClass() const OVERRIDE { return kClass; } |
| 152 |
| 153 virtual int GetInterfaceSubclass() const OVERRIDE { return kSubclass; } |
| 154 |
| 155 virtual int GetInterfaceProtocol() const OVERRIDE { return kProtocol; } |
| 156 |
| 157 protected: |
| 158 virtual ~MockUsbInterfaceAltSettingDescriptor() {}; |
| 159 |
| 160 private: |
| 161 const int interface_number_; |
| 162 const int alternate_setting_; |
| 163 }; |
| 164 |
| 165 class MockUsbInterfaceDescriptor : public UsbInterfaceDescriptor { |
| 166 public: |
| 167 explicit MockUsbInterfaceDescriptor(int interface_number) |
| 168 : interface_number_(interface_number) {} |
| 169 |
| 170 virtual size_t GetNumAltSettings() const OVERRIDE { |
| 171 // See IsAndroidInterface function in android_usb_device.cc |
| 172 return 1; |
| 173 } |
| 174 virtual scoped_refptr<const UsbInterfaceAltSettingDescriptor> GetAltSetting( |
| 175 size_t index) const OVERRIDE { |
| 176 EXPECT_EQ(static_cast<size_t>(0), index); |
| 177 return new MockUsbInterfaceAltSettingDescriptor(interface_number_, 0); |
| 178 } |
| 179 |
| 180 protected: |
| 181 const int interface_number_; |
| 182 virtual ~MockUsbInterfaceDescriptor() {} |
| 183 }; |
| 184 |
| 185 class MockUsbConfigDescriptor : public UsbConfigDescriptor { |
| 186 public: |
| 187 MockUsbConfigDescriptor() {} |
| 188 |
| 189 virtual size_t GetNumInterfaces() const OVERRIDE { return 1; } |
| 190 |
| 191 virtual scoped_refptr<const UsbInterfaceDescriptor> GetInterface( |
| 192 size_t index) const OVERRIDE { |
| 193 EXPECT_EQ(static_cast<size_t>(0), index); |
| 194 return new MockUsbInterfaceDescriptor(index); |
| 195 } |
| 196 |
| 197 protected: |
| 198 virtual ~MockUsbConfigDescriptor() {}; |
| 199 }; |
| 200 |
| 201 class MockUsbDevice; |
| 202 |
| 203 class MockUsbDeviceHandle : public UsbDeviceHandle { |
| 204 public: |
| 205 explicit MockUsbDeviceHandle(MockUsbDevice* device) |
| 206 : device_(device), remaining_body_length_(0) {} |
| 207 |
| 208 virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE { |
| 209 return device_; |
| 210 } |
| 211 |
| 212 virtual void Close() OVERRIDE { device_ = NULL; } |
| 213 |
| 214 virtual bool ClaimInterface(const int interface_number) OVERRIDE; |
| 215 virtual bool ReleaseInterface(const int interface_number) OVERRIDE; |
| 216 virtual bool SetInterfaceAlternateSetting( |
| 217 const int interface_number, |
| 218 const int alternate_setting) OVERRIDE { |
| 219 return true; |
| 220 } |
| 221 |
| 222 virtual bool ResetDevice() OVERRIDE { return true; } |
| 223 |
| 224 virtual bool GetSerial(base::string16* serial) OVERRIDE { |
| 225 *serial = base::UTF8ToUTF16(kDeviceSerial); |
| 226 return true; |
| 227 } |
| 228 |
| 229 // Async IO. Can be called on any thread. |
| 230 virtual void ControlTransfer(const UsbEndpointDirection direction, |
| 231 const TransferRequestType request_type, |
| 232 const TransferRecipient recipient, |
| 233 const uint8 request, |
| 234 const uint16 value, |
| 235 const uint16 index, |
| 236 net::IOBuffer* buffer, |
| 237 const size_t length, |
| 238 const unsigned int timeout, |
| 239 const UsbTransferCallback& callback) OVERRIDE {} |
| 240 |
| 241 virtual void BulkTransfer(const UsbEndpointDirection direction, |
| 242 const uint8 endpoint, |
| 243 net::IOBuffer* buffer, |
| 244 const size_t length, |
| 245 const unsigned int timeout, |
| 246 const UsbTransferCallback& callback) OVERRIDE { |
| 247 if (direction == USB_DIRECTION_OUTBOUND) { |
| 248 if (remaining_body_length_ == 0) { |
| 249 std::vector<uint32> header(6); |
| 250 memcpy(&header[0], buffer->data(), length); |
| 251 current_message_ = new AdbMessage(header[0], header[1], header[2], ""); |
| 252 remaining_body_length_ = header[3]; |
| 253 uint32 magic = header[5]; |
| 254 if ((current_message_->command ^ 0xffffffff) != magic) { |
| 255 DCHECK(false) << "Header checksum error"; |
| 256 return; |
| 257 } |
| 258 } else { |
| 259 DCHECK(current_message_); |
| 260 current_message_->body += std::string(buffer->data(), length); |
| 261 remaining_body_length_ -= length; |
| 262 } |
| 263 |
| 264 if (remaining_body_length_ == 0) { |
| 265 ProcessIncoming(); |
| 266 } |
| 267 |
| 268 base::MessageLoop::current()->PostTask( |
| 269 FROM_HERE, |
| 270 base::Bind(callback, |
| 271 usb_service::USB_TRANSFER_COMPLETED, |
| 272 scoped_refptr<net::IOBuffer>(), |
| 273 0)); |
| 274 |
| 275 } else if (direction == USB_DIRECTION_INBOUND) { |
| 276 queries_.push(Query(callback, make_scoped_refptr(buffer), length)); |
| 277 ProcessQueries(); |
| 278 } |
| 279 } |
| 280 |
| 281 template <class T> |
| 282 void append(T data) { |
| 283 std::copy(reinterpret_cast<char*>(&data), |
| 284 (reinterpret_cast<char*>(&data)) + sizeof(T), |
| 285 std::back_inserter(output_buffer_)); |
| 286 } |
| 287 |
| 288 // Copied from AndroidUsbDevice::Checksum |
| 289 uint32 Checksum(const std::string& data) { |
| 290 unsigned char* x = (unsigned char*)data.data(); |
| 291 int count = data.length(); |
| 292 uint32 sum = 0; |
| 293 while (count-- > 0) |
| 294 sum += *x++; |
| 295 return sum; |
| 296 } |
| 297 |
| 298 void ProcessIncoming() { |
| 299 DCHECK(current_message_); |
| 300 switch (current_message_->command) { |
| 301 case AdbMessage::kCommandCNXN: |
| 302 WriteResponse(new AdbMessage(AdbMessage::kCommandCNXN, |
| 303 kVersion, |
| 304 kMaxPayload, |
| 305 "device::ro.product.name=SampleProduct;ro." |
| 306 "product.model=SampleModel;ro.product." |
| 307 "device=SampleDevice;")); |
| 308 break; |
| 309 case AdbMessage::kCommandOPEN: |
| 310 DCHECK(current_message_->arg1 == 0); |
| 311 DCHECK(current_message_->arg0 != 0); |
| 312 if (current_message_->body.find("shell:") != std::string::npos) { |
| 313 WriteResponse(new AdbMessage(AdbMessage::kCommandOKAY, |
| 314 ++next_local_socket_, |
| 315 current_message_->arg0, |
| 316 "")); |
| 317 WriteResponse( |
| 318 new AdbMessage(AdbMessage::kCommandWRTE, |
| 319 next_local_socket_, |
| 320 current_message_->arg0, |
| 321 GetMockShellResponse(current_message_->body.substr( |
| 322 0, current_message_->body.size() - 1)))); |
| 323 WriteResponse(new AdbMessage( |
| 324 AdbMessage::kCommandCLSE, 0, current_message_->arg0, "")); |
| 325 } |
| 326 default: |
| 327 return; |
| 328 } |
| 329 ProcessQueries(); |
| 330 } |
| 331 |
| 332 void WriteResponse(scoped_refptr<AdbMessage> response) { |
| 333 append(response->command); |
| 334 append(response->arg0); |
| 335 append(response->arg1); |
| 336 bool add_zero = response->body.length() && |
| 337 (response->command != AdbMessage::kCommandWRTE); |
| 338 append(static_cast<uint32>(response->body.length() + (add_zero ? 1 : 0))); |
| 339 append(Checksum(response->body)); |
| 340 append(response->command ^ 0xffffffff); |
| 341 std::copy(response->body.begin(), |
| 342 response->body.end(), |
| 343 std::back_inserter(output_buffer_)); |
| 344 if (add_zero) { |
| 345 output_buffer_.push_back(0); |
| 346 } |
| 347 ProcessQueries(); |
| 348 } |
| 349 |
| 350 void ProcessQueries() { |
| 351 if (!queries_.size()) |
| 352 return; |
| 353 Query query = queries_.front(); |
| 354 |
| 355 if (query.size > output_buffer_.size()) |
| 356 return; |
| 357 |
| 358 queries_.pop(); |
| 359 std::copy(output_buffer_.begin(), |
| 360 output_buffer_.begin() + query.size, |
| 361 query.buffer->data()); |
| 362 output_buffer_.erase(output_buffer_.begin(), |
| 363 output_buffer_.begin() + query.size); |
| 364 base::MessageLoop::current()->PostTask( |
| 365 FROM_HERE, |
| 366 base::Bind(query.callback, |
| 367 usb_service::USB_TRANSFER_COMPLETED, |
| 368 query.buffer, |
| 369 query.size)); |
| 370 } |
| 371 |
| 372 virtual void InterruptTransfer(const UsbEndpointDirection direction, |
| 373 const uint8 endpoint, |
| 374 net::IOBuffer* buffer, |
| 375 const size_t length, |
| 376 const unsigned int timeout, |
| 377 const UsbTransferCallback& callback) OVERRIDE { |
| 378 } |
| 379 |
| 380 virtual void IsochronousTransfer( |
| 381 const UsbEndpointDirection direction, |
| 382 const uint8 endpoint, |
| 383 net::IOBuffer* buffer, |
| 384 const size_t length, |
| 385 const unsigned int packets, |
| 386 const unsigned int packet_length, |
| 387 const unsigned int timeout, |
| 388 const UsbTransferCallback& callback) OVERRIDE {} |
| 389 |
| 390 protected: |
| 391 virtual ~MockUsbDeviceHandle() {} |
| 392 |
| 393 struct Query { |
| 394 UsbTransferCallback callback; |
| 395 scoped_refptr<net::IOBuffer> buffer; |
| 396 size_t size; |
| 397 |
| 398 Query(UsbTransferCallback callback, |
| 399 scoped_refptr<net::IOBuffer> buffer, |
| 400 int size) |
| 401 : callback(callback), buffer(buffer), size(size) {}; |
| 402 }; |
| 403 |
| 404 scoped_refptr<MockUsbDevice> device_; |
| 405 uint32 remaining_body_length_; |
| 406 scoped_refptr<AdbMessage> current_message_; |
| 407 std::vector<char> output_buffer_; |
| 408 std::queue<Query> queries_; |
| 409 int next_local_socket_ = 1; |
| 410 }; |
| 411 |
| 412 class MockUsbDevice : public UsbDevice { |
| 413 public: |
| 414 MockUsbDevice() : UsbDevice(0, 0, 0) {} |
| 415 |
| 416 virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE { |
| 417 return new MockUsbDeviceHandle(this); |
| 418 } |
| 419 |
| 420 virtual scoped_refptr<UsbConfigDescriptor> ListInterfaces() OVERRIDE { |
| 421 return new MockUsbConfigDescriptor(); |
| 422 } |
| 423 |
| 424 virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE { |
| 425 return true; |
| 426 } |
| 427 |
| 428 #if defined(OS_CHROMEOS) |
| 429 // On ChromeOS, if an interface of a claimed device is not claimed, the |
| 430 // permission broker can change the owner of the device so that the unclaimed |
| 431 // interfaces can be used. If this argument is missing, permission broker will |
| 432 // not be used and this method fails if the device is claimed. |
| 433 virtual void RequestUsbAcess( |
| 434 int interface_id, |
| 435 const base::Callback<void(bool success)>& callback) OVERRIDE { |
| 436 callback.Run(true); |
| 437 } |
| 438 #endif // OS_CHROMEOS |
| 439 |
| 440 std::set<int> claimed_interfaces_; |
| 441 |
| 442 protected: |
| 443 virtual ~MockUsbDevice() {} |
| 444 }; |
| 445 |
| 446 class MockUsbService : public UsbService { |
| 447 public: |
| 448 MockUsbService() { devices_.push_back(new MockUsbDevice()); } |
| 449 |
| 450 virtual ~MockUsbService() {} |
| 451 |
| 452 virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) OVERRIDE { |
| 453 NOTIMPLEMENTED(); |
| 454 return NULL; |
| 455 } |
| 456 |
| 457 virtual void GetDevices( |
| 458 std::vector<scoped_refptr<UsbDevice> >* devices) OVERRIDE { |
| 459 STLClearObject(devices); |
| 460 std::copy(devices_.begin(), devices_.end(), back_inserter(*devices)); |
| 461 } |
| 462 |
| 463 std::vector<scoped_refptr<UsbDevice> > devices_; |
| 464 }; |
| 465 |
| 466 bool MockUsbDeviceHandle::ClaimInterface(const int interface_number) { |
| 467 if (device_->claimed_interfaces_.find(interface_number) != |
| 468 device_->claimed_interfaces_.end()) |
| 469 return false; |
| 470 |
| 471 device_->claimed_interfaces_.insert(interface_number); |
| 472 return true; |
| 473 } |
| 474 |
| 475 bool MockUsbDeviceHandle::ReleaseInterface(const int interface_number) { |
| 476 if (device_->claimed_interfaces_.find(interface_number) == |
| 477 device_->claimed_interfaces_.end()) |
| 478 return false; |
| 479 |
| 480 device_->claimed_interfaces_.erase(interface_number); |
| 481 return true; |
| 482 } |
| 483 |
| 484 class AndroidUsbDiscoveryTest |
| 485 : public InProcessBrowserTest, |
| 486 public DevToolsAndroidBridge::DeviceListListener { |
| 487 protected: |
| 488 virtual void SetUpOnMainThread() OVERRIDE { |
| 489 scoped_refptr<content::MessageLoopRunner> runner = |
| 490 new content::MessageLoopRunner; |
| 491 |
| 492 BrowserThread::PostTaskAndReply( |
| 493 BrowserThread::FILE, |
| 494 FROM_HERE, |
| 495 base::Bind(&AndroidUsbDiscoveryTest::SetUpService, this), |
| 496 runner->QuitClosure()); |
| 497 runner->Run(); |
| 498 |
| 499 adb_bridge_ = |
| 500 DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile()); |
| 501 |
| 502 scoped_refptr<UsbDeviceProvider> provider = |
| 503 new UsbDeviceProvider(browser()->profile()); |
| 504 |
| 505 AndroidDeviceManager::DeviceProviders providers; |
| 506 providers.push_back(provider); |
| 507 |
| 508 adb_bridge_->set_device_providers_for_test(providers); |
| 509 |
| 510 runner_ = new content::MessageLoopRunner; |
| 511 } |
| 512 |
| 513 void SetUpService() { |
| 514 service_ = new MockUsbService(); |
| 515 UsbService::SetInstanceForTest(service_); |
| 516 } |
| 517 |
| 518 virtual void CleanUpOnMainThread() OVERRIDE { |
| 519 scoped_refptr<content::MessageLoopRunner> runner = |
| 520 new content::MessageLoopRunner; |
| 521 UsbService* service = NULL; |
| 522 BrowserThread::PostTaskAndReply( |
| 523 BrowserThread::FILE, |
| 524 FROM_HERE, |
| 525 base::Bind(&UsbService::SetInstanceForTest, service), |
| 526 runner->QuitClosure()); |
| 527 runner->Run(); |
| 528 } |
| 529 |
| 530 virtual void DeviceListChanged( |
| 531 const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE { |
| 532 if (devices.size() > 0) { |
| 533 if (devices[0]->is_connected()) { |
| 534 ASSERT_EQ(kDeviceModel, devices[0]->model()); |
| 535 ASSERT_EQ(kDeviceSerial, devices[0]->serial()); |
| 536 adb_bridge_->RemoveDeviceListListener(this); |
| 537 runner_->Quit(); |
| 538 } |
| 539 } |
| 540 } |
| 541 |
| 542 bool passed = false; |
| 543 scoped_refptr<content::MessageLoopRunner> runner_; |
| 544 MockUsbService* service_; |
| 545 scoped_refptr<DevToolsAndroidBridge> adb_bridge_; |
| 546 }; |
| 547 |
| 548 } // namespace |
| 549 |
| 550 IN_PROC_BROWSER_TEST_F(AndroidUsbDiscoveryTest, TestDeviceDiscovery) { |
| 551 if (!adb_bridge_) { |
| 552 FAIL() << "Failed to get DevToolsAndroidBridge."; |
| 553 } |
| 554 |
| 555 adb_bridge_->AddDeviceListListener(this); |
| 556 |
| 557 runner_->Run(); |
| 558 } |
OLD | NEW |