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

Commit 843330f9 authored by Himanshu Rawat's avatar Himanshu Rawat
Browse files

Map headtracking service characteristics to HOGP report characteristics

Parse Android Headtracking service. Map its characteristics to HOGP
report characteristics.

Test: mmm packages/modules/Bluetooth
Flag: com.android.bluetooth.flags.android_headtracker_service
Bug: 332590397
Bug: 335708774
Change-Id: Id72c5cd8b5fb25df3105214bcdd8d345d5a513e2
parent fb7759ee
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -96,6 +96,7 @@ cc_library_static {
        "hh/bta_hh_act.cc",
        "hh/bta_hh_act.cc",
        "hh/bta_hh_api.cc",
        "hh/bta_hh_api.cc",
        "hh/bta_hh_cfg.cc",
        "hh/bta_hh_cfg.cc",
        "hh/bta_hh_headtracker.cc",
        "hh/bta_hh_le.cc",
        "hh/bta_hh_le.cc",
        "hh/bta_hh_main.cc",
        "hh/bta_hh_main.cc",
        "hh/bta_hh_utils.cc",
        "hh/bta_hh_utils.cc",
@@ -1290,6 +1291,7 @@ cc_test {
        "hh/bta_hh_act.cc",
        "hh/bta_hh_act.cc",
        "hh/bta_hh_api.cc",
        "hh/bta_hh_api.cc",
        "hh/bta_hh_cfg.cc",
        "hh/bta_hh_cfg.cc",
        "hh/bta_hh_headtracker.cc",
        "hh/bta_hh_le.cc",
        "hh/bta_hh_le.cc",
        "hh/bta_hh_main.cc",
        "hh/bta_hh_main.cc",
        "hh/bta_hh_utils.cc",
        "hh/bta_hh_utils.cc",
+1 −0
Original line number Original line Diff line number Diff line
@@ -81,6 +81,7 @@ static_library("bta") {
    "hh/bta_hh_act.cc",
    "hh/bta_hh_act.cc",
    "hh/bta_hh_api.cc",
    "hh/bta_hh_api.cc",
    "hh/bta_hh_cfg.cc",
    "hh/bta_hh_cfg.cc",
    "hh/bta_hh_headtracker.cc",
    "hh/bta_hh_le.cc",
    "hh/bta_hh_le.cc",
    "hh/bta_hh_main.cc",
    "hh/bta_hh_main.cc",
    "hh/bta_hh_utils.cc",
    "hh/bta_hh_utils.cc",
+167 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright 2024 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.
 */

#define LOG_TAG "bta_hh_headtracker"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>

#include "bta/hh/bta_hh_int.h"
#include "stack/include/bt_types.h"
#include "stack/include/bt_uuid16.h"
#include "types/bluetooth/uuid.h"

using bluetooth::Uuid;
using namespace bluetooth;

static bool bta_hh_headtracker_parse_version_charac(
    tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
  tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
      p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
      charac.value_handle);
  if (p_rpt == nullptr) {
    log::error("Add report entry failed !!!");
    return false;
  }

  bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 2);
  return true;
}

static bool bta_hh_headtracker_prase_control_charac(
    tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
  tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
      p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
      charac.value_handle);
  if (p_rpt == nullptr) {
    log::error("Add report entry failed !!!");
    return false;
  }

  bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 1);
  return true;
}

static bool bta_hh_headtracker_parse_report_charac(
    tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
  tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
      p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
      charac.value_handle);
  if (p_rpt == nullptr) {
    log::error("Add report entry failed !!!");
    return false;
  }

  bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_INPUT, 1);
  return true;
}

/* Hardcoded Android Headtracker HID descriptor */
static const uint8_t ANDROID_HEADTRACKER_DESCRIPTOR[] = {
    0x05, 0x20, 0x09, 0xe1, 0xa1, 0x01, 0x85, 0x02, 0x0a, 0x08, 0x03, 0x15,
    0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x19, 0xb1, 0x03, 0x0a, 0x02, 0x03,
    0x15, 0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x10, 0xb1, 0x03, 0x85, 0x01,
    0x0a, 0x16, 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1,
    0x02, 0x0a, 0x40, 0x08, 0x0a, 0x41, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x19,
    0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a,
    0x55, 0x08, 0x0a, 0x51, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x0e, 0x03, 0x15,
    0x00, 0x25, 0x3f, 0x35, 0x0a, 0x45, 0x64, 0x75, 0x06, 0x95, 0x01, 0x66,
    0x01, 0x10, 0x55, 0x0d, 0xb1, 0x02, 0x0a, 0x10, 0xf4, 0x15, 0x00, 0x25,
    0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a, 0x00, 0xf8, 0x0a, 0x01,
    0xf8, 0xb1, 0x00, 0xc0, 0xb1, 0x02, 0x0a, 0x44, 0x5,  0x16, 0x01, 0x80,
    0x26, 0xff, 0x7f, 0x37, 0x60, 0x4f, 0x46, 0xed, 0x47, 0xa1, 0xb0, 0xb9,
    0x12, 0x55, 0x08, 0x75, 0x10, 0x95, 0x03, 0x81, 0x02, 0x0a, 0x45, 0x05,
    0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x35, 0xe0, 0x45, 0x20, 0x55, 0x00,
    0x75, 0x10, 0x95, 0x03, 0x81, 0x02, 0x0a, 0x46, 0x05, 0x15, 0x00, 0x25,
    0xff, 0x35, 0x00, 0x45, 0xff, 0x55, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81,
    0x02, 0xc0};

/*******************************************************************************
 *
 * Function         bta_hh_headtracker_parse_service
 *
 * Description      This function discover all characteristics of the
 *                  headtracker service
 *
 * Parameters:
 *
 ******************************************************************************/
void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb,
                                      const gatt::Service* service) {
  log::info("");
  bta_hh_le_srvc_init(p_dev_cb, service->handle);
  p_dev_cb->mode = BTA_HH_PROTO_RPT_MODE;
  p_dev_cb->hid_srvc.is_headtracker = true;

  bta_hh_le_save_report_map(p_dev_cb,
                            (uint16_t)sizeof(ANDROID_HEADTRACKER_DESCRIPTOR),
                            (uint8_t*)&ANDROID_HEADTRACKER_DESCRIPTOR);

  bool version_found = false;
  bool control_found = false;
  bool data_found = false;

  for (const gatt::Characteristic& charac : service->characteristics) {
    if (charac.uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID) {
      version_found = bta_hh_headtracker_parse_version_charac(p_dev_cb, charac);
    } else if (charac.uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID) {
      control_found = bta_hh_headtracker_prase_control_charac(p_dev_cb, charac);
    } else if (charac.uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID) {
      data_found = bta_hh_headtracker_parse_report_charac(p_dev_cb, charac);
    } else {
      log::warn("Unexpected characteristic {}", charac.uuid.ToString());
    }
  }

  tGATT_STATUS status = (version_found && control_found && data_found)
                            ? GATT_SUCCESS
                            : GATT_ERROR;
  bta_hh_le_service_parsed(p_dev_cb, status);
}

/*******************************************************************************
 *
 * Function         bta_hh_headtracker_supported
 *
 * Description      Checks if the connection instance is for headtracker
 *
 * Parameters:
 *
 ******************************************************************************/
bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) {
  return com::android::bluetooth::flags::android_headtracker_service() &&
         p_dev_cb->hid_srvc.is_headtracker;
}

/*******************************************************************************
 *
 * Function         bta_hh_get_uuid16
 *
 * Description      Maps Headtracker characteristic UUIDs to HOGP Report UUID
 *
 * Parameters:
 *
 ******************************************************************************/
uint16_t bta_hh_get_uuid16(tBTA_HH_DEV_CB* p_dev_cb, Uuid uuid) {
  if (bta_hh_headtracker_supported(p_dev_cb) &&
      (uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID ||
       uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID ||
       uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID)) {
    return GATT_UUID_HID_REPORT;
  } else {
    return uuid.As16Bit();
  }
}
+6 −1
Original line number Original line Diff line number Diff line
@@ -168,7 +168,7 @@ typedef struct {
  uint8_t* rpt_map;
  uint8_t* rpt_map;
  uint16_t ext_rpt_ref;
  uint16_t ext_rpt_ref;
  tBTA_HH_DEV_DESCR descriptor;
  tBTA_HH_DEV_DESCR descriptor;

  bool is_headtracker;
} tBTA_HH_LE_HID_SRVC;
} tBTA_HH_LE_HID_SRVC;


/* convert a HID handle to the LE CB index */
/* convert a HID handle to the LE CB index */
@@ -325,6 +325,11 @@ void bta_hh_le_save_report_map(tBTA_HH_DEV_CB* p_dev_cb, uint16_t len,
                               uint8_t* desc);
                               uint8_t* desc);
void bta_hh_le_service_parsed(tBTA_HH_DEV_CB* p_dev_cb, tGATT_STATUS status);
void bta_hh_le_service_parsed(tBTA_HH_DEV_CB* p_dev_cb, tGATT_STATUS status);


void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb,
                                      const gatt::Service* service);
bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb);
uint16_t bta_hh_get_uuid16(tBTA_HH_DEV_CB* p_dev_cb, bluetooth::Uuid uuid);

#if (BTA_HH_DEBUG == TRUE)
#if (BTA_HH_DEBUG == TRUE)
void bta_hh_trace_dev_db(void);
void bta_hh_trace_dev_db(void);
#endif
#endif
+24 −7
Original line number Original line Diff line number Diff line
@@ -666,7 +666,7 @@ static void write_rpt_ctl_cfg_cb(uint16_t conn_id, tGATT_STATUS status,
  tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
  tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
  const gatt::Characteristic* characteristic =
  const gatt::Characteristic* characteristic =
      BTA_GATTC_GetOwningCharacteristic(conn_id, handle);
      BTA_GATTC_GetOwningCharacteristic(conn_id, handle);
  uint16_t char_uuid = characteristic->uuid.As16Bit();
  uint16_t char_uuid = bta_hh_get_uuid16(p_dev_cb, characteristic->uuid);


  srvc_inst_id = BTA_GATTC_GetOwningService(conn_id, handle)->handle;
  srvc_inst_id = BTA_GATTC_GetOwningService(conn_id, handle)->handle;
  switch (char_uuid) {
  switch (char_uuid) {
@@ -778,7 +778,8 @@ static bool bta_hh_le_set_protocol_mode(tBTA_HH_DEV_CB* p_cb,


  cback_data.handle = p_cb->hid_handle;
  cback_data.handle = p_cb->hid_handle;
  /* boot mode is not supported in the remote device */
  /* boot mode is not supported in the remote device */
  if (p_cb->hid_srvc.proto_mode_handle == 0) {
  if (p_cb->hid_srvc.proto_mode_handle == 0 ||
      bta_hh_headtracker_supported(p_cb)) {
    p_cb->mode = BTA_HH_PROTO_RPT_MODE;
    p_cb->mode = BTA_HH_PROTO_RPT_MODE;


    if (mode == BTA_HH_PROTO_BOOT_MODE) {
    if (mode == BTA_HH_PROTO_BOOT_MODE) {
@@ -860,7 +861,8 @@ static void bta_hh_le_get_protocol_mode(tBTA_HH_DEV_CB* p_cb) {
  p_cb->w4_evt = BTA_HH_GET_PROTO_EVT;
  p_cb->w4_evt = BTA_HH_GET_PROTO_EVT;


  if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED &&
  if (p_cb->hid_srvc.state >= BTA_HH_SERVICE_DISCOVERED &&
      p_cb->hid_srvc.proto_mode_handle != 0) {
      p_cb->hid_srvc.proto_mode_handle != 0 &&
      !bta_hh_headtracker_supported(p_cb)) {
    BtaGattQueue::ReadCharacteristic(p_cb->conn_id,
    BtaGattQueue::ReadCharacteristic(p_cb->conn_id,
                                     p_cb->hid_srvc.proto_mode_handle,
                                     p_cb->hid_srvc.proto_mode_handle,
                                     get_protocol_mode_cb, p_cb);
                                     get_protocol_mode_cb, p_cb);
@@ -1533,6 +1535,7 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
  const gatt::Service* hogp_service = nullptr;
  const gatt::Service* hogp_service = nullptr;
  const gatt::Service* gap_service = nullptr;
  const gatt::Service* gap_service = nullptr;
  const gatt::Service* scp_service = nullptr;
  const gatt::Service* scp_service = nullptr;
  const gatt::Service* headtracker_service = nullptr;


  int num_hid_service = 0;
  int num_hid_service = 0;
  for (const gatt::Service& service : *services) {
  for (const gatt::Service& service : *services) {
@@ -1558,6 +1561,9 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
      scp_service = &service;
      scp_service = &service;
    } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
    } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
      gap_service = &service;
      gap_service = &service;
    } else if (com::android::bluetooth::flags::android_headtracker_service() &&
               service.uuid == ANDROID_HEADTRACKER_SERVICE_UUID) {
      headtracker_service = &service;
    }
    }
  }
  }


@@ -1565,6 +1571,10 @@ static void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) {
    log::verbose("have HOGP service inst_id={}",
    log::verbose("have HOGP service inst_id={}",
                 p_dev_cb->hid_srvc.srvc_inst_id);
                 p_dev_cb->hid_srvc.srvc_inst_id);
    bta_hh_le_parse_hogp_service(p_dev_cb, hogp_service);
    bta_hh_le_parse_hogp_service(p_dev_cb, hogp_service);
  } else if (headtracker_service != nullptr) {
    log::verbose("have Android Headtracker service inst_id={}",
                 p_dev_cb->hid_srvc.srvc_inst_id);
    bta_hh_headtracker_parse_service(p_dev_cb, headtracker_service);
  } else {
  } else {
    log::error("HID service not found");
    log::error("HID service not found");
    p_dev_cb->status = BTA_HH_ERR_SDP;
    p_dev_cb->status = BTA_HH_ERR_SDP;
@@ -1633,8 +1643,9 @@ static void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY* p_data) {
  const gatt::Service* p_svc =
  const gatt::Service* p_svc =
      BTA_GATTC_GetOwningService(p_dev_cb->conn_id, p_char->value_handle);
      BTA_GATTC_GetOwningService(p_dev_cb->conn_id, p_char->value_handle);


  p_rpt = bta_hh_le_find_report_entry(
  p_rpt = bta_hh_le_find_report_entry(p_dev_cb, p_svc->handle,
      p_dev_cb, p_svc->handle, p_char->uuid.As16Bit(), p_char->value_handle);
                                      bta_hh_get_uuid16(p_dev_cb, p_char->uuid),
                                      p_char->value_handle);
  if (p_rpt == NULL) {
  if (p_rpt == NULL) {
    log::error("Unknown Report, uuid:{}, handle:0x{:04x}",
    log::error("Unknown Report, uuid:{}, handle:0x{:04x}",
               p_char->uuid.ToString(), p_char->value_handle);
               p_char->uuid.ToString(), p_char->value_handle);
@@ -1825,7 +1836,8 @@ static void read_report_cb(uint16_t conn_id, tGATT_STATUS status,
    return;
    return;
  }
  }


  uint16_t char_uuid = p_char->uuid.As16Bit();
  uint16_t char_uuid = bta_hh_get_uuid16(p_dev_cb, p_char->uuid);

  switch (char_uuid) {
  switch (char_uuid) {
    case GATT_UUID_HID_REPORT:
    case GATT_UUID_HID_REPORT:
    case GATT_UUID_HID_BT_KB_INPUT:
    case GATT_UUID_HID_BT_KB_INPUT:
@@ -1910,7 +1922,7 @@ static void write_report_cb(uint16_t conn_id, tGATT_STATUS status,


  if (p_char == nullptr) return;
  if (p_char == nullptr) return;


  uint16_t uuid16 = p_char->uuid.As16Bit();
  uint16_t uuid16 = bta_hh_get_uuid16(p_dev_cb, p_char->uuid);
  if (uuid16 != GATT_UUID_HID_REPORT && uuid16 != GATT_UUID_HID_BT_KB_INPUT &&
  if (uuid16 != GATT_UUID_HID_REPORT && uuid16 != GATT_UUID_HID_BT_KB_INPUT &&
      uuid16 != GATT_UUID_HID_BT_MOUSE_INPUT &&
      uuid16 != GATT_UUID_HID_BT_MOUSE_INPUT &&
      uuid16 != GATT_UUID_HID_BT_KB_OUTPUT) {
      uuid16 != GATT_UUID_HID_BT_KB_OUTPUT) {
@@ -1980,6 +1992,11 @@ static void bta_hh_le_write_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type,
 ******************************************************************************/
 ******************************************************************************/
static void bta_hh_le_suspend(tBTA_HH_DEV_CB* p_cb,
static void bta_hh_le_suspend(tBTA_HH_DEV_CB* p_cb,
                              tBTA_HH_TRANS_CTRL_TYPE ctrl_type) {
                              tBTA_HH_TRANS_CTRL_TYPE ctrl_type) {
  if (bta_hh_headtracker_supported(p_cb)) {
    log::warn("Suspend not applicable for headtracker service");
    return;
  }

  ctrl_type -= BTA_HH_CTRL_SUSPEND;
  ctrl_type -= BTA_HH_CTRL_SUSPEND;


  // We don't care about response
  // We don't care about response