/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// This file contains implementation of a test application that tests
// functionality of AdbWinApi interface. In this test we will use AdbWinApi
// interface in order to enumerate USB interfaces for Android ADB class, and
// for each interface found we will test USB I/O on that interface by sending
// a simple "hand shake" message to the device connected via this interface.

#include "stdafx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// Android ADB interface identifier
const GUID kAdbInterfaceId = ANDROID_USB_CLASS_ID;

// Number of interfaces detected in TestEnumInterfaces.
int interface_count = 0;

// Constants used to initialize a "handshake" message
#define MAX_PAYLOAD 4096
#define A_SYNC 0x434e5953
#define A_CNXN 0x4e584e43
#define A_OPEN 0x4e45504f
#define A_OKAY 0x59414b4f
#define A_CLSE 0x45534c43
#define A_WRTE 0x45545257
#define A_AUTH 0x48545541
#define A_VERSION 0x01000000

// AUTH packets first argument
#define ADB_AUTH_TOKEN         1
#define ADB_AUTH_SIGNATURE     2
#define ADB_AUTH_RSAPUBLICKEY  3

// Interface descriptor constants for ADB interface
#define ADB_CLASS              0xff
#define ADB_SUBCLASS           0x42
#define ADB_PROTOCOL           0x1

// Formats message sent to USB device
struct message {
    unsigned int command;       /* command identifier constant      */
    unsigned int arg0;          /* first argument                   */
    unsigned int arg1;          /* second argument                  */
    unsigned int data_length;   /* length of payload (0 is allowed) */
    unsigned int data_crc32;    /* crc32 of data payload            */
    unsigned int magic;         /* command ^ 0xffffffff             */
};

//
// Test routines declarations.
//

// Tests interface enumeration.
bool TestEnumInterfaces();

// Tests all interfaces detected for our device class.
bool TestInterfaces();

// Tests interface addressed by the given device name.
bool TestInterface(const wchar_t* device_name);

// Tests interface opened with ADB API.
bool TestInterfaceHandle(ADBAPIHANDLE interface_handle);

// Sends a "handshake" message to the given interface.
bool DeviceHandShake(ADBAPIHANDLE adb_interface);

// Test AdbCloseHandle race condition.
bool TestCloseRaceCondition();

int __cdecl _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {
  // Test enum interfaces.
  if (!TestEnumInterfaces())
    return -1;

  if (0 == interface_count) {
    printf("\nNo ADB interfaces found. Make sure that device is "
           "connected to USB port and is powered on.");
    return 1;
  }

  // Test each interface found in the system
  if (!TestInterfaces())
    return -2;

  // Test for AdbCloseHandle race condition
  if (!TestCloseRaceCondition())
    return -3;

  return 0;
}

bool TestEnumInterfaces() {
  // Enumerate interfaces
  ADBAPIHANDLE enum_handle =
    AdbEnumInterfaces(kAdbInterfaceId, true, true, true);
  if (NULL == enum_handle) {
    printf("\nEnum interfaces failure:");
    printf("\nUnable to enumerate ADB interfaces: %u", GetLastError());
    return false;
  }

  // Unite interface info structure and buffer big enough to contain the
  // largest structure.
  union {
    AdbInterfaceInfo interface_info;
    char buf[4096];
  };
  unsigned long buf_size = sizeof(buf);

  // Enumerate (and count) interfaces, printing information for each found
  // interface.
  interface_count = 0;
  while (AdbNextInterface(enum_handle, &interface_info, &buf_size)) {
    interface_count++;
    printf("\nFound interface %ws:", interface_info.device_name);
    if (interface_info.flags & SPINT_ACTIVE)
      printf(" ACTIVE");
    if (interface_info.flags & SPINT_DEFAULT)
      printf(" DEFAULT");
    if (interface_info.flags & SPINT_REMOVED)
      printf(" REMOVED");

    buf_size = sizeof(buf);
  }

  bool ret = true;
  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
    printf("\n--- AdbNextInterface failure %u", GetLastError());
    ret = false;
  }

  if (!AdbCloseHandle(enum_handle)) {
    printf("\n--- AdbCloseHandle failure %u", GetLastError());
    ret = false;
  }

  return ret;
}

bool TestInterfaces() {
  bool ret = true;

  // Enumerate interfaces
  ADBAPIHANDLE enum_handle =
    AdbEnumInterfaces(kAdbInterfaceId, true, true, true);
  if (NULL == enum_handle) {
    printf("\nTest interfaces failure:");
    printf("\nUnable to enumerate ADB interfaces: %u", GetLastError());
    ret = false;
  } else {
    // Unite interface info structure and buffer big enough to contain the
    // largest structure.
    union {
      AdbInterfaceInfo interface_info;
      char buf[4096];
    };
    unsigned long buf_size = sizeof(buf);

    // Test each found interface
    while (AdbNextInterface(enum_handle, &interface_info, &buf_size)) {
      TestInterface(interface_info.device_name);
      buf_size = sizeof(buf);
    }

    if (GetLastError() != ERROR_NO_MORE_ITEMS) {
      printf("\n--- AdbNextInterface failure %u", GetLastError());
      ret = false;
    }

    if (!AdbCloseHandle(enum_handle)) {
      printf("\n--- AdbCloseHandle failure %u", GetLastError());
      ret = false;
    }
  }

  return ret;
}

bool TestInterface(const wchar_t* device_name) {
  printf("\n*** Test interface( %ws )", device_name);

  // Get ADB handle to the interface by its name
  ADBAPIHANDLE interface_handle = AdbCreateInterfaceByName(device_name);
  if (NULL == interface_handle) {
    printf(" FAILED:\nUnable to create interface by name: %u", GetLastError());
    return false;
  }

  // Test it
  TestInterfaceHandle(interface_handle);
  if (!AdbCloseHandle(interface_handle)) {
    printf("\n--- AdbCloseHandle failure %u", GetLastError());
    return false;
  }

  return true;
}

bool TestInterfaceName(ADBAPIHANDLE interface_handle) {
  bool ret = true;
  unsigned long intr_name_size = 0;
  char* buf = NULL;

  if (AdbGetInterfaceName(interface_handle, NULL, &intr_name_size, true)) {
    printf("\n--- AdbGetInterfaceName unexpectedly succeeded %u",
           GetLastError());
    ret = false;
    goto exit;
  }
  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
    printf("\n--- AdbGetInterfaceName failure %u", GetLastError());
    ret = false;
    goto exit;
  }
  if (intr_name_size == 0) {
    printf("\n--- AdbGetInterfaceName returned name size of zero");
    ret = false;
    goto exit;
  }

  const size_t buf_size = intr_name_size + 16; // extra in case of overwrite
  buf = reinterpret_cast<char*>(malloc(buf_size));
  if (buf == NULL) {
    printf("\n--- could not malloc %d bytes, errno %u", buf_size, errno);
    ret = false;
    goto exit;
  }
  const char buf_fill = (unsigned char)0xFF;
  memset(buf, buf_fill, buf_size);

  if (!AdbGetInterfaceName(interface_handle, buf, &intr_name_size, true)) {
    printf("\n--- AdbGetInterfaceName failure %u", GetLastError());
    ret = false;
    goto exit;
  }
  if (buf[intr_name_size - 1] != '\0') {
    printf("\n--- AdbGetInterfaceName returned non-NULL terminated string");
    ret = false;
    goto exit;
  }
  for (size_t i = intr_name_size; i < buf_size; ++i) {
    if (buf[i] != buf_fill) {
      printf("\n--- AdbGetInterfaceName overwrote past the end of the buffer at"
             " index %u with 0x%02X", i, (unsigned char)buf[i]);
      ret = false;
      goto exit;
    }
  }

  printf("\n+++ Interface name %s", buf);

exit:
  free(buf);

  return ret;
}

void DumpEndpointInformation(const AdbEndpointInformation* pipe_info) {
  printf("\n          max_packet_size   = %u", pipe_info->max_packet_size);
  printf("\n          max_transfer_size = %u", pipe_info->max_transfer_size);
  printf("\n          endpoint_type     = %u", pipe_info->endpoint_type);
  const char* endpoint_type_desc = NULL;
  switch (pipe_info->endpoint_type) {
#define CASE_TYPE(type) case type: endpoint_type_desc = #type; break
    CASE_TYPE(AdbEndpointTypeInvalid);
    CASE_TYPE(AdbEndpointTypeControl);
    CASE_TYPE(AdbEndpointTypeIsochronous);
    CASE_TYPE(AdbEndpointTypeBulk);
    CASE_TYPE(AdbEndpointTypeInterrupt);
#undef CASE_TYPE
  }
  if (endpoint_type_desc != NULL) {
    printf(" (%s)", endpoint_type_desc);
  }
  printf("\n          endpoint_address  = %02X", pipe_info->endpoint_address);
  printf("\n          polling_interval  = %u", pipe_info->polling_interval);
  printf("\n          setting_index     = %u", pipe_info->setting_index);
}

bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) {
  // Get interface name.
  if (!TestInterfaceName(interface_handle)) {
    return false;
  }

  // Get device descriptor for the interface
  USB_DEVICE_DESCRIPTOR dev_desc;
  if (AdbGetUsbDeviceDescriptor(interface_handle, &dev_desc)) {
    printf("\n+++ Device descriptor:");
    printf("\n        bLength            = %u", dev_desc.bLength);
    printf("\n        bDescriptorType    = %u", dev_desc.bDescriptorType);
    printf("\n        bcdUSB             = %u", dev_desc.bcdUSB);
    printf("\n        bDeviceClass       = %u", dev_desc.bDeviceClass);
    printf("\n        bDeviceSubClass    = %u", dev_desc.bDeviceSubClass);
    printf("\n        bDeviceProtocol    = %u", dev_desc.bDeviceProtocol);
    printf("\n        bMaxPacketSize0    = %u", dev_desc.bMaxPacketSize0);
    printf("\n        idVendor           = %X", dev_desc.idVendor);
    printf("\n        idProduct          = %X", dev_desc.idProduct);
    printf("\n        bcdDevice          = %u", dev_desc.bcdDevice);
    printf("\n        iManufacturer      = %u", dev_desc.iManufacturer);
    printf("\n        iProduct           = %u", dev_desc.iProduct);
    printf("\n        iSerialNumber      = %u", dev_desc.iSerialNumber);
    printf("\n        bNumConfigurations = %u", dev_desc.bNumConfigurations);
  } else {
    printf("\n--- AdbGetUsbDeviceDescriptor failure %u", GetLastError());
    return false;
  }

  // Get configuration descriptor for the interface
  USB_CONFIGURATION_DESCRIPTOR config_desc;
  if (AdbGetUsbConfigurationDescriptor(interface_handle, &config_desc)) {
    printf("\n+++ Configuration descriptor:");
    printf("\n        bLength             = %u", config_desc.bLength);
    printf("\n        bDescriptorType     = %u", config_desc.bDescriptorType);
    printf("\n        wTotalLength        = %u", config_desc.wTotalLength);
    printf("\n        bNumInterfaces      = %u", config_desc.bNumInterfaces);
    printf("\n        bConfigurationValue = %u", config_desc.bConfigurationValue);
    printf("\n        iConfiguration      = %u", config_desc.iConfiguration);
    printf("\n        bmAttributes        = %u", config_desc.bmAttributes);
    printf("\n        MaxPower            = %u", config_desc.MaxPower);
  } else {
    printf("\n--- AdbGetUsbConfigurationDescriptor failure %u", GetLastError());
    return false;
  }

  // Get device serial number
  char ser_num[1024];
  unsigned long ser_num_size = sizeof(ser_num);
  if (AdbGetSerialNumber(interface_handle, ser_num, &ser_num_size, true)) {
    printf("\n+++ Serial number: %s", ser_num);
  } else {
    printf("\n--- AdbGetSerialNumber failure %u", GetLastError());
    return false;
  }

  // Get interface descriptor
  USB_INTERFACE_DESCRIPTOR intr_desc;
  if (AdbGetUsbInterfaceDescriptor(interface_handle, &intr_desc)) {
    printf("\n+++ Interface descriptor:");
    printf("\n        bDescriptorType    = %u", intr_desc.bDescriptorType);
    printf("\n        bInterfaceNumber   = %u", intr_desc.bInterfaceNumber);
    printf("\n        bAlternateSetting  = %u", intr_desc.bAlternateSetting);
    printf("\n        bNumEndpoints      = %u", intr_desc.bNumEndpoints);
    printf("\n        bInterfaceClass    = %u", intr_desc.bInterfaceClass);
    if (intr_desc.bInterfaceClass == ADB_CLASS) {
      printf(" (ADB_CLASS)");
    }
    printf("\n        bInterfaceSubClass = %u", intr_desc.bInterfaceSubClass);
    if (intr_desc.bInterfaceSubClass == ADB_SUBCLASS) {
      printf(" (ADB_SUBCLASS)");
    }
    printf("\n        bInterfaceProtocol = %u", intr_desc.bInterfaceProtocol);
    if (intr_desc.bInterfaceProtocol == ADB_PROTOCOL) {
      printf(" (ADB_PROTOCOL)");
    }
    printf("\n        iInterface         = %u", intr_desc.iInterface);
  } else {
    printf("\n--- AdbGetUsbInterfaceDescriptor failure %u", GetLastError());
    return false;
  }

  // Enumerate interface's endpoints
  AdbEndpointInformation pipe_info;
  for (UCHAR pipe = 0; pipe < intr_desc.bNumEndpoints; pipe++) {
    if (AdbGetEndpointInformation(interface_handle, pipe, &pipe_info)) {
      printf("\n      PIPE %u info:", pipe);
      DumpEndpointInformation(&pipe_info);
    } else {
      printf("\n--- AdbGetEndpointInformation(%u) failure %u", pipe,
             GetLastError());
      return false;
    }
  }

  // Get default bulk read endpoint info
  if (AdbGetDefaultBulkReadEndpointInformation(interface_handle, &pipe_info)) {
    printf("\n      Default Bulk Read Pipe info:");
    DumpEndpointInformation(&pipe_info);
  } else {
    printf("\n--- AdbGetDefaultBulkReadEndpointInformation failure %u",
           GetLastError());
    return false;
  }

  // Get default bulk write endpoint info
  if (AdbGetDefaultBulkWriteEndpointInformation(interface_handle, &pipe_info)) {
    printf("\n      Default Bulk Write Pipe info:");
    DumpEndpointInformation(&pipe_info);
  } else {
    printf("\n--- AdbGetDefaultBulkWriteEndpointInformation failure %u",
           GetLastError());
    return false;
  }

  // Test a handshake on that interface
  DeviceHandShake(interface_handle);

  return true;
}

void HexDump(const void* data, const size_t read_bytes) {
  const unsigned char* buf = reinterpret_cast<const unsigned char*>(data);
  const size_t line_length = 16;
  for (size_t n = 0; n < read_bytes; n += line_length) {
    const unsigned char* line = &buf[n];
    const size_t max_line = min(line_length, read_bytes - n);

    printf("\n          ");
    for (size_t i = 0; i < line_length; ++i) {
      if (i >= max_line) {
        printf("   ");
      } else {
        printf("%02X ", line[i]);
      }
    }
    printf(" ");
    for (size_t i = 0; i < max_line; ++i) {
      if (isprint(line[i])) {
        printf("%c", line[i]);
      } else {
        printf(".");
      }
    }
  }
}

void DumpMessageArg0(unsigned int command, unsigned int arg0) {
  if (command == A_AUTH) {
    const char* desc = NULL;
    switch (arg0) {
#define CASE_ARG0(arg) case arg: desc = # arg; break
      CASE_ARG0(ADB_AUTH_TOKEN);
      CASE_ARG0(ADB_AUTH_SIGNATURE);
      CASE_ARG0(ADB_AUTH_RSAPUBLICKEY);
#undef CASE_ARG0
    }
    if (desc != NULL) {
      printf(" (%s)", desc);
    }
  }
}

bool DeviceHandShake(ADBAPIHANDLE adb_interface) {
  // Get interface name
  char interf_name[512];
  unsigned long name_size = sizeof(interf_name);
  if (!AdbGetInterfaceName(adb_interface, interf_name, &name_size, true)) {
    printf("\nDeviceHandShake: AdbGetInterfaceName returned error %u",
           GetLastError());
    return false;
  }

  printf("\n\nDeviceHandShake on %s", interf_name);

  char* ser_num = NULL;
  name_size = 0;
  if (!AdbGetSerialNumber(adb_interface, ser_num, &name_size, true)) {
    ser_num = reinterpret_cast<char*>(malloc(name_size));
    if (NULL != ser_num) {
      if (!AdbGetSerialNumber(adb_interface, ser_num, &name_size, true)) {
        printf("\n      AdbGetSerialNumber returned error %u", GetLastError());
        AdbCloseHandle(adb_interface);
        return false;
      }
      printf("\nInterface serial number is %s", ser_num);
      free(ser_num);
    }
  }

  // Get default read endpoint
  ADBAPIHANDLE adb_read = AdbOpenDefaultBulkReadEndpoint(adb_interface,
                                                         AdbOpenAccessTypeReadWrite,
                                                         AdbOpenSharingModeReadWrite);
  if (NULL == adb_read) {
    printf("\n      AdbOpenDefaultBulkReadEndpoint returned error %u", GetLastError());
    return false;
  }

  // Get default write endpoint
  ADBAPIHANDLE adb_write = AdbOpenDefaultBulkWriteEndpoint(adb_interface,
                                                           AdbOpenAccessTypeReadWrite,
                                                           AdbOpenSharingModeReadWrite);
  if (NULL == adb_write) {
    printf("\n      AdbOpenDefaultBulkWriteEndpoint returned error %u", GetLastError());
    AdbCloseHandle(adb_read);
    return false;
  }

  // Send connect message
  message msg_send;
  msg_send.command = A_CNXN;
  msg_send.arg0 = A_VERSION;
  msg_send.arg1 = MAX_PAYLOAD;
  msg_send.data_length = 0;
  msg_send.data_crc32 = 0;
  msg_send.magic = msg_send.command ^ 0xffffffff;

  ULONG written_bytes = 0;
  bool write_res = AdbWriteEndpointSync(adb_write, &msg_send, sizeof(msg_send), &written_bytes, 500);
  if (!write_res) {
    printf("\n       AdbWriteEndpointSync returned error %u", GetLastError());
    AdbCloseHandle(adb_write);
    AdbCloseHandle(adb_read);
    return false;
  }

  // Receive handshake
  message msg_rcv;
  ULONG read_bytes = 0;
  bool read_res = AdbReadEndpointSync(adb_read, &msg_rcv, sizeof(msg_rcv), &read_bytes, 512);
  if (!read_res) {
    printf("\n       AdbReadEndpointSync returned error %u", GetLastError());
    AdbCloseHandle(adb_write);
    AdbCloseHandle(adb_read);
    return false;
  }

  printf("\n      Read handshake: %u bytes received", read_bytes);
  char* cmd_ansi = reinterpret_cast<char*>(&msg_rcv.command);
  printf("\n         command     = %08X (%c%c%c%c)", msg_rcv.command,
         cmd_ansi[0], cmd_ansi[1], cmd_ansi[2], cmd_ansi[3]);
  printf("\n         arg0        = %08X", msg_rcv.arg0);
  DumpMessageArg0(msg_rcv.command, msg_rcv.arg0);
  printf("\n         arg1        = %08X", msg_rcv.arg1);
  printf("\n         data_length = %u", msg_rcv.data_length);
  printf("\n         data_crc32  = %08X", msg_rcv.data_crc32);
  printf("\n         magic       = %08X", msg_rcv.magic);
  printf(" (%s)", (msg_rcv.magic == (msg_rcv.command ^ 0xffffffff)) ?
           "valid" : "invalid");

  if (0 != msg_rcv.data_length) {
    char* buf = reinterpret_cast<char*>(malloc(msg_rcv.data_length));
    read_res = AdbReadEndpointSync(adb_read, buf, msg_rcv.data_length, &read_bytes, 512);
    if (!read_res) {
      printf("\n       AdbReadEndpointSync (data) returned error %u", GetLastError());
      free(buf);
      AdbCloseHandle(adb_write);
      AdbCloseHandle(adb_read);
      return false;
    }

    HexDump(buf, read_bytes);

    free(buf);
  }

  if (!AdbCloseHandle(adb_write)) {
    printf("\n--- AdbCloseHandle failure %u", GetLastError());
  }
  if (!AdbCloseHandle(adb_read)) {
    printf("\n--- AdbCloseHandle failure %u", GetLastError());
  }

  return true;
}

// Randomly delay the current thread.
class RandomDelayer {
public:
  // Prepare for a call to Delay() by getting random data. This call might grab
  // locks, causing serialization, so this should be called before
  // time-sensitive code.
  void SeedRandom() {
    r_ = rand();
  }

  // Randomly delay the current thread based on a previous call to SeedRandom().
  void Delay() {
    switch (r_ % 5) {
      case 0:
        Sleep(0); // Give up time slice to another read-to-run thread.
        break;
      case 1:
        // Try to sleep for 1 ms, but probably more based on OS scheduler
        // minimum granularity.
        Sleep(1);
        break;
      case 2:
        // Yield to another thread ready-to-run on the current processor.
        SwitchToThread();
        break;
      case 3:
        // Busy-wait for a random amount of time.
        for (int i = 0; i < r_; ++i) {
          GetLastError();
        }
        break;
      case 4:
        break; // Do nothing, no delay.
    }
  }

private:
  int r_;
};

volatile ADBAPIHANDLE g_read_handle;
volatile ADBAPIHANDLE g_interface_handle;
volatile bool g_stop_close_race_thread;

unsigned __stdcall CloseRaceThread(void*) {
  RandomDelayer r;

  while (!g_stop_close_race_thread) {
    r.SeedRandom();

    // Do volatile reads of both globals
    ADBAPIHANDLE read_handle = g_read_handle;
    ADBAPIHANDLE interface_handle = g_interface_handle;

    // If we got both handles, close them and clear the globals
    if (read_handle != NULL && interface_handle != NULL) {
      // Delay random amount before calling the API that conflicts with
      // Adb{Read,Write}EndpointSync().
      r.Delay();

      if (!AdbCloseHandle(read_handle)) {
        printf("\nAdbCloseHandle(read) failure: %u", GetLastError());
      }
      if (!AdbCloseHandle(interface_handle)) {
        printf("\nAdbCloseHandle(interface) failure: %u", GetLastError());
      }

      // Clear globals so that read thread is free to set them.
      g_read_handle = NULL;
      g_interface_handle = NULL;
    }
  }
  return 0;
}

#define EXPECTED_ERROR_LIST(FOR_EACH) \
  FOR_EACH(ERROR_INVALID_HANDLE) \
  FOR_EACH(ERROR_HANDLES_CLOSED) \
  FOR_EACH(ERROR_OPERATION_ABORTED)

#define MAKE_ARRAY_ITEM(x) x,
const DWORD g_expected_errors[] = {
  EXPECTED_ERROR_LIST(MAKE_ARRAY_ITEM)
};
#undef MAKE_ARRAY_ITEM

#define MAKE_STRING_ITEM(x) #x,
const char* g_expected_error_strings[] = {
  EXPECTED_ERROR_LIST(MAKE_STRING_ITEM)
};
#undef MAKE_STRING_ITEM

std::string get_error_description(const DWORD err) {
  const DWORD* end = g_expected_errors + ARRAYSIZE(g_expected_errors);
  const DWORD* found = std::find(g_expected_errors, end, err);
  if (found != end) {
    return g_expected_error_strings[found - g_expected_errors];
  } else {
    char buf[64];
    _snprintf(buf, sizeof(buf), "%u", err);
    return std::string(buf);
  }
}

bool is_expected_error(const DWORD err) {
  const DWORD* end = g_expected_errors + ARRAYSIZE(g_expected_errors);
  return std::find(g_expected_errors, end, err) != end;
}

// Test to reproduce https://code.google.com/p/android/issues/detail?id=161890
bool TestCloseRaceCondition() {
  const DWORD test_duration_sec = 10;
  printf("\nTesting close race condition for %u seconds... ",
         test_duration_sec);

  ADBAPIHANDLE enum_handle =
    AdbEnumInterfaces(kAdbInterfaceId, true, true, true);
  if (NULL == enum_handle) {
    printf("\nUnable to enumerate ADB interfaces: %u", GetLastError());
    return false;
  }

  union {
    AdbInterfaceInfo interface_info;
    char buf[4096];
  };
  unsigned long buf_size = sizeof(buf);

  // Get the first interface
  if (!AdbNextInterface(enum_handle, &interface_info, &buf_size)) {
    printf("\n--- AdbNextInterface failure %u", GetLastError());
    return false;
  }

  if (!AdbCloseHandle(enum_handle)) {
    printf("\nAdbCloseHandle(enum_handle) failure: %u", GetLastError());
  }

  HANDLE thread_handle = reinterpret_cast<HANDLE>(
    _beginthreadex(NULL, 0, CloseRaceThread, NULL, 0, NULL));
  if (thread_handle == NULL) {
    printf("\n--- _beginthreadex failure %u", errno);
    return false;
  }

  // Run the test for 10 seconds. It usually reproduces the crash in 1 second.
  const DWORD tick_start = GetTickCount();
  const DWORD test_duration_ticks = test_duration_sec * 1000;
  RandomDelayer r;

  std::map<DWORD, size_t> read_errors;

  while (GetTickCount() < tick_start + test_duration_ticks) {
    // Busy-wait until close thread has cleared the handles, so that we don't
    // leak handles during the test.
    while (g_read_handle != NULL) {}
    while (g_interface_handle != NULL) {}

    ADBAPIHANDLE interface_handle = AdbCreateInterfaceByName(
      interface_info.device_name);
    if (interface_handle == NULL) {
      // Not really expected to encounter an error here.
      printf("\n--- AdbCreateInterfaceByName failure %u", GetLastError());
      continue; // try again
    }
    ADBAPIHANDLE read_handle = AdbOpenDefaultBulkReadEndpoint(
      interface_handle, AdbOpenAccessTypeReadWrite,
      AdbOpenSharingModeReadWrite);
    if (read_handle == NULL) {
      // Not really expected to encounter an error here, so report, cleanup,
      // and retry.
      printf("\n--- AdbOpenDefaultBulkReadEndpoint failure %u", GetLastError());
      AdbCloseHandle(interface_handle);
      continue;
    }

    r.SeedRandom();

    // Set handles to allow other thread to close them.
    g_read_handle = read_handle;
    g_interface_handle = interface_handle;

    // Delay random amount before calling the API that conflicts with
    // AdbCloseHandle().
    r.Delay();

    message msg_rcv;
    ULONG read_bytes = 0;

    while (AdbReadEndpointSync(read_handle, &msg_rcv, sizeof(msg_rcv),
                               &read_bytes, 0 /* infinite timeout */)) {
      // Keep reading until a crash or we're broken out of the read
      // (with an error) by the CloseRaceThread.
    }
    read_errors[GetLastError()]++;
  }

  g_stop_close_race_thread = true;
  if (WaitForSingleObject(thread_handle, INFINITE) != WAIT_OBJECT_0) {
    printf("\n--- WaitForSingleObject failure %u", GetLastError());
  }
  if (!CloseHandle(thread_handle)) {
    printf("\n--- CloseHandle failure %u", GetLastError());
  }

  // The expected errors are the errors that would be encountered if the code
  // had all the major concurrent interleavings. So the test only passes if
  // we encountered all the expected errors, and thus stress tested all the
  // possible major concurrent interleavings.
  bool pass = true;
  for (size_t i = 0; i < ARRAYSIZE(g_expected_errors); ++i) {
    // If we didn't encounter the expected error code, then the test failed.
    if (read_errors.count(g_expected_errors[i]) == 0) {
      pass = false;
      break;
    }
  }

  if (pass) {
    printf("passed");
  } else {
    printf("failed.");
    printf("\nPerhaps you just need to run the test longer or again.");
  }

  printf("\nRead Error Code\t\tCount");
  printf("\n=============================");

  for (std::map<DWORD, size_t>::iterator it = read_errors.begin();
       it != read_errors.end(); ++it) {
    printf("\n%s\t%u%s", get_error_description(it->first).c_str(), it->second,
           is_expected_error(it->first) ? " (expected)" : "");
  }

  for (size_t i = 0; i < ARRAYSIZE(g_expected_errors); ++i) {
    if (read_errors.count(g_expected_errors[i]) == 0) {
      printf("\n%s\t%u (was not encountered, but was expected)",
             get_error_description(g_expected_errors[i]).c_str(), 0);
    }
  }

  return pass;
}
