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 "bta/hh/bta_hh_int.h"
23 #include "btif/include/btif_storage.h"
24 #include "stack/include/bt_types.h"
25 #include "stack/include/bt_uuid16.h"
26 #include "types/bluetooth/uuid.h"
27
28 using bluetooth::Uuid;
29 using namespace bluetooth;
30
bta_hh_headtracker_parse_version_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)31 static bool bta_hh_headtracker_parse_version_charac(
32 tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
33 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
34 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
35 charac.value_handle);
36 if (p_rpt == nullptr) {
37 log::error("Add report entry failed !!!");
38 return false;
39 }
40
41 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 2);
42 return true;
43 }
44
bta_hh_headtracker_prase_control_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)45 static bool bta_hh_headtracker_prase_control_charac(
46 tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
47 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
48 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
49 charac.value_handle);
50 if (p_rpt == nullptr) {
51 log::error("Add report entry failed !!!");
52 return false;
53 }
54
55 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_FEATURE, 1);
56 return true;
57 }
58
bta_hh_headtracker_parse_report_charac(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Characteristic & charac)59 static bool bta_hh_headtracker_parse_report_charac(
60 tBTA_HH_DEV_CB* p_dev_cb, const gatt::Characteristic& charac) {
61 tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_alloc_report_entry(
62 p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT,
63 charac.value_handle);
64 if (p_rpt == nullptr) {
65 log::error("Add report entry failed !!!");
66 return false;
67 }
68
69 bta_hh_le_save_report_ref(p_dev_cb, p_rpt, BTA_HH_RPTT_INPUT, 1);
70 return true;
71 }
72
73 /* Hardcoded Android Headtracker HID descriptor */
74 static const uint8_t ANDROID_HEADTRACKER_DESCRIPTOR[] = {
75 0x05, 0x20, 0x09, 0xe1, 0xa1, 0x01, 0x85, 0x02, 0x0a, 0x08, 0x03, 0x15,
76 0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x19, 0xb1, 0x03, 0x0a, 0x02, 0x03,
77 0x15, 0x00, 0x25, 0xff, 0x75, 0x08, 0x95, 0x10, 0xb1, 0x03, 0x85, 0x01,
78 0x0a, 0x16, 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1,
79 0x02, 0x0a, 0x40, 0x08, 0x0a, 0x41, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x19,
80 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a,
81 0x55, 0x08, 0x0a, 0x51, 0x08, 0xb1, 0x00, 0xc0, 0x0a, 0x0e, 0x03, 0x15,
82 0x00, 0x25, 0x3f, 0x35, 0x0a, 0x45, 0x64, 0x75, 0x06, 0x95, 0x01, 0x66,
83 0x01, 0x10, 0x55, 0x0d, 0xb1, 0x02, 0x0a, 0x10, 0xf4, 0x15, 0x00, 0x25,
84 0x01, 0x75, 0x01, 0x95, 0x01, 0xa1, 0x02, 0x0a, 0x00, 0xf8, 0x0a, 0x01,
85 0xf8, 0xb1, 0x00, 0xc0, 0xb1, 0x02, 0x0a, 0x44, 0x5, 0x16, 0x01, 0x80,
86 0x26, 0xff, 0x7f, 0x37, 0x60, 0x4f, 0x46, 0xed, 0x47, 0xa1, 0xb0, 0xb9,
87 0x12, 0x55, 0x08, 0x75, 0x10, 0x95, 0x03, 0x81, 0x02, 0x0a, 0x45, 0x05,
88 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x35, 0xe0, 0x45, 0x20, 0x55, 0x00,
89 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,
91 0x02, 0xc0};
92
93 /*******************************************************************************
94 *
95 * Function bta_hh_headtracker_parse_service
96 *
97 * Description This function discover all characteristics of the
98 * headtracker service
99 *
100 * Parameters:
101 *
102 ******************************************************************************/
bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB * p_dev_cb,const gatt::Service * service)103 void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb,
104 const gatt::Service* service) {
105 log::info("");
106 bta_hh_le_srvc_init(p_dev_cb, service->handle);
107 p_dev_cb->mode = BTA_HH_PROTO_RPT_MODE;
108 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_AVAILABLE;
109
110 bta_hh_le_save_report_map(p_dev_cb,
111 (uint16_t)sizeof(ANDROID_HEADTRACKER_DESCRIPTOR),
112 (uint8_t*)&ANDROID_HEADTRACKER_DESCRIPTOR);
113
114 bool version_found = false;
115 bool control_found = false;
116 bool data_found = false;
117
118 for (const gatt::Characteristic& charac : service->characteristics) {
119 if (charac.uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID) {
120 version_found = bta_hh_headtracker_parse_version_charac(p_dev_cb, charac);
121 } else if (charac.uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID) {
122 control_found = bta_hh_headtracker_prase_control_charac(p_dev_cb, charac);
123 } else if (charac.uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID) {
124 data_found = bta_hh_headtracker_parse_report_charac(p_dev_cb, charac);
125 } else {
126 log::warn("Unexpected characteristic {}", charac.uuid.ToString());
127 }
128 }
129
130 tGATT_STATUS status = (version_found && control_found && data_found)
131 ? GATT_SUCCESS
132 : GATT_ERROR;
133 bta_hh_le_service_parsed(p_dev_cb, status);
134 }
135
136 /*******************************************************************************
137 *
138 * Function bta_hh_headtracker_supported
139 *
140 * Description Checks if the connection instance is for headtracker
141 *
142 * Parameters:
143 *
144 ******************************************************************************/
bta_hh_headtracker_supported(tBTA_HH_DEV_CB * p_dev_cb)145 bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) {
146 if (!com::android::bluetooth::flags::android_headtracker_service()) {
147 return false;
148 }
149
150 if (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_UNKNOWN) {
151 bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {};
152 bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids),
153 &remote_uuids};
154 const RawAddress& bd_addr = p_dev_cb->link_spec.addrt.bda;
155 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_UNAVAILABLE;
156
157 // Find which services known to be available
158 if (btif_storage_get_remote_device_property(&bd_addr, &remote_properties) ==
159 BT_STATUS_SUCCESS) {
160 int count = remote_properties.len / sizeof(remote_uuids[0]);
161 for (int i = 0; i < count; i++) {
162 if (remote_uuids[i] == ANDROID_HEADTRACKER_SERVICE_UUID) {
163 p_dev_cb->hid_srvc.headtracker_support = BTA_HH_AVAILABLE;
164 break;
165 }
166 }
167 }
168
169 log::verbose("Headtracker support: {}",
170 (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_AVAILABLE));
171 }
172
173 return (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_AVAILABLE);
174 }
175
176 /*******************************************************************************
177 *
178 * Function bta_hh_get_uuid16
179 *
180 * Description Maps Headtracker characteristic UUIDs to HOGP Report UUID
181 *
182 * Parameters:
183 *
184 ******************************************************************************/
bta_hh_get_uuid16(tBTA_HH_DEV_CB * p_dev_cb,Uuid uuid)185 uint16_t bta_hh_get_uuid16(tBTA_HH_DEV_CB* p_dev_cb, Uuid uuid) {
186 if (bta_hh_headtracker_supported(p_dev_cb) &&
187 (uuid == ANDROID_HEADTRACKER_VERSION_CHARAC_UUID ||
188 uuid == ANDROID_HEADTRACKER_CONTROL_CHARAC_UUID ||
189 uuid == ANDROID_HEADTRACKER_REPORT_CHARAC_UUID)) {
190 return GATT_UUID_HID_REPORT;
191 } else if (!uuid.Is16Bit()) {
192 log::warn("UUID is not 16 bit");
193 return 0;
194 } else {
195 return uuid.As16Bit();
196 }
197 }
198