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