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