Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a5b06b0f authored by Spencer Low's avatar Spencer Low
Browse files

adb: win32: fix USB device hang when resuming from sleep/hibernation



After resuming Windows from sleep or hibernation, USB connections are not
automatically disconnected. Writing to the USB connections does not return any
errors, but read never returns. My theory is that the device saw the host
sleep/hibernation as a disconnect, so the device is waiting for re-auth from the
host as if the host was just connected.

To solve this, detect resume from sleep/hibernation, disconnect all USB
connections and let the device poll thread re-detect the USB devices in 1 sec.

This is done by using a hidden window that receives power notifications. The
hidden window code is based on Chromium's similar code (platform-tools already
includes the Chromium Authors license).

This depends on a change to AdbWinUsbApi.dll that makes AdbCloseHandle(endpoint)
abort any pending IOs and wait for those IOs to be aborted.

I expect that this should solve many adb and Android Studio related bugs
regarding hangs or errors.

Also in this change:

 - Add D() logging for any errors from AdbWinApi.dll API calls.

 - Stop setting errno to Win32 error values which the caller can't really do
   anything with. Stop calling SetLastError() because the callers don't check
   GetLastError() anyway.

 - Check the return value from writing zero length packets.

 - If the full amount of data isn't written, return an error.

 - Upon any usb_read/usb_write error, kick the connection instead of only
   kicking when ERROR_INVALID_HANDLE is encountered.

 - Restructure some code from nested-if-trees to goto-fail to make it easier
   to follow.

 - Delete usb_name() since it isn't thread-safe and it isn't used.

Change-Id: Iffcf5315ad8593d0c7e93012afaabe6fae354ac1
Signed-off-by: default avatarSpencer Low <CompareAndSwap@gmail.com>
parent c70ac57d
Loading
Loading
Loading
Loading
+284 −122
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@
/** Structure usb_handle describes our connection to the usb device via
  AdbWinApi.dll. This structure is returned from usb_open() routine and
  is expected in each subsequent call that is accessing the device.

  Most members are protected by usb_lock, except for adb_{read,write}_pipe which
  rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
  ability to break a thread out of pipe IO.
*/
struct usb_handle {
  /// Previous entry in the list of opened usb handles
@@ -86,6 +90,9 @@ int recognized_device(usb_handle* handle);
/// registers usb transport for them.
void find_devices();

/// Kicks all USB devices
static void kick_devices();

/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
void* device_poll_thread(void* unused);
@@ -111,9 +118,6 @@ void usb_kick(usb_handle* handle);
/// Closes opened usb handle
int usb_close(usb_handle* handle);

/// Gets interface (device) name for an opened usb handle
const char *usb_name(usb_handle* handle);

int known_device_locked(const char* dev_name) {
  usb_handle* usb;

@@ -177,17 +181,99 @@ void* device_poll_thread(void* unused) {
  return NULL;
}

static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
                                           LPARAM lParam) {
  switch (uMsg) {
  case WM_POWERBROADCAST:
    switch (wParam) {
    case PBT_APMRESUMEAUTOMATIC:
      // Resuming from sleep or hibernation, so kick all existing USB devices
      // and then allow the device_poll_thread to redetect USB devices from
      // scratch. If we don't do this, existing USB devices will never respond
      // to us because they'll be waiting for the connect/auth handshake.
      D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
        "so kicking all USB devices\n");
      kick_devices();
      return TRUE;
    }
  }
  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

static void* _power_notification_thread(void* unused) {
  // This uses a thread with its own window message pump to get power
  // notifications. If adb runs from a non-interactive service account, this
  // might not work (not sure). If that happens to not work, we could use
  // heavyweight WMI APIs to get power notifications. But for the common case
  // of a developer's interactive session, a window message pump is more
  // appropriate.
  D("Created power notification thread\n");

  // Window class names are process specific.
  static const WCHAR kPowerNotificationWindowClassName[] =
    L"PowerNotificationWindow";

  // Get the HINSTANCE corresponding to the module that _power_window_proc
  // is in (the main module).
  const HINSTANCE instance = GetModuleHandleW(NULL);
  if (!instance) {
    // This is such a common API call that this should never fail.
    fatal("GetModuleHandleW failed: %s",
          SystemErrorCodeToString(GetLastError()).c_str());
  }

  WNDCLASSEXW wndclass;
  memset(&wndclass, 0, sizeof(wndclass));
  wndclass.cbSize = sizeof(wndclass);
  wndclass.lpfnWndProc = _power_window_proc;
  wndclass.hInstance = instance;
  wndclass.lpszClassName = kPowerNotificationWindowClassName;
  if (!RegisterClassExW(&wndclass)) {
    fatal("RegisterClassExW failed: %s",
          SystemErrorCodeToString(GetLastError()).c_str());
  }

  if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
                       L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
                       NULL, NULL, instance, NULL)) {
    fatal("CreateWindowExW failed: %s",
          SystemErrorCodeToString(GetLastError()).c_str());
  }

  MSG msg;
  while (GetMessageW(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessageW(&msg);
  }

  // GetMessageW() will return false if a quit message is posted. We don't
  // do that, but it might be possible for that to occur when logging off or
  // shutting down. Not a big deal since the whole process will be going away
  // soon anyway.
  D("Power notification thread exiting\n");

  return NULL;
}

void usb_init() {
  if (!adb_thread_create(device_poll_thread, nullptr)) {
    fatal_errno("cannot create input thread");
    fatal_errno("cannot create device poll thread");
  }
  if (!adb_thread_create(_power_notification_thread, nullptr)) {
    fatal_errno("cannot create power notification thread");
  }
}

usb_handle* do_usb_open(const wchar_t* interface_name) {
  unsigned long name_len = 0;

  // Allocate our handle
  usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
  if (NULL == ret)
    return NULL;
  usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
  if (NULL == ret) {
    D("Could not allocate %u bytes for usb_handle: %s\n", sizeof(usb_handle),
      strerror(errno));
    goto fail;
  }

  // Set linkers back to the handle
  ret->next = ret;
@@ -195,11 +281,10 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {

  // Create interface.
  ret->adb_interface = AdbCreateInterfaceByName(interface_name);

  if (NULL == ret->adb_interface) {
    free(ret);
    errno = GetLastError();
    return NULL;
    D("AdbCreateInterfaceByName failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    goto fail;
  }

  // Open read pipe (endpoint)
@@ -207,45 +292,60 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {
    AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
                                   AdbOpenAccessTypeReadWrite,
                                   AdbOpenSharingModeReadWrite);
  if (NULL != ret->adb_read_pipe) {
  if (NULL == ret->adb_read_pipe) {
    D("AdbOpenDefaultBulkReadEndpoint failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    goto fail;
  }

  // Open write pipe (endpoint)
  ret->adb_write_pipe =
    AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
                                    AdbOpenAccessTypeReadWrite,
                                    AdbOpenSharingModeReadWrite);
    if (NULL != ret->adb_write_pipe) {
      // Save interface name
      unsigned long name_len = 0;
  if (NULL == ret->adb_write_pipe) {
    D("AdbOpenDefaultBulkWriteEndpoint failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    goto fail;
  }

  // Save interface name
  // First get expected name length
  AdbGetInterfaceName(ret->adb_interface,
                      NULL,
                      &name_len,
                      true);
      if (0 != name_len) {
  if (0 == name_len) {
    D("AdbGetInterfaceName returned name length of zero: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    goto fail;
  }

  ret->interface_name = (char*)malloc(name_len);
  if (NULL == ret->interface_name) {
    D("Could not allocate %lu bytes for interface_name: %s\n", name_len,
      strerror(errno));
    goto fail;
  }

        if (NULL != ret->interface_name) {
  // Now save the name
          if (AdbGetInterfaceName(ret->adb_interface,
  if (!AdbGetInterfaceName(ret->adb_interface,
                           ret->interface_name,
                           &name_len,
                           true)) {
    D("AdbGetInterfaceName failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    goto fail;
  }

  // We're done at this point
  return ret;
          }
        } else {
          SetLastError(ERROR_OUTOFMEMORY);
        }
      }
    }
  }

  // Something went wrong.
  int saved_errno = GetLastError();
fail:
  if (NULL != ret) {
    usb_cleanup_handle(ret);
    free(ret);
  SetLastError(saved_errno);
  }

  return NULL;
}
@@ -253,92 +353,130 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {
int usb_write(usb_handle* handle, const void* data, int len) {
  unsigned long time_out = 5000;
  unsigned long written = 0;
  int ret;
  int err = 0;

  D("usb_write %d\n", len);
  if (NULL != handle) {
  if (NULL == handle) {
    D("usb_write was passed NULL handle\n");
    err = EINVAL;
    goto fail;
  }

  // Perform write
    ret = AdbWriteEndpointSync(handle->adb_write_pipe,
  if (!AdbWriteEndpointSync(handle->adb_write_pipe,
                            (void*)data,
                            (unsigned long)len,
                            &written,
                               time_out);
    int saved_errno = GetLastError();
                            time_out)) {
    D("AdbWriteEndpointSync failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    err = EIO;
    goto fail;
  }

    if (ret) {
  // Make sure that we've written what we were asked to write
  D("usb_write got: %ld, expected: %d\n", written, len);
      if (written == (unsigned long)len) {
  if (written != (unsigned long)len) {
    // If this occurs, this code should be changed to repeatedly call
    // AdbWriteEndpointSync() until all bytes are written.
    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld\n",
      len, written);
    err = EIO;
    goto fail;
  }

  if (handle->zero_mask && (len & handle->zero_mask) == 0) {
    // Send a zero length packet
          AdbWriteEndpointSync(handle->adb_write_pipe,
    if (!AdbWriteEndpointSync(handle->adb_write_pipe,
                              (void*)data,
                              0,
                              &written,
                               time_out);
                              time_out)) {
      D("AdbWriteEndpointSync of zero length packet failed: %s\n",
        SystemErrorCodeToString(GetLastError()).c_str());
      err = EIO;
      goto fail;
    }
        return 0;
  }
    } else {
      // assume ERROR_INVALID_HANDLE indicates we are disconnected
      if (saved_errno == ERROR_INVALID_HANDLE)

  return 0;

fail:
  // Any failure should cause us to kick the device instead of leaving it a
  // zombie state with potential to hang.
  if (NULL != handle) {
    D("Kicking device due to error in usb_write\n");
    usb_kick(handle);
  }
    errno = saved_errno;
  } else {
    D("usb_write NULL handle\n");
    SetLastError(ERROR_INVALID_HANDLE);
  }

  D("usb_write failed: %d\n", errno);

  D("usb_write failed\n");
  errno = err;
  return -1;
}

int usb_read(usb_handle *handle, void* data, int len) {
  unsigned long time_out = 0;
  unsigned long read = 0;
  int err = 0;

  D("usb_read %d\n", len);
  if (handle != nullptr) {
  if (NULL == handle) {
    D("usb_read was passed NULL handle\n");
    err = EINVAL;
    goto fail;
  }

  while (len > 0) {
      int ret = AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out);
      int saved_errno = GetLastError();
      D("usb_write got: %ld, expected: %d, errno: %d\n", read, len, saved_errno);
      if (ret) {
    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
                             time_out)) {
      D("AdbReadEndpointSync failed: %s\n",
        SystemErrorCodeToString(GetLastError()).c_str());
      err = EIO;
      goto fail;
    }
    D("usb_read got: %ld, expected: %d\n", read, len);

    data = (char *)data + read;
    len -= read;
  }

        if (len == 0)
  return 0;
      } else {
        // assume ERROR_INVALID_HANDLE indicates we are disconnected
        if (saved_errno == ERROR_INVALID_HANDLE)

fail:
  // Any failure should cause us to kick the device instead of leaving it a
  // zombie state with potential to hang.
  if (NULL != handle) {
    D("Kicking device due to error in usb_read\n");
    usb_kick(handle);
        break;
      }
      errno = saved_errno;
    }
  } else {
    D("usb_read NULL handle\n");
    SetLastError(ERROR_INVALID_HANDLE);
  }

  D("usb_read failed: %d\n", errno);

  D("usb_read failed\n");
  errno = err;
  return -1;
}

// Wrapper around AdbCloseHandle() that logs diagnostics.
static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
  if (!AdbCloseHandle(adb_handle)) {
    D("AdbCloseHandle(%p) failed: %s\n", adb_handle,
      SystemErrorCodeToString(GetLastError()).c_str());
  }
}

void usb_cleanup_handle(usb_handle* handle) {
  D("usb_cleanup_handle\n");
  if (NULL != handle) {
    if (NULL != handle->interface_name)
      free(handle->interface_name);
    // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
    // wait until the pipe no longer uses the interface. Then we can
    // AdbCloseHandle() the interface.
    if (NULL != handle->adb_write_pipe)
      AdbCloseHandle(handle->adb_write_pipe);
      _adb_close_handle(handle->adb_write_pipe);
    if (NULL != handle->adb_read_pipe)
      AdbCloseHandle(handle->adb_read_pipe);
      _adb_close_handle(handle->adb_read_pipe);
    if (NULL != handle->adb_interface)
      AdbCloseHandle(handle->adb_interface);
      _adb_close_handle(handle->adb_interface);

    handle->interface_name = NULL;
    handle->adb_write_pipe = NULL;
@@ -347,16 +485,22 @@ void usb_cleanup_handle(usb_handle* handle) {
  }
}

static void usb_kick_locked(usb_handle* handle) {
  // The reason the lock must be acquired before calling this function is in
  // case multiple threads are trying to kick the same device at the same time.
  usb_cleanup_handle(handle);
}

void usb_kick(usb_handle* handle) {
  D("usb_kick\n");
  if (NULL != handle) {
    adb_mutex_lock(&usb_lock);

    usb_cleanup_handle(handle);
    usb_kick_locked(handle);

    adb_mutex_unlock(&usb_lock);
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    errno = ERROR_INVALID_HANDLE;
    errno = EINVAL;
  }
}

@@ -384,16 +528,6 @@ int usb_close(usb_handle* handle) {
  return 0;
}

const char *usb_name(usb_handle* handle) {
  if (NULL == handle) {
    SetLastError(ERROR_INVALID_HANDLE);
    errno = ERROR_INVALID_HANDLE;
    return NULL;
  }

  return (const char*)handle->interface_name;
}

int recognized_device(usb_handle* handle) {
  if (NULL == handle)
    return 0;
@@ -403,6 +537,8 @@ int recognized_device(usb_handle* handle) {

  if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
                                 &device_desc)) {
    D("AdbGetUsbDeviceDescriptor failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    return 0;
  }

@@ -411,6 +547,8 @@ int recognized_device(usb_handle* handle) {

  if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
                                    &interf_desc)) {
    D("AdbGetUsbInterfaceDescriptor failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    return 0;
  }

@@ -427,6 +565,10 @@ int recognized_device(usb_handle* handle) {
      // assuming zero is a valid bulk endpoint ID
      if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
        handle->zero_mask = endpoint_info.max_packet_size - 1;
        D("device zero_mask: 0x%x\n", handle->zero_mask);
      } else {
        D("AdbGetEndpointInformation failed: %s\n",
          SystemErrorCodeToString(GetLastError()).c_str());
      }
    }

@@ -448,8 +590,11 @@ void find_devices() {
  ADBAPIHANDLE enum_handle =
    AdbEnumInterfaces(usb_class_id, true, true, true);

  if (NULL == enum_handle)
  if (NULL == enum_handle) {
    D("AdbEnumInterfaces failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
    return;
  }

  while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
    // TODO: FIXME - temp hack converting wchar_t into char.
@@ -486,7 +631,8 @@ void find_devices() {
              free(handle);
            }
          } else {
            D("cannot get serial number\n");
            D("cannot get serial number: %s\n",
              SystemErrorCodeToString(GetLastError()).c_str());
            usb_cleanup_handle(handle);
            free(handle);
          }
@@ -500,5 +646,21 @@ void find_devices() {
    entry_buffer_size = sizeof(entry_buffer);
  }

  AdbCloseHandle(enum_handle);
  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
    // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
    D("AdbNextInterface failed: %s\n",
      SystemErrorCodeToString(GetLastError()).c_str());
  }

  _adb_close_handle(enum_handle);
}

static void kick_devices() {
  // Need to acquire lock to safely walk the list which might be modified
  // by another thread.
  adb_mutex_lock(&usb_lock);
  for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
    usb_kick_locked(usb);
  }
  adb_mutex_unlock(&usb_lock);
}