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

Commit 24ed4cfd authored by Archie Pusaka's avatar Archie Pusaka Committed by Archie Pusaka
Browse files

hh: disconnect if UHID is not ready after 10 seconds

It's possible, e.g. due to a bad driver, for us to never receive UHID
OPEN event, thus making the device forever stuck in not ready state.

While this is not a problem by itself, the flag hid_report_queuing
queues any incoming input event during this whole time, so it will
waste memory, especially if the device is very actively sending input
events.

Disconnect the device if UHID is not ready after 10 seconds.

Bug: 359134513
Bug: 362665394
Test: mmm packages/modules/Bluetooth
Flag: com.android.bluetooth.flags.close_hid_if_uhid_ready_too_slow
Change-Id: I84cdd578cacfa68c21782e5a52b83184b0776fac
parent 94cfdf46
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ typedef struct {

typedef enum : uint8_t {
  BTA_HH_UHID_INBOUND_INPUT_EVT,
  BTA_HH_UHID_INBOUND_OPEN_TIMEOUT_EVT,
  BTA_HH_UHID_INBOUND_READY_EVT,
  BTA_HH_UHID_INBOUND_CLOSE_EVT,
  BTA_HH_UHID_INBOUND_DSCP_EVT,
  BTA_HH_UHID_INBOUND_GET_REPORT_EVT,
+43 −10
Original line number Diff line number Diff line
@@ -58,6 +58,9 @@ static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX];
#define BTA_HH_UHID_POLL_PERIOD2_MS -1
/* Max number of polling interrupt allowed */
#define BTA_HH_UHID_INTERRUPT_COUNT_MAX 100
/* Disconnect if UHID isn't ready after this many milliseconds. */
#define BTA_HH_UHID_READY_DISCONN_TIMEOUT_MS 10000
#define BTA_HH_UHID_READY_SHORT_DISCONN_TIMEOUT_MS 2000

using namespace bluetooth;

@@ -208,21 +211,35 @@ static void uhid_set_ready(btif_hh_uhid_t* p_uhid) {
}

// This runs on main thread.
static void uhid_open_timeout(void* data) {
static void uhid_delayed_ready_cback(void* data) {
  int send_fd = PTR_TO_INT(data);
  tBTA_HH_TO_UHID_EVT ev = {};

  // Notify the UHID thread that the timer has expired.
  log::verbose("UHID Open timeout evt");
  ev.type = BTA_HH_UHID_INBOUND_OPEN_TIMEOUT_EVT;
  log::verbose("UHID delayed ready evt");
  ev.type = BTA_HH_UHID_INBOUND_READY_EVT;
  to_uhid_thread(send_fd, &ev, 0);
}

// This runs on main thread.
static void uhid_ready_disconn_timeout(void* data) {
  int dev_handle = PTR_TO_INT(data);

  log::verbose("UHID ready disconn timeout evt");
  BTA_HhClose(dev_handle);
}

static void uhid_on_open(btif_hh_uhid_t* p_uhid) {
  if (p_uhid->ready_for_data || alarm_is_scheduled(p_uhid->ready_timer)) {
  if (p_uhid->ready_for_data || alarm_is_scheduled(p_uhid->delayed_ready_timer)) {
    return;
  }

  if (com::android::bluetooth::flags::close_hid_if_uhid_ready_too_slow()) {
    if (alarm_is_scheduled(p_uhid->ready_disconn_timer)) {
      alarm_cancel(p_uhid->ready_disconn_timer);
    }
  }

  // On some platforms delay is required, because even though UHID has indicated
  // ready, the input events might still not be processed, and therefore lost.
  // If it's not required, immediately set UHID as ready.
@@ -232,7 +249,7 @@ static void uhid_on_open(btif_hh_uhid_t* p_uhid) {
    return;
  }

  alarm_set_on_mloop(p_uhid->ready_timer, ready_delay_ms, uhid_open_timeout,
  alarm_set_on_mloop(p_uhid->delayed_ready_timer, ready_delay_ms, uhid_delayed_ready_cback,
                     INT_TO_PTR(p_uhid->internal_send_fd));
}

@@ -293,8 +310,18 @@ static int uhid_read_outbound_event(btif_hh_uhid_t* p_uhid) {
      log::verbose("UHID_CLOSE from uhid-dev\n");
      p_uhid->ready_for_data = false;
      if (com::android::bluetooth::flags::hid_report_queuing()) {
        if (alarm_is_scheduled(p_uhid->ready_timer)) {
          alarm_cancel(p_uhid->ready_timer);
        if (alarm_is_scheduled(p_uhid->delayed_ready_timer)) {
          alarm_cancel(p_uhid->delayed_ready_timer);
        }
        if (com::android::bluetooth::flags::close_hid_if_uhid_ready_too_slow()) {
          // It's possible to get OPEN->CLOSE->OPEN sequence from UHID. Therefore, instead of
          // immediately disconnecting when receiving CLOSE, here we wait a while and will
          // disconnect if we don't receive OPEN before it times out.
          if (!alarm_is_scheduled(p_uhid->ready_disconn_timer)) {
            alarm_set_on_mloop(p_uhid->ready_disconn_timer,
                               BTA_HH_UHID_READY_SHORT_DISCONN_TIMEOUT_MS,
                               uhid_ready_disconn_timeout, INT_TO_PTR(p_uhid->dev_handle));
          }
        }
      }
      break;
@@ -380,7 +407,7 @@ static int uhid_read_inbound_event(btif_hh_uhid_t* p_uhid) {
        uhid_queue_input(p_uhid, &ev.uhid, ret - 1);
      }
      break;
    case BTA_HH_UHID_INBOUND_OPEN_TIMEOUT_EVT:
    case BTA_HH_UHID_INBOUND_READY_EVT:
      uhid_set_ready(p_uhid);
      break;
    case BTA_HH_UHID_INBOUND_CLOSE_EVT:
@@ -471,7 +498,8 @@ static void uhid_fd_close(btif_hh_uhid_t* p_uhid) {
    fixed_queue_free(p_uhid->input_queue, nullptr);
    p_uhid->input_queue = nullptr;

    alarm_free(p_uhid->ready_timer);
    alarm_free(p_uhid->delayed_ready_timer);
    alarm_free(p_uhid->ready_disconn_timer);
    osi_free(p_uhid);
  }
}
@@ -660,7 +688,12 @@ static void* btif_hh_poll_event_thread(void* arg) {
      return 0;
    }
    p_uhid->ready_for_data = false;
    p_uhid->ready_timer = alarm_new("uhid_ready_timer");
    p_uhid->delayed_ready_timer = alarm_new("uhid_delayed_ready_timer");
    p_uhid->ready_disconn_timer = alarm_new("uhid_ready_disconn_timer");
    if (com::android::bluetooth::flags::close_hid_if_uhid_ready_too_slow()) {
      alarm_set_on_mloop(p_uhid->ready_disconn_timer, BTA_HH_UHID_READY_DISCONN_TIMEOUT_MS,
                         uhid_ready_disconn_timeout, INT_TO_PTR(p_uhid->dev_handle));
    }

    p_uhid->get_rpt_id_queue = fixed_queue_new(SIZE_MAX);
    log::assert_that(p_uhid->get_rpt_id_queue, "assert failed: p_uhid->get_rpt_id_queue");
+2 −1
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ typedef struct {
  fixed_queue_t* set_rpt_id_queue;
#endif  // ENABLE_UHID_SET_REPORT
  fixed_queue_t* input_queue;  // to store the inputs before uhid is ready.
  alarm_t* ready_timer;
  alarm_t* delayed_ready_timer;  // to delay marking a device as ready, give input chance to listen.
  alarm_t* ready_disconn_timer;  // to disconnect device if still not ready after some time.
} btif_hh_uhid_t;

/* Control block to maintain properties of devices */