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