diff --git a/host/windows/.gitignore b/host/windows/.gitignore index 434a0faf9..0b5cf3137 100755 --- a/host/windows/.gitignore +++ b/host/windows/.gitignore @@ -6,7 +6,10 @@ usb/Debug usb/Release usb/api/obj* usb/api/*.log +usb/api/*.wrn usb/adb_winapi_test/obj* usb/adb_winapi_test/*.log +usb/adb_winapi_test/*.wrn usb/winusb/obj* -usb/winusb/*.log \ No newline at end of file +usb/winusb/*.log +usb/winusb/*.wrn diff --git a/host/windows/prebuilt/usb/AdbWinApi.def b/host/windows/prebuilt/usb/AdbWinApi.def deleted file mode 100644 index 18941481b..000000000 --- a/host/windows/prebuilt/usb/AdbWinApi.def +++ /dev/null @@ -1,15 +0,0 @@ -LIBRARY AdbWinApi.dll -EXPORTS -AdbEnumInterfaces -AdbNextInterface -AdbCreateInterfaceByName -AdbOpenDefaultBulkReadEndpoint -AdbOpenDefaultBulkWriteEndpoint -AdbCloseHandle -AdbGetInterfaceName -AdbWriteEndpointSync -AdbReadEndpointSync -AdbGetSerialNumber -AdbGetUsbInterfaceDescriptor -AdbGetUsbDeviceDescriptor -AdbGetEndpointInformation diff --git a/host/windows/prebuilt/usb/AdbWinApi.dll b/host/windows/prebuilt/usb/AdbWinApi.dll index b5586eb50..7abe26cf1 100755 Binary files a/host/windows/prebuilt/usb/AdbWinApi.dll and b/host/windows/prebuilt/usb/AdbWinApi.dll differ diff --git a/host/windows/prebuilt/usb/AdbWinApi.pdb b/host/windows/prebuilt/usb/AdbWinApi.pdb new file mode 100755 index 000000000..233518bb3 Binary files /dev/null and b/host/windows/prebuilt/usb/AdbWinApi.pdb differ diff --git a/host/windows/prebuilt/usb/AdbWinUsbApi.dll b/host/windows/prebuilt/usb/AdbWinUsbApi.dll index 0c9e00bd9..e7a6de120 100755 Binary files a/host/windows/prebuilt/usb/AdbWinUsbApi.dll and b/host/windows/prebuilt/usb/AdbWinUsbApi.dll differ diff --git a/host/windows/prebuilt/usb/AdbWinUsbApi.pdb b/host/windows/prebuilt/usb/AdbWinUsbApi.pdb new file mode 100755 index 000000000..0883ebf60 Binary files /dev/null and b/host/windows/prebuilt/usb/AdbWinUsbApi.pdb differ diff --git a/host/windows/usb/adb_winapi_test/BUILDME.TXT b/host/windows/usb/adb_winapi_test/BUILDME.TXT index 9365b5855..f53fee293 100755 --- a/host/windows/usb/adb_winapi_test/BUILDME.TXT +++ b/host/windows/usb/adb_winapi_test/BUILDME.TXT @@ -12,8 +12,31 @@ 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. -In order to build adb_winapi_test.dll you will need to install Windows Driver -Kit, which can be obtained from Microsoft. Assuming that WDK is installed, you -need to set one of the WDK's build environments, "cd" back into this directory, -and execute "build -cbeEIFZ" to clean and rebuild this project, or you can -execute "build -befEIF" to do a minimal build. +In order to build a directory with a SOURCES file you will need to install +the Windows Driver Kit, which can be obtained from Microsoft: + +Windows Driver Kit Version 7.1.0 +https://www.microsoft.com/en-us/download/details.aspx?id=11800 +md5: 8fe981a1706d43ad34bda496e6558f94 +sha1: de6abdb8eb4e08942add4aa270c763ed4e3d8242 + +This old version is used because it can build for Windows Vista (WDK 8.1 +cannot), it includes compilers (so it doesn't require Visual Studio), and it is +probably not too far from the WDK that this code was originally built with, so +it should be less risky. + +When installing the WDK, uncheck `Device Simulation Framework' because it is +unnecessary and it installs a kernel-mode driver that we don't need. + +Assuming that WDK is installed, you need to set one of the WDK's build +environments (Start Menu -> Windows Driver Kits -> x86 Free Build Environment; +choose the one for the oldest version of Windows you want to support), +"cd" back into this directory, and execute "build -cbeEIFZ" to clean and rebuild +this project, or you can execute "build -befEIF" to do a minimal build. + +Note that you need to build AdbWinApi.dll (..\api) before you build +this directory, as this depends on the AdbWinApi.lib import library. + +When you're done with the WDK build environment, don't forget to right-click the +OACR icon (in the lower-right notification area of the taskbar) and choose +`Close'. diff --git a/host/windows/usb/adb_winapi_test/SOURCES b/host/windows/usb/adb_winapi_test/SOURCES index ec82dee3f..3663ecb43 100755 --- a/host/windows/usb/adb_winapi_test/SOURCES +++ b/host/windows/usb/adb_winapi_test/SOURCES @@ -18,12 +18,17 @@ TARGETNAME = adb_winapi_test TARGETPATH = obj TARGETTYPE = PROGRAM +_NT_TARGET_VERSION = $(_NT_TARGET_VERSION_VISTA) + UMTYPE = console UMENTRY = main # Use statically linked atl libraries: USE_STATIC_ATL = 1 +# Use STL, default version +USE_STL = 1 + # Use multithreaded libraries USE_LIBCMT = 1 @@ -34,13 +39,19 @@ TARGETLIBS=$(SDK_LIB_PATH)\ole32.lib \ INCLUDES=$(DDK_INC_PATH)\;$(SDK_INC_PATH)\;$(CRT_INC_PATH)\;$(ATL_INC_PATH)\api # Common C defines -USER_C_FLAGS = $(USER_C_FLAGS) /FD /wd4100 /nologo +USER_C_FLAGS = $(USER_C_FLAGS) /FD /wd4100 /nologo + +# The STL uses C++ exception handling. +USE_NATIVE_EH=1 # Turn on all warnings, and treat warnings as errors -MSC_WARNING_LEVEL = /W4 /Wp64 /WX +MSC_WARNING_LEVEL = /W4 /WX -PRECOMPILED_CXX = 1 -PRECOMPILED_INCLUDE = stdafx.h -PRECOMPILED_SOURCEFILE = stdafx.cpp +# Disable precompiled header to work-around compiler issue with interaction with +# ASLR on Windows 7 and newer. +# http://blogs.msdn.com/b/vcblog/archive/2009/11/12/visual-c-precompiled-header-errors-on-windows-7.aspx +#PRECOMPILED_CXX = 1 +#PRECOMPILED_INCLUDE = stdafx.h +#PRECOMPILED_SOURCEFILE = stdafx.cpp SOURCES = adb_winapi_test.cpp diff --git a/host/windows/usb/adb_winapi_test/adb_winapi_test.cpp b/host/windows/usb/adb_winapi_test/adb_winapi_test.cpp index 11fcadf01..75bf76a82 100755 --- a/host/windows/usb/adb_winapi_test/adb_winapi_test.cpp +++ b/host/windows/usb/adb_winapi_test/adb_winapi_test.cpp @@ -33,24 +33,35 @@ const GUID kAdbInterfaceId = ANDROID_USB_CLASS_ID; 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_VERSION 0x01000000 +#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 */ -}; +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. @@ -69,7 +80,11 @@ bool TestInterface(const wchar_t* device_name); bool TestInterfaceHandle(ADBAPIHANDLE interface_handle); // Sends a "handshake" message to the given interface. -bool DeviceHandShake(ADBAPIHANDLE adb_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()) @@ -85,6 +100,10 @@ int __cdecl _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { if (!TestInterfaces()) return -2; + // Test for AdbCloseHandle race condition + if (!TestCloseRaceCondition()) + return -3; + return 0; } @@ -119,52 +138,60 @@ bool TestEnumInterfaces() { if (interface_info.flags & SPINT_REMOVED) printf(" REMOVED"); - buf_size = sizeof(buf);; - }; + buf_size = sizeof(buf); + } - AdbCloseHandle(enum_handle); - return true; + 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()); - return false; + 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; + } } - // 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); - }; - - AdbCloseHandle(enum_handle); - - // Create interface by VID/PID/MI - ADBAPIHANDLE interface_handle = - AdbCreateInterface(kAdbInterfaceId, DEVICE_VENDOR_ID, - DEVICE_COMPOSITE_PRODUCT_ID, DEVICE_INTERFACE_ID); - if (NULL == interface_handle) { - printf("\nUnable to create interface by VID/PID: %u", GetLastError()); - return false; - } - - // Test it - TestInterfaceHandle(interface_handle); - AdbCloseHandle(interface_handle); - return true; + return ret; } bool TestInterface(const wchar_t* device_name) { @@ -179,18 +206,98 @@ bool TestInterface(const wchar_t* device_name) { // Test it TestInterfaceHandle(interface_handle); - AdbCloseHandle(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(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. - char intr_name[4096]; - unsigned long intr_name_size = sizeof(intr_name); - if (AdbGetInterfaceName(interface_handle, intr_name, &intr_name_size, true)) { - printf("\n+++ Interface name %s", intr_name); - } else { - printf("\n--- AdbGetInterfaceName failure %u", GetLastError()); + if (!TestInterfaceName(interface_handle)) { return false; } @@ -211,7 +318,7 @@ bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { 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.bDescriptorType); + printf("\n bNumConfigurations = %u", dev_desc.bNumConfigurations); } else { printf("\n--- AdbGetUsbDeviceDescriptor failure %u", GetLastError()); return false; @@ -253,8 +360,17 @@ bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { 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()); @@ -266,14 +382,10 @@ bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { for (UCHAR pipe = 0; pipe < intr_desc.bNumEndpoints; pipe++) { if (AdbGetEndpointInformation(interface_handle, pipe, &pipe_info)) { printf("\n PIPE %u info:", pipe); - 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); - 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); + DumpEndpointInformation(&pipe_info); } else { - printf("\n--- AdbGetEndpointInformation(%u) failure %u", pipe, GetLastError()); + printf("\n--- AdbGetEndpointInformation(%u) failure %u", pipe, + GetLastError()); return false; } } @@ -281,28 +393,20 @@ bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { // Get default bulk read endpoint info if (AdbGetDefaultBulkReadEndpointInformation(interface_handle, &pipe_info)) { printf("\n Default Bulk Read 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); - 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); + DumpEndpointInformation(&pipe_info); } else { - printf("\n--- AdbGetDefaultBulkReadEndpointInformation failure %u", GetLastError()); + 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:"); - 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); - 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); + DumpEndpointInformation(&pipe_info); } else { - printf("\n--- AdbGetDefaultBulkWriteEndpointInformation failure %u", GetLastError()); + printf("\n--- AdbGetDefaultBulkWriteEndpointInformation failure %u", + GetLastError()); return false; } @@ -312,6 +416,48 @@ bool TestInterfaceHandle(ADBAPIHANDLE interface_handle) { return true; } +void HexDump(const void* data, const size_t read_bytes) { + const unsigned char* buf = reinterpret_cast(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]; @@ -361,11 +507,11 @@ bool DeviceHandShake(ADBAPIHANDLE adb_interface) { // 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; + 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); @@ -392,10 +538,13 @@ bool DeviceHandShake(ADBAPIHANDLE adb_interface) { 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(malloc(msg_rcv.data_length)); @@ -408,19 +557,261 @@ bool DeviceHandShake(ADBAPIHANDLE adb_interface) { return false; } - for (ULONG n = 0; n < read_bytes; n++) { - if (0 == (n % 16)) - printf("\n "); - printf("%02X ", buf[n]); - } + HexDump(buf, read_bytes); - printf("\n %s", buf); - - delete buf; + free(buf); } - AdbCloseHandle(adb_write); - AdbCloseHandle(adb_read); + 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( + _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 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::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; +} diff --git a/host/windows/usb/adb_winapi_test/stdafx.h b/host/windows/usb/adb_winapi_test/stdafx.h index 2aa99100a..41f6bdb5f 100755 --- a/host/windows/usb/adb_winapi_test/stdafx.h +++ b/host/windows/usb/adb_winapi_test/stdafx.h @@ -49,6 +49,9 @@ #include #include +#include +#include +#include #include diff --git a/host/windows/usb/api/BUILDME.TXT b/host/windows/usb/api/BUILDME.TXT index 8e131071f..998673ae7 100755 --- a/host/windows/usb/api/BUILDME.TXT +++ b/host/windows/usb/api/BUILDME.TXT @@ -12,8 +12,28 @@ 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. -In order to build AdbWinApi.dll you will need to install Windows Driver Kit, -which can be obtained from Microsoft. Assuming that WDK is installed, you -need to set one of the WDK's build environments, "cd" back into this directory, -and execute "build -cbeEIFZ" to clean and rebuild this project, or you can -execute "build -befEIF" to do a minimal build. +In order to build a directory with a SOURCES file you will need to install +the Windows Driver Kit, which can be obtained from Microsoft: + +Windows Driver Kit Version 7.1.0 +https://www.microsoft.com/en-us/download/details.aspx?id=11800 +md5: 8fe981a1706d43ad34bda496e6558f94 +sha1: de6abdb8eb4e08942add4aa270c763ed4e3d8242 + +This old version is used because it can build for Windows Vista (WDK 8.1 +cannot), it includes compilers (so it doesn't require Visual Studio), and it is +probably not too far from the WDK that this code was originally built with, so +it should be less risky. + +When installing the WDK, uncheck `Device Simulation Framework' because it is +unnecessary and it installs a kernel-mode driver that we don't need. + +Assuming that WDK is installed, you need to set one of the WDK's build +environments (Start Menu -> Windows Driver Kits -> x86 Free Build Environment; +choose the one for the oldest version of Windows you want to support), +"cd" back into this directory, and execute "build -cbeEIFZ" to clean and rebuild +this project, or you can execute "build -befEIF" to do a minimal build. + +When you're done with the WDK build environment, don't forget to right-click the +OACR icon (in the lower-right notification area of the taskbar) and choose +`Close'. diff --git a/host/windows/usb/api/SOURCES b/host/windows/usb/api/SOURCES index 35695217b..25cc89212 100755 --- a/host/windows/usb/api/SOURCES +++ b/host/windows/usb/api/SOURCES @@ -18,8 +18,13 @@ TARGETNAME = AdbWinApi TARGETPATH = obj TARGETTYPE = DYNLINK +_NT_TARGET_VERSION = $(_NT_TARGET_VERSION_VISTA) + UMTYPE = windows DLLDEF = AdbWinApi.def +# Use the same load address as previous versions just to be conservative. This +# really doesn't matter on newer OSes that use ASLR. +DLLBASE = 0x400000 # Use statically linked atl libraries: # - atls.lib for free build @@ -33,6 +38,12 @@ STL_VER = 60 # Use multithreaded libraries USE_LIBCMT = 1 +!IF !$(FREEBUILD) +# In checked build, ATL headers call APIs that are only in atlsd.lib. To use +# atlsd.lib in checked build, set DEBUG_CRTS. +DEBUG_CRTS=1 +!ENDIF + # Include directories INCLUDES = $(DDK_INC_PATH); \ $(SDK_INC_PATH); \ @@ -52,33 +63,28 @@ TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib \ $(SDK_LIB_PATH)\setupapi.lib \ $(SDK_LIB_PATH)\usbd.lib -!IF "$(DDKBUILDENV)" == "fre" -# Libraries for release (free) builds -TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib -!ELSE -# Libraries for debug (checked) builds -TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib -!ENDIF - # Common C defines C_DEFINES= $(C_DEFINES) -DADBWIN_EXPORTS -D_UNICODE \ -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL -!IF "$(DDKBUILDENV)" == "fre" -# C defines for release (free) builds +!IF "$(DDKBUILDENV)" == "fre" +# C defines for release (free) builds C_DEFINES = $(C_DEFINES) -DNDEBUG !ELSE -# C defines for debug (checked) builds +# C defines for debug (checked) builds C_DEFINES = $(C_DEFINES) -D_DEBUG !ENDIF # Turn on all warnings, and treat warnings as errors -MSC_WARNING_LEVEL = /W4 /Wp64 /WX - +MSC_WARNING_LEVEL = /W4 /WX + +# operator new throws C++ exceptions +USE_NATIVE_EH=1 + # Common C defines -USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo - -# Set precompiled header information +USER_C_FLAGS = $(USER_C_FLAGS) /FD /wd4100 /wd4200 /wd4702 /nologo + +# Set precompiled header information PRECOMPILED_CXX = 1 PRECOMPILED_INCLUDE = stdafx.h PRECOMPILED_SOURCEFILE = stdafx.cpp diff --git a/host/windows/usb/api/adb_api.h b/host/windows/usb/api/adb_api.h old mode 100644 new mode 100755 index 29c4ee360..9bd9274e9 --- a/host/windows/usb/api/adb_api.h +++ b/host/windows/usb/api/adb_api.h @@ -91,28 +91,6 @@ typedef struct _AdbEndpointInformation { #define ANDROID_USB_CLASS_ID \ {0xf72fe0d4, 0xcbcb, 0x407d, {0x88, 0x14, 0x9e, 0xd6, 0x73, 0xd0, 0xdd, 0x6b}}; -/// Defines vendor ID for HCT devices. -#define DEVICE_VENDOR_ID 0x0BB4 - -/// Defines product ID for the device with single interface. -#define DEVICE_SINGLE_PRODUCT_ID 0x0C01 - -/// Defines product ID for the Dream composite device. -#define DEVICE_COMPOSITE_PRODUCT_ID 0x0C02 - -/// Defines product ID for the Magic composite device. -#define DEVICE_MAGIC_COMPOSITE_PRODUCT_ID 0x0C03 - -/// Defines interface ID for the device. -#define DEVICE_INTERFACE_ID 0x01 - -/// Defines vendor ID for the device -#define DEVICE_EMULATOR_VENDOR_ID 0x18D1 - -/// Defines product ID for a SoftUSB device simulator that is used to test -/// the driver in isolation from hardware. -#define DEVICE_EMULATOR_PROD_ID 0xDDDD - // The following ifdef block is the standard way of creating macros which make // exporting from a DLL simpler. All files within this DLL are compiled with // the ADBWIN_EXPORTS symbol defined on the command line. this symbol should diff --git a/host/windows/usb/winusb/AdbWinUsbApi.rc b/host/windows/usb/winusb/AdbWinUsbApi.rc index 44aa100f4..a33082e07 100755 --- a/host/windows/usb/winusb/AdbWinUsbApi.rc +++ b/host/windows/usb/winusb/AdbWinUsbApi.rc @@ -57,8 +57,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,0,0,0 - PRODUCTVERSION 2,0,0,0 + FILEVERSION 2,0,0,1 + PRODUCTVERSION 2,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -75,12 +75,12 @@ BEGIN BEGIN VALUE "CompanyName", "Google, inc" VALUE "FileDescription", "Android ADB API (WinUsb)" - VALUE "FileVersion", "2.0.0.0" - VALUE "LegalCopyright", "Copyright (C) 2006 The Android Open Source Project" + VALUE "FileVersion", "2.0.0.1" + VALUE "LegalCopyright", "Copyright (C) 2006-2015 The Android Open Source Project" VALUE "InternalName", "AdbWinUsbApi.dll" VALUE "OriginalFilename", "AdbWinUsbApi.dll" VALUE "ProductName", "Android SDK" - VALUE "ProductVersion", "2.0.0.0" + VALUE "ProductVersion", "2.0.0.1" VALUE "OLESelfRegister", "" END END diff --git a/host/windows/usb/winusb/BUILDME.TXT b/host/windows/usb/winusb/BUILDME.TXT index 2a459ef53..1f7402195 100755 --- a/host/windows/usb/winusb/BUILDME.TXT +++ b/host/windows/usb/winusb/BUILDME.TXT @@ -1,7 +1,42 @@ -In order to build AdbWinUsbApi.dll you will need to install Windows Driver Kit, -which can be obtained from Microsoft. Assuming that WDK is installed, you -need to set one of the WDK's build environments, "cd" back into this directory, -and execute "build -cbeEIFZ" to clean and rebuild this project, or you can -execute "build -befEIF" to do a minimal build. +Copyright (C) 2006 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. + +In order to build a directory with a SOURCES file you will need to install +the Windows Driver Kit, which can be obtained from Microsoft: + +Windows Driver Kit Version 7.1.0 +https://www.microsoft.com/en-us/download/details.aspx?id=11800 +md5: 8fe981a1706d43ad34bda496e6558f94 +sha1: de6abdb8eb4e08942add4aa270c763ed4e3d8242 + +This old version is used because it can build for Windows Vista (WDK 8.1 +cannot), it includes compilers (so it doesn't require Visual Studio), and it is +probably not too far from the WDK that this code was originally built with, so +it should be less risky. + +When installing the WDK, uncheck `Device Simulation Framework' because it is +unnecessary and it installs a kernel-mode driver that we don't need. + +Assuming that WDK is installed, you need to set one of the WDK's build +environments (Start Menu -> Windows Driver Kits -> x86 Free Build Environment; +choose the one for the oldest version of Windows you want to support), +"cd" back into this directory, and execute "build -cbeEIFZ" to clean and rebuild +this project, or you can execute "build -befEIF" to do a minimal build. + Note that you need to build AdbWinApi.dll (..\api) before you build -AdbWinUsbApi.dll, as it depends on AdbWinApi.lib library. +this directory, as this depends on the AdbWinApi.lib import library. + +When you're done with the WDK build environment, don't forget to right-click the +OACR icon (in the lower-right notification area of the taskbar) and choose +`Close'. diff --git a/host/windows/usb/winusb/SOURCES b/host/windows/usb/winusb/SOURCES index 80d17ae1f..d61fbcd2a 100755 --- a/host/windows/usb/winusb/SOURCES +++ b/host/windows/usb/winusb/SOURCES @@ -18,8 +18,13 @@ TARGETNAME = AdbWinUsbApi TARGETPATH = obj TARGETTYPE = DYNLINK +_NT_TARGET_VERSION = $(_NT_TARGET_VERSION_VISTA) + UMTYPE = windows DLLDEF = AdbWinUsbApi.def +# Use the same load address as previous versions just to be conservative. This +# really doesn't matter on newer OSes that use ASLR. +DLLBASE = 0x400000 # Use statically linked atl libraries: # - atls.lib for free build @@ -33,6 +38,12 @@ STL_VER = 60 # Use multithreaded libraries USE_LIBCMT = 1 +!IF !$(FREEBUILD) +# In checked build, ATL headers call APIs that are only in atlsd.lib. To use +# atlsd.lib in checked build, set DEBUG_CRTS. +DEBUG_CRTS=1 +!ENDIF + # Include directories INCLUDES = $(DDK_INC_PATH); \ $(SDK_INC_PATH); \ @@ -54,33 +65,28 @@ TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib \ $(SDK_LIB_PATH)\winusb.lib \ ..\api\obj$(BUILD_ALT_DIR)\i386\AdbWinApi.lib -!IF "$(DDKBUILDENV)" == "fre" -# Libraries for release (free) builds -TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib -!ELSE -# Libraries for debug (checked) builds -TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib -!ENDIF - # Common C defines C_DEFINES= $(C_DEFINES) -DADBWINUSB_EXPORTS -D_UNICODE \ -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL -!IF "$(DDKBUILDENV)" == "fre" -# C defines for release (free) builds +!IF "$(DDKBUILDENV)" == "fre" +# C defines for release (free) builds C_DEFINES = $(C_DEFINES) -DNDEBUG !ELSE -# C defines for debug (checked) builds +# C defines for debug (checked) builds C_DEFINES = $(C_DEFINES) -D_DEBUG !ENDIF # Turn on all warnings, and treat warnings as errors -MSC_WARNING_LEVEL = /W4 /Wp64 /WX - +MSC_WARNING_LEVEL = /W4 /WX + +# operator new throws C++ exceptions +USE_NATIVE_EH=1 + # Common C defines -USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo - -# Set precompiled header information +USER_C_FLAGS = $(USER_C_FLAGS) /FD /wd4100 /wd4200 /wd4702 /nologo + +# Set precompiled header information PRECOMPILED_CXX = 1 PRECOMPILED_INCLUDE = stdafx.h PRECOMPILED_SOURCEFILE = stdafx.cpp diff --git a/host/windows/usb/winusb/adb_winusb_endpoint_object.cpp b/host/windows/usb/winusb/adb_winusb_endpoint_object.cpp index 16f78370c..dee916705 100755 --- a/host/windows/usb/winusb/adb_winusb_endpoint_object.cpp +++ b/host/windows/usb/winusb/adb_winusb_endpoint_object.cpp @@ -27,7 +27,8 @@ AdbWinUsbEndpointObject::AdbWinUsbEndpointObject( AdbWinUsbInterfaceObject* parent_interf, UCHAR endpoint_id, UCHAR endpoint_index) - : AdbEndpointObject(parent_interf, endpoint_id, endpoint_index) { + : AdbEndpointObject(parent_interf, endpoint_id, endpoint_index), + lock_(), is_closing_(false), pending_io_count_(0) { } AdbWinUsbEndpointObject::~AdbWinUsbEndpointObject() { @@ -44,6 +45,54 @@ LONG AdbWinUsbEndpointObject::Release() { return ret; } +bool AdbWinUsbEndpointObject::CloseHandle() { + // This method only returns once all pending IOs are aborted and after + // preventing future pending IOs. This means that once CloseHandle() + // returns, threads using this object won't be using + // parent_winusb_interface()->winusb_handle(), so it can then be safely + // released. + lock_.Lock(); + if (!is_closing_) { + // Set flag to prevent new I/Os from starting up. + is_closing_ = true; + } + + // While there are pending IOs, keep aborting the pipe. We have to do this + // repeatedly because pending_ios_ is incremented before the IO has actually + // started, and abort (probably) only works if the IO has been started. + while (pending_io_count_ > 0) { + lock_.Unlock(); + + // It has been noticed that on Windows 7, if you only call + // WinUsb_AbortPipe(), without first calling WinUsb_ResetPipe(), the call + // to WinUsb_AbortPipe() hangs. + if (!WinUsb_ResetPipe(parent_winusb_interface()->winusb_handle(), + endpoint_id()) || + !WinUsb_AbortPipe(parent_winusb_interface()->winusb_handle(), + endpoint_id())) { + // Reset or Abort failed for unexpected reason. We might not be able to + // abort pending IOs, so we shouldn't keep polling pending_io_count_ or + // else we might hang forever waiting for the IOs to abort. In this + // situation it is preferable to risk a race condition (which may or may + // not crash) and just break now. + lock_.Lock(); + break; + } + + // Give the IO threads time to break out of I/O calls and decrement + // pending_io_count_. They should finish up pretty quick. The amount of time + // "wasted" here (as opposed to if we did synchronization with an event) + // doesn't really matter since this is an uncommon corner-case. + Sleep(16); // 16 ms, old default OS scheduler granularity + + lock_.Lock(); + } + + lock_.Unlock(); + + return AdbEndpointObject::CloseHandle(); +} + ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite( bool is_read, void* buffer, @@ -51,6 +100,9 @@ ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite( ULONG* bytes_transferred, HANDLE event_handle, ULONG time_out) { + // TODO: Do synchronization with is_closing_ and pending_io_count_ like + // CommonSyncReadWrite(). This is not yet implemented because there are no + // callers to Adb{Read,Write}EndpointAsync() in AOSP, and hence no testing. if (!SetTimeout(time_out)) return false; @@ -110,6 +162,24 @@ bool AdbWinUsbEndpointObject::CommonSyncReadWrite(bool is_read, ULONG bytes_to_transfer, ULONG* bytes_transferred, ULONG time_out) { + lock_.Lock(); + if (is_closing_) { + lock_.Unlock(); + // AdbCloseHandle() is in progress, so don't start up any new IOs. + SetLastError(ERROR_HANDLES_CLOSED); + return false; + } else { + // Not closing down, so record the fact that we're doing IO. This will + // prevent CloseHandle() from returning until our IO completes or it aborts + // our IO. + ++pending_io_count_; + lock_.Unlock(); + } + + // Because we've incremented pending_ios_, do the matching decrement when this + // object goes out of scope. + DecrementPendingIO dec(this); + if (!SetTimeout(time_out)) return false; diff --git a/host/windows/usb/winusb/adb_winusb_endpoint_object.h b/host/windows/usb/winusb/adb_winusb_endpoint_object.h index 92b6e04fe..2da7bd206 100755 --- a/host/windows/usb/winusb/adb_winusb_endpoint_object.h +++ b/host/windows/usb/winusb/adb_winusb_endpoint_object.h @@ -72,6 +72,17 @@ class AdbWinUsbEndpointObject : public AdbEndpointObject { */ virtual LONG Release(); + /** \brief This method is called when handle to this object gets closed. + + In this call object is deleted from the AdbObjectHandleMap. We override + this method in order to abort pending IOs and to prevent new IOs from + starting up. + @return true on success or false if object is already closed. If + false is returned GetLastError() provides extended error + information. + */ + virtual bool CloseHandle(); + // // Abstract overrides // @@ -150,6 +161,32 @@ class AdbWinUsbEndpointObject : public AdbEndpointObject { WINUSB_INTERFACE_HANDLE winusb_handle() const { return parent_winusb_interface()->winusb_handle(); } + + protected: + /// Helper class whose destructor decrements pending_io_count_. + class DecrementPendingIO { + public: + DecrementPendingIO(AdbWinUsbEndpointObject* endpoint) + : endpoint_(endpoint) {} + ~DecrementPendingIO() { + endpoint_->lock_.Lock(); + ATLASSERT(endpoint_->pending_io_count_ > 0); + --(endpoint_->pending_io_count_); + endpoint_->lock_.Unlock(); + } + private: + AdbWinUsbEndpointObject* endpoint_; + }; + + protected: + /// Protects is_closing_ and pending_io_count_. + CComAutoCriticalSection lock_; + + /// Once set, prevents new IOs from starting up. + bool is_closing_; + + /// Count of pending IOs potentially blocked in WinUsb APIs. + ULONG pending_io_count_; }; #endif // ANDROID_USB_API_ADB_WINUSB_ENDPOINT_OBJECT_H__