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

Commit 9b561328 authored by Himanshu Rawat's avatar Himanshu Rawat Committed by Gerrit Code Review
Browse files

Merge "Handle service changed indication in HOGP host"

parents 1d832b93 49266db0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -989,7 +989,7 @@ void bta_hh_close_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
void bta_hh_get_dscp_act(tBTA_HH_DEV_CB* p_cb,
                         UNUSED_ATTR const tBTA_HH_DATA* p_data) {
  if (p_cb->is_le_device) {
    if (p_cb->hid_srvc.in_use) {
    if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED) {
      p_cb->dscp_info.hid_handle = p_cb->hid_handle;
    }
    bta_hh_le_get_dscp_act(p_cb);
+7 −1
Original line number Diff line number Diff line
@@ -147,8 +147,14 @@ typedef struct {
#define BTA_HH_LE_RPT_MAX 20
#endif

enum tBTA_HH_SERVICE_STATE {
  BTA_HH_SERVICE_UNKNOWN,
  BTA_HH_SERVICE_CHANGED,
  BTA_HH_SERVICE_DISCOVERED,
};

typedef struct {
  bool in_use;
  tBTA_HH_SERVICE_STATE state;
  uint8_t srvc_inst_id;
  tBTA_HH_LE_RPT report[BTA_HH_LE_RPT_MAX];

+133 −33
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ static const char* bta_hh_le_rpt_name[4] = {"UNKNOWN", "INPUT", "OUTPUT",
static void bta_hh_le_hid_report_dbg(tBTA_HH_DEV_CB* p_cb) {
  APPL_TRACE_DEBUG("%s: HID Report DB", __func__);

  if (!p_cb->hid_srvc.in_use) return;
  if (p_cb->hid_srvc.state < BTA_HH_SERVICE_DISCOVERED) return;

  tBTA_HH_LE_RPT* p_rpt = &p_cb->hid_srvc.report[0];

@@ -304,7 +304,8 @@ static tBTA_HH_DEV_CB* bta_hh_le_find_dev_cb_by_bda(const RawAddress& bda) {
 ******************************************************************************/
static uint8_t bta_hh_le_find_service_inst_by_battery_inst_id(
    tBTA_HH_DEV_CB* p_cb, uint8_t ba_inst_id) {
  if (p_cb->hid_srvc.in_use && p_cb->hid_srvc.incl_srvc_inst == ba_inst_id) {
  if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED &&
      p_cb->hid_srvc.incl_srvc_inst == ba_inst_id) {
    return p_cb->hid_srvc.srvc_inst_id;
  }
  return BTA_HH_IDX_INVALID;
@@ -849,7 +850,8 @@ static void bta_hh_le_get_protocol_mode(tBTA_HH_DEV_CB* p_cb) {
  tBTA_HH_HSDATA hs_data;
  p_cb->w4_evt = BTA_HH_GET_PROTO_EVT;

  if (p_cb->hid_srvc.in_use && p_cb->hid_srvc.proto_mode_handle != 0) {
  if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED &&
      p_cb->hid_srvc.proto_mode_handle != 0) {
    BtaGattQueue::ReadCharacteristic(p_cb->conn_id,
                                     p_cb->hid_srvc.proto_mode_handle,
                                     get_protocol_mode_cb, p_cb);
@@ -877,8 +879,18 @@ static void bta_hh_le_dis_cback(const RawAddress& addr,
                                tDIS_VALUE* p_dis_value) {
  tBTA_HH_DEV_CB* p_cb = bta_hh_le_find_dev_cb_by_bda(addr);

  if (p_cb == NULL || p_dis_value == NULL) {
    APPL_TRACE_ERROR("received unexpected/error DIS callback");
  if (p_cb == nullptr) {
    LOG_WARN("Unknown address");
    return;
  }

  if (p_cb->status == BTA_HH_ERR_SDP) {
    LOG_WARN("HID service was not found");
    return;
  }

  if (p_dis_value == nullptr) {
    LOG_WARN("Invalid value");
    return;
  }

@@ -965,7 +977,7 @@ void bta_hh_security_cmpl(tBTA_HH_DEV_CB* p_cb,
                          UNUSED_ATTR const tBTA_HH_DATA* p_buf) {
  APPL_TRACE_DEBUG("%s", __func__);
  if (p_cb->status == BTA_HH_OK) {
    if (!p_cb->hid_srvc.in_use) {
    if (p_cb->hid_srvc.state < BTA_HH_SERVICE_DISCOVERED) {
      APPL_TRACE_DEBUG("bta_hh_security_cmpl no reports loaded, try to load");

      /* start loading the cache if not in stack */
@@ -977,7 +989,8 @@ void bta_hh_security_cmpl(tBTA_HH_DEV_CB* p_cb,
      }
    }
    /*  discovery has been done for HID service */
    if (p_cb->app_id != 0 && p_cb->hid_srvc.in_use) {
    if (p_cb->app_id != 0 &&
        p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED) {
      APPL_TRACE_DEBUG("%s: discovery has been done for HID service", __func__);
      /* configure protocol mode */
      if (!bta_hh_le_set_protocol_mode(p_cb, p_cb->mode)) {
@@ -1148,6 +1161,14 @@ static void bta_hh_le_close(const tBTA_GATTC_CLOSE& gattc_data) {
             ADDRESS_TO_LOGGABLE_CSTR(gattc_data.remote_bda));
    return;
  }

  if (p_cb->hid_srvc.state == BTA_HH_SERVICE_CHANGED) {
    /* Service change would have already prompted a local disconnection */
    LOG_WARN("Disconnected after service changed indication:%s",
             ADDRESS_TO_LOGGABLE_CSTR(gattc_data.remote_bda));
    return;
  }

  p_cb->conn_id = GATT_INVALID_CONN_ID;
  p_cb->security_pending = false;

@@ -1449,14 +1470,15 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
  if (p_dev_cb == NULL) return;

  if (p_data->status != GATT_SUCCESS) {
    LOG_ERROR("Service discovery failed %d", p_data->status);
    p_dev_cb->status = BTA_HH_ERR_SDP;
    /* close the connection and report service discovery complete with error */
    bta_hh_le_api_disc_act(p_dev_cb);
    return;
  }

  const std::list<gatt::Service>* services =
      BTA_GATTC_GetServices(p_data->conn_id);
  const std::list<gatt::Service>* services = BTA_GATTC_GetServices(p_data->conn_id);
  const gatt::Service* gap_service = nullptr;
  const gatt::Service* scp_service = nullptr;

  bool have_hid = false;
  for (const gatt::Service& service : *services) {
@@ -1465,7 +1487,7 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
      have_hid = true;

      /* found HID primamry service */
      p_dev_cb->hid_srvc.in_use = true;
      p_dev_cb->hid_srvc.state = BTA_HH_SERVICE_DISCOVERED;
      p_dev_cb->hid_srvc.srvc_inst_id = service.handle;
      p_dev_cb->hid_srvc.proto_mode_handle = 0;
      p_dev_cb->hid_srvc.control_point_handle = 0;
@@ -1475,31 +1497,42 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
      APPL_TRACE_DEBUG("%s: have HID service inst_id= %d", __func__,
                       p_dev_cb->hid_srvc.srvc_inst_id);
    } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_SCAN_PARAM)) {

      for (const gatt::Characteristic& charac : service.characteristics) {
        if (charac.uuid == Uuid::From16Bit(GATT_UUID_SCAN_REFRESH)) {

          if (charac.properties & GATT_CHAR_PROP_BIT_NOTIFY)
            p_dev_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT;
          else
            p_dev_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE;

          break;
      scp_service = &service;
    } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
      gap_service = &service;
    }
  }
    } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
      // TODO(jpawlowski): this should be done by GAP profile, remove when GAP
      // is fixed.
      for (const gatt::Characteristic& charac : service.characteristics) {

  if (!have_hid) {
    LOG_ERROR("HID service not found");
    p_dev_cb->status = BTA_HH_ERR_SDP;
    bta_hh_le_api_disc_act(p_dev_cb);
    return;
  }

  if (gap_service != nullptr) {
    // TODO: This should be done by GAP profile, remove when GAP is fixed.
    for (const gatt::Characteristic& charac : gap_service->characteristics) {
      if (charac.uuid == Uuid::From16Bit(GATT_UUID_GAP_PREF_CONN_PARAM)) {
        /* read the char value */
          BtaGattQueue::ReadCharacteristic(p_dev_cb->conn_id,
                                           charac.value_handle,
        BtaGattQueue::ReadCharacteristic(p_dev_cb->conn_id, charac.value_handle,
                                         read_pref_conn_params_cb, p_dev_cb);
        break;
      }
    }
  }

  if (scp_service != nullptr) {
    for (const gatt::Characteristic& charac : scp_service->characteristics) {
      if (charac.uuid == Uuid::From16Bit(GATT_UUID_SCAN_REFRESH)) {
        if (charac.properties & GATT_CHAR_PROP_BIT_NOTIFY) {
          p_dev_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT;
        } else {
          p_dev_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE;
        }
        break;
      }
    }
  }

  bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status);
@@ -1600,7 +1633,7 @@ void bta_hh_le_open_fail(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
    bta_hh_clear_service_cache(p_cb);
  }

  if (p_cb->is_le_device) {
  if (p_cb->is_le_device && p_cb->status != BTA_HH_ERR_SDP) {
    LOG_DEBUG("gd_acl: Re-adding HID device to acceptlist");
    // gd removes from bg list after failed connection
    // Correct the cached state to allow re-add to acceptlist.
@@ -1965,7 +1998,7 @@ void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
 *
 ******************************************************************************/
void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB* p_cb) {
  if (p_cb->hid_srvc.in_use) {
  if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED) {
    p_cb->dscp_info.descriptor.dl_len = p_cb->hid_srvc.descriptor.dl_len;
    p_cb->dscp_info.descriptor.dsc_list = p_cb->hid_srvc.descriptor.dsc_list;

@@ -2063,6 +2096,65 @@ void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB* p_dev_cb) {
  bta_hh_le_deregister_input_notif(p_dev_cb);
}

static void bta_hh_le_service_changed(RawAddress remote_bda) {
  tBTA_HH_DEV_CB* p_cb = bta_hh_le_find_dev_cb_by_bda(remote_bda);
  if (p_cb == nullptr) {
    LOG_WARN("Received close event with unknown device:%s",
             ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return;
  }

  /* Forget the cached reports */
  bta_hh_le_co_reset_rpt_cache(p_cb->addr, p_cb->app_id);
  p_cb->dscp_info.descriptor.dsc_list = NULL;
  osi_free_and_reset((void**)&p_cb->hid_srvc.rpt_map);
  p_cb->hid_srvc = {};
  p_cb->hid_srvc.state = BTA_HH_SERVICE_CHANGED;
  p_cb->status = BTA_HH_HS_SERVICE_CHANGED;

  /* Pretend that the HOGP device disconnected so that higher layers don't
     try to communicate with it while the GATT database is rediscovered. */
  const tBTA_HH_DATA data = {
      .le_close =
          {
              .conn_id = p_cb->conn_id,
              .reason = GATT_CONN_OK,
              .hdr =
                  {
                      .event = BTA_HH_GATT_CLOSE_EVT,
                      .layer_specific = static_cast<uint16_t>(p_cb->hid_handle),
                  },
          },
  };
  bta_hh_sm_execute(p_cb, BTA_HH_GATT_CLOSE_EVT, &data);
}

static void bta_hh_le_service_discovery_done(RawAddress remote_bda) {
  tBTA_HH_DEV_CB* p_cb = bta_hh_le_find_dev_cb_by_bda(remote_bda);
  if (p_cb == nullptr) {
    LOG_WARN("Received service discovery done event for unknown device:%s",
             ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return;
  }

  if (p_cb->hid_srvc.state == BTA_HH_SERVICE_CHANGED) {
    /* Service rediscovery completed after service change.
       Pretend to have connected with a new HOGP device. */
    p_cb->hid_srvc.state = BTA_HH_SERVICE_UNKNOWN;
    const tBTA_GATTC_OPEN open = {
        .status = GATT_SUCCESS,
        .conn_id = p_cb->conn_id,
        .client_if = bta_hh_cb.gatt_if,
        .remote_bda = remote_bda,
        .transport = BT_TRANSPORT_LE,
        .mtu = 0,
    };
    bta_hh_sm_execute(p_cb, BTA_HH_GATT_OPEN_EVT, (tBTA_HH_DATA*)&open);
  } else {
    LOG_INFO("Discovery done, service state: %d", p_cb->hid_srvc.state);
  }
}

/*******************************************************************************
 *
 * Function         bta_hh_gattc_callback
@@ -2104,6 +2196,14 @@ static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
      bta_hh_le_input_rpt_notify(&p_data->notify);
      break;

    case BTA_GATTC_SRVC_CHG_EVT:
      bta_hh_le_service_changed(p_data->remote_bda);
      break;

    case BTA_GATTC_SRVC_DISC_DONE_EVT:
      bta_hh_le_service_discovery_done(p_data->remote_bda);
      break;

    case BTA_GATTC_ENC_CMPL_CB_EVT: /* 17 */
      p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->enc_cmpl.remote_bda);
      if (p_dev_cb) {
@@ -2134,7 +2234,7 @@ static void bta_hh_process_cache_rpt(tBTA_HH_DEV_CB* p_cb,

  if (num_rpt != 0) /* no cache is found */
  {
    p_cb->hid_srvc.in_use = true;
    p_cb->hid_srvc.state = BTA_HH_SERVICE_UNKNOWN;

    /* set the descriptor info */
    p_cb->hid_srvc.descriptor.dl_len = p_cb->dscp_info.descriptor.dl_len;
+2 −1
Original line number Diff line number Diff line
@@ -138,7 +138,8 @@ typedef enum : uint8_t {
  BTA_HH_ERR_NO_RES,      /* out of system resources */
  BTA_HH_ERR_AUTH_FAILED, /* authentication fail */
  BTA_HH_ERR_HDL,
  BTA_HH_ERR_SEC
  BTA_HH_ERR_SEC,
  BTA_HH_HS_SERVICE_CHANGED /* GATT service changed on the peer */
} tBTA_HH_STATUS;

inline tBTA_HH_STATUS to_bta_hh_status(uint32_t status) {
+7 −0
Original line number Diff line number Diff line
@@ -878,6 +878,13 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) {
        if (p_dev->local_vup) {
          p_dev->local_vup = false;
          BTA_DmRemoveDevice(p_dev->bd_addr);
        } else if (p_data->dev_status.status == BTA_HH_HS_SERVICE_CHANGED) {
          /* Local disconnection due to service change in the HOGP device.
             HID descriptor would be read again, so remove it from cache. */
          LOG_WARN(
              "Removing cached descriptor due to service change, handle = %d",
              p_data->dev_status.handle);
          btif_storage_remove_hid_info(p_dev->bd_addr);
        }

        btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;