1 /*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "bta_hh_headtracker"
18
19 #include <bluetooth/log.h>
20 #include <com_android_bluetooth_flags.h>
21
22 #include <cstdint>
23
24 #include "bta/hh/bta_hh_int.h"
25 #include "bta_hh_api.h"
26 #include "btif/include/btif_storage.h"
27 #include "gatt/database.h"
28 #include "gatt_api.h"
29 #include "gattdefs.h"
30 #include "hardware/bluetooth.h"
31 #include "types/bluetooth/uuid.h"
32 #include "types/raw_address.h"
33
34 using bluetooth::Uuid;
35 using namespace bluetooth;
36
bta_hh_headtracker_parse_version_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)37 static bool bta_hh_headtracker_parse_version_charac(tBTA_HH_DEV_CB* p_dev_cb,
38 const gatt::Characteristic& charac) {
39 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
40 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT, charac.value_handle);
41 if (p_rpt == nullptr) {
42 log::error("Add report entry failed !!!");
43 return false;
44 }
45
46 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 2);
47 return true;
48 }
49
bta_hh_headtracker_prase_control_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)50 static bool bta_hh_headtracker_prase_control_charac(tBTA_HH_DEV_CB* p_dev_cb,
51 const gatt::Characteristic& charac) {
52 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
53 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT, charac.value_handle);
54 if (p_rpt == nullptr) {
55 log::error("Add report entry failed !!!");
56 return false;
57 }
58
59 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 1);
60 return true;
61 }
62
bta_hh_headtracker_parse_report_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)63 static bool bta_hh_headtracker_parse_report_charac(tBTA_HH_DEV_CB* p_dev_cb,
64 const gatt::Characteristic& charac) {
65 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
66 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT, charac.value_handle);
67 if (p_rpt == nullptr) {
68 log::error("Add report entry failed !!!");
69 return false;
70 }
71
72 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_INPUT, 1);
73 return true;
74 }
75
76 /* Hardcoded Android Headtracker HID descriptor */
77 static const uint8_t ANDROID_HEADTRACKER_DESCRIPTOR[] = {
78 0x05, 0x20, 0x09, 0xe1, 0xa1, 0x01, 0x85, 0x02, 0x0a, 0x08, 0x03, 0x15, 0x00, 0x25, 0xff,
79 0x75, 0x08, 0x95, 0x19, 0xb1, 0x03, 0x0a, 0x02, 0x03, 0x15, 0x00, 0x25, 0xff, 0x75, 0x08,
80 0x95, 0x10, 0xb1, 0x03, 0x85, 0x01, 0x0a, 0x16, 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
81 0x95, 0x01, 0xa1, 0x02, 0x0a, 0x40, 0x08, 0x0a, 0x41, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x19,
82 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a, 0x55, 0x08, 0x0a,
83 0x51, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x0e, 0x03, 0x15, 0x00, 0x25, 0x3f, 0x35, 0x0a, 0x45,
84 0x64, 0x75, 0x06, 0x95, 0x01, 0x66, 0x01, 0x10, 0x55, 0x0d, 0xb1, 0x02, 0x0a, 0x10, 0xf4,
85 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a, 0x00, 0xf8, 0x0a, 0x01,
86 0xf8, 0xb1, 0x00, 0xc0, 0xb1, 0x02, 0x0a, 0x44, 0x5, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f,
87 0x37, 0x60, 0x4f, 0x46, 0xed, 0x47, 0xa1, 0xb0, 0xb9, 0x12, 0x55, 0x08, 0x75, 0x10, 0x95,
88 0x03, 0x81, 0x02, 0x0a, 0x45, 0x05, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x35, 0xe0, 0x45,
89 0x20, 0x55, 0x00, 0x75, 0x10, 0x95, 0x03, 0x81, 0x02, 0x0a, 0x46, 0x05, 0x15, 0x00, 0x25,
90 0xff, 0x35, 0x00, 0x45, 0xff, 0x55, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xc0};
91
92 /*******************************************************************************
93 *
94 * Function bta_hh_headtracker_parse_service
95 *
96 * Description This function discover all characteristics of the
97 * headtracker service
98 *
99 * Parameters:
100 *
101 ******************************************************************************/
bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Service * service)102 void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb, const gatt::Service* service) {
103 log::info("");
104 bta_hh_le_srvc_init(p_dev_cb, service->handle);
105 p_dev_cb->mode = BTA_HH_PROTO_RPT_MODE;
106 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_AVAILABLE;
107
108 bta_hh_le_save_report_map(p_dev_cb, (uint16_t)sizeof(ANDROID_HEADTRACKER_DESCRIPTOR),
109 (uint8_t*)&ANDROID_HEADTRACKER_DESCRIPTOR);
110
111 bool version_found = false;
112 bool control_found = false;
113 bool data_found = false;
114
115 for (const gatt::Characteristic& charac : service->characteristics) {
116 if (charac.uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID) {
117 version_found = bta_hh_headtracker_parse_version_charac(p_dev_cb, charac);
118 } else if (charac.uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID) {
119 control_found = bta_hh_headtracker_prase_control_charac(p_dev_cb, charac);
120 } else if (charac.uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID) {
121 data_found = bta_hh_headtracker_parse_report_charac(p_dev_cb, charac);
122 } else {
123 log::warn("Unexpected characteristic {}", charac.uuid.ToString());
124 }
125 }
126
127 tGATT_STATUS status = (version_found && control_found && data_found) ? GATT_SUCCESS : GATT_ERROR;
128 bta_hh_le_service_parsed(p_dev_cb, status);
129 }
130
131 /*******************************************************************************
132 *
133 * Function bta_hh_headtracker_supported
134 *
135 * Description Checks if the connection instance is for headtracker
136 *
137 * Parameters:
138 *
139 ******************************************************************************/
bta_hh_headtracker_supported(tBTA_HH_DEV_CB * p_dev_cb)140 bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) {
141 if (!com::android::bluetooth::flags::android_headtracker_service()) {
142 return false;
143 }
144
145 if (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_UNKNOWN) {
146 bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {};
147 bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids), &remote_uuids};
148 const RawAddress& bd_addr = p_dev_cb->link_spec.addrt.bda;
149 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_UNAVAILABLE;
150
151 // Find which services known to be available
152 if (btif_storage_get_remote_device_property(&bd_addr, &remote_properties) ==
153 BT_STATUS_SUCCESS) {
154 int count = remote_properties.len / sizeof(remote_uuids[0]);
155 for (int i = 0; i < count; i++) {
156 if (remote_uuids[i] == ANDROID_HEADTRACKER_SERVICE_UUID) {
157 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_AVAILABLE;
158 break;
159 }
160 }
161 }
162
163 log::verbose("Headtracker support: {}",
164 (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_AVAILABLE));
165 }
166
167 return p_dev_cb->hid_srvc.headtracker_support == BTA_HH_AVAILABLE;
168 }
169
170 /*******************************************************************************
171 *
172 * Function bta_hh_get_uuid16
173 *
174 * Description Maps Headtracker characteristic UUIDs to HOGP Report UUID
175 *
176 * Parameters:
177 *
178 ******************************************************************************/
bta_hh_get_uuid16(tBTA_HH_DEV_CB * p_dev_cb,Uuid uuid)179 uint16_t bta_hh_get_uuid16(tBTA_HH_DEV_CB* p_dev_cb, Uuid uuid) {
180 if (bta_hh_headtracker_supported(p_dev_cb) && (uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID ||
181 uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID ||
182 uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID)) {
183 return GATT_UUID_HID_REPORT;
184 } else if (!uuid.Is16Bit()) {
185 log::warn("UUID is not 16 bit");
186 return 0;
187 } else {
188 return uuid.As16Bit();
189 }
190 }
191