1 /*
2 * Copyright (C) 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 #include "bta/le_audio/gmap_server.h"
18
19 #include <base/functional/bind.h>
20 #include <base/functional/callback.h>
21 #include <base/strings/string_number_conversions.h>
22 #include <bluetooth/log.h>
23 #include <com_android_bluetooth_flags.h>
24 #include <stdio.h>
25
26 #include <bitset>
27 #include <cstdint>
28 #include <sstream>
29 #include <unordered_map>
30 #include <vector>
31
32 #include "bta/le_audio/le_audio_types.h"
33 #include "bta_gatt_api.h"
34 #include "bta_gatt_queue.h"
35 #include "gatt_api.h"
36 #include "hardware/bt_common_types.h"
37 #include "include/hardware/bt_gmap.h"
38 #include "osi/include/properties.h"
39 #include "types/bluetooth/uuid.h"
40 #include "types/bt_transport.h"
41
42 using bluetooth::Uuid;
43 using namespace bluetooth;
44 using bluetooth::le_audio::GmapCharacteristic;
45 using bluetooth::le_audio::GmapServer;
46
47 bool GmapServer::is_offloader_support_gmap_ = false;
48 uint16_t GmapServer::server_if_ = 0;
49 std::unordered_map<uint16_t, GmapCharacteristic> GmapServer::characteristics_ =
50 std::unordered_map<uint16_t, GmapCharacteristic>();
51 // default role is UGG
52 std::bitset<8> GmapServer::role_ = 0b0001;
53 // AOSP's LE Audio source support multi-sink on default
54 std::bitset<8> GmapServer::UGG_feature_ =
55 static_cast<uint8_t>(bluetooth::gmap::UGGFeatureBitMask::MultisinkFeatureSupport);
56
IsGmapServerEnabled()57 bool GmapServer::IsGmapServerEnabled() {
58 // for UGG, both GMAP Server and Client are needed. So server and client share the same flag.
59 bool flag = com::android::bluetooth::flags::leaudio_gmap_client();
60 bool system_prop = osi_property_get_bool("bluetooth.profile.gmap.enabled", false);
61
62 bool result = flag && system_prop && is_offloader_support_gmap_;
63 log::info("GmapServerEnabled={}, flag={}, system_prop={}, offloader_support={}", result,
64 system_prop, flag, GmapServer::is_offloader_support_gmap_);
65 return result;
66 }
67
UpdateGmapOffloaderSupport(bool value)68 void GmapServer::UpdateGmapOffloaderSupport(bool value) {
69 GmapServer::is_offloader_support_gmap_ = value;
70 }
71
DebugDump(int fd)72 void GmapServer::DebugDump(int fd) {
73 std::stringstream stream;
74 stream << "GmapServer is enabled: " << IsGmapServerEnabled() << "\n";
75 if (IsGmapServerEnabled()) {
76 stream << "GmapServer Role: " << role_ << ", UGG Feature: " << UGG_feature_ << "\n";
77 }
78
79 dprintf(fd, "%s", stream.str().c_str());
80 }
81
Initialize(std::bitset<8> role,std::bitset<8> UGG_feature)82 void GmapServer::Initialize(std::bitset<8> role, std::bitset<8> UGG_feature) {
83 GmapServer::role_ = role;
84 GmapServer::Initialize(UGG_feature);
85 }
86
Initialize(std::bitset<8> UGG_feature)87 void GmapServer::Initialize(std::bitset<8> UGG_feature) {
88 GmapServer::UGG_feature_ = UGG_feature;
89 log::info("GmapServer initialized, role={}, UGG_feature={}", GmapServer::role_.to_string(),
90 UGG_feature.to_string());
91 characteristics_.clear();
92
93 BTA_GATTS_AppRegister(
94 bluetooth::le_audio::uuid::kGamingAudioServiceUuid,
95 [](tBTA_GATTS_EVT event, tBTA_GATTS *p_data) {
96 if (p_data) {
97 GmapServer::GattsCallback(event, p_data);
98 }
99 },
100 false);
101 }
102
GetRole()103 std::bitset<8> GmapServer::GetRole() { return GmapServer::role_; }
104
GetRoleHandle()105 uint16_t GmapServer::GetRoleHandle() {
106 for (auto &[attribute_handle, characteristic] : characteristics_) {
107 if (characteristic.uuid_ == bluetooth::le_audio::uuid::kRoleCharacteristicUuid) {
108 return attribute_handle;
109 }
110 }
111 log::warn("no valid UGG feature handle");
112 return 0;
113 }
114
GetUGGFeature()115 std::bitset<8> GmapServer::GetUGGFeature() { return GmapServer::UGG_feature_; }
116
GetUGGFeatureHandle()117 uint16_t GmapServer::GetUGGFeatureHandle() {
118 for (auto &[attribute_handle, characteristic] : characteristics_) {
119 if (characteristic.uuid_ == bluetooth::le_audio::uuid::kUnicastGameGatewayCharacteristicUuid) {
120 return attribute_handle;
121 }
122 }
123 log::warn("no valid UGG feature handle");
124 return 0;
125 }
126
GetCharacteristics()127 std::unordered_map<uint16_t, GmapCharacteristic> &GmapServer::GetCharacteristics() {
128 return GmapServer::characteristics_;
129 }
130
GattsCallback(tBTA_GATTS_EVT event,tBTA_GATTS * p_data)131 void GmapServer::GattsCallback(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) {
132 log::info("event: {}", gatt_server_event_text(event));
133 switch (event) {
134 case BTA_GATTS_CONNECT_EVT: {
135 OnGattConnect(p_data);
136 break;
137 }
138 case BTA_GATTS_DEREG_EVT: {
139 BTA_GATTS_AppDeregister(server_if_);
140 break;
141 }
142 case BTA_GATTS_DISCONNECT_EVT: {
143 OnGattDisconnect(p_data);
144 break;
145 }
146 case BTA_GATTS_REG_EVT: {
147 OnGattServerRegister(p_data);
148 break;
149 }
150 case BTA_GATTS_READ_CHARACTERISTIC_EVT: {
151 OnReadCharacteristic(p_data);
152 break;
153 }
154 default:
155 log::warn("Unhandled event {}", gatt_server_event_text(event));
156 }
157 }
158
OnGattConnect(tBTA_GATTS * p_data)159 void GmapServer::OnGattConnect(tBTA_GATTS *p_data) {
160 if (p_data == nullptr) {
161 log::warn("invalid p_data");
162 }
163 auto address = p_data->conn.remote_bda;
164 log::info("Address: {}, conn_id:{}", address, p_data->conn.conn_id);
165 if (p_data->conn.transport == BT_TRANSPORT_BR_EDR) {
166 log::warn("Skip BE/EDR connection");
167 return;
168 }
169 }
170
OnGattDisconnect(tBTA_GATTS * p_data)171 void GmapServer::OnGattDisconnect(tBTA_GATTS *p_data) {
172 if (p_data == nullptr) {
173 log::warn("invalid p_data");
174 }
175 auto address = p_data->conn.remote_bda;
176 log::info("Address: {}, conn_id:{}", address, p_data->conn.conn_id);
177 }
178
OnGattServerRegister(tBTA_GATTS * p_data)179 void GmapServer::OnGattServerRegister(tBTA_GATTS *p_data) {
180 if (p_data == nullptr) {
181 log::warn("invalid p_data");
182 }
183 tGATT_STATUS status = p_data->reg_oper.status;
184 log::info("status: {}", gatt_status_text(p_data->reg_oper.status));
185
186 if (status != tGATT_STATUS::GATT_SUCCESS) {
187 log::warn("Register Server fail");
188 return;
189 }
190 server_if_ = p_data->reg_oper.server_if;
191
192 std::vector<btgatt_db_element_t> service;
193
194 // GMAP service
195 btgatt_db_element_t gmap_service;
196 gmap_service.uuid = bluetooth::le_audio::uuid::kGamingAudioServiceUuid;
197 gmap_service.type = BTGATT_DB_PRIMARY_SERVICE;
198 service.push_back(gmap_service);
199
200 // GMAP role
201 btgatt_db_element_t role_characteristic;
202 role_characteristic.uuid = bluetooth::le_audio::uuid::kRoleCharacteristicUuid;
203 role_characteristic.type = BTGATT_DB_CHARACTERISTIC;
204 role_characteristic.properties = GATT_CHAR_PROP_BIT_READ;
205 role_characteristic.permissions = GATT_PERM_READ;
206 service.push_back(role_characteristic);
207
208 // GMAP UGG feature
209 btgatt_db_element_t UGG_feature_characteristic;
210 UGG_feature_characteristic.uuid =
211 bluetooth::le_audio::uuid::kUnicastGameGatewayCharacteristicUuid;
212 UGG_feature_characteristic.type = BTGATT_DB_CHARACTERISTIC;
213 UGG_feature_characteristic.properties = GATT_CHAR_PROP_BIT_READ;
214 UGG_feature_characteristic.permissions = GATT_PERM_READ;
215 service.push_back(UGG_feature_characteristic);
216
217 log::info("add service");
218 BTA_GATTS_AddService(server_if_, service,
219 base::BindRepeating([](tGATT_STATUS status, int server_if,
220 std::vector<btgatt_db_element_t> service) {
221 OnServiceAdded(status, server_if, service);
222 }));
223 }
224
OnServiceAdded(tGATT_STATUS status,int server_if,std::vector<btgatt_db_element_t> services)225 void GmapServer::OnServiceAdded(tGATT_STATUS status, int server_if,
226 std::vector<btgatt_db_element_t> services) {
227 log::info("status: {}, server_if: {}", gatt_status_text(status), server_if);
228 for (const auto &service : services) {
229 uint16_t attribute_handle = service.attribute_handle;
230 Uuid uuid = service.uuid;
231 if (service.type == BTGATT_DB_CHARACTERISTIC) {
232 log::info("Characteristic uuid: 0x{:04x}, handle:0x{:04x}", uuid.As16Bit(), attribute_handle);
233 GmapCharacteristic characteristic{.uuid_ = uuid, .attribute_handle_ = attribute_handle};
234 characteristics_[attribute_handle] = characteristic;
235 }
236 }
237 }
238
OnReadCharacteristic(tBTA_GATTS * p_data)239 void GmapServer::OnReadCharacteristic(tBTA_GATTS *p_data) {
240 uint16_t read_req_handle = p_data->req_data.p_data->read_req.handle;
241 log::info("read_req_handle: 0x{:04x},", read_req_handle);
242
243 tGATTS_RSP p_msg;
244 p_msg.attr_value.handle = read_req_handle;
245 auto it = characteristics_.find(read_req_handle);
246 if (it == characteristics_.end()) {
247 log::error("Invalid handle 0x{:04x}", read_req_handle);
248 BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, GATT_INVALID_HANDLE,
249 &p_msg);
250 return;
251 }
252
253 auto uuid = it->second.uuid_;
254
255 log::info("Read uuid, 0x{:04x}", uuid.As16Bit());
256 // Check Characteristic UUID
257 if (bluetooth::le_audio::uuid::kRoleCharacteristicUuid == uuid) {
258 p_msg.attr_value.len = GmapServer::kGmapRoleLen;
259 auto role = GmapServer::GetRole();
260 p_msg.attr_value.value[0] = static_cast<uint8_t>(role.to_ulong());
261 } else if (bluetooth::le_audio::uuid::kUnicastGameGatewayCharacteristicUuid == uuid) {
262 p_msg.attr_value.len = GmapServer::kGmapUGGFeatureLen;
263 auto UGGFeature = GmapServer::GetUGGFeature();
264 p_msg.attr_value.value[0] = static_cast<uint8_t>(UGGFeature.to_ulong());
265 } else {
266 log::warn("Unhandled uuid {}", uuid.ToString());
267 BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, GATT_ILLEGAL_PARAMETER,
268 &p_msg);
269 return;
270 }
271
272 BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, GATT_SUCCESS, &p_msg);
273 }
274