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

Commit 23672095 authored by Himanshu Rawat's avatar Himanshu Rawat Committed by Automerger Merge Worker
Browse files

Merge "Handle service changed indication in HOGP host" am: 9b561328

parents fbc99863 9b561328
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;