1 /*
2  * Copyright 2017 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 "gatt"
18 
19 #include <bluetooth/log.h>
20 
21 #include <list>
22 #include <unordered_map>
23 #include <unordered_set>
24 
25 #include "bta_gatt_server_queue.h"
26 
27 using gatts_operation = BtaGattServerQueue::gatts_operation;
28 using bluetooth::Uuid;
29 using namespace bluetooth;
30 
31 constexpr uint8_t GATT_NOTIFY = 1;
32 
33 std::unordered_map<tCONN_ID, std::list<gatts_operation>> BtaGattServerQueue::gatts_op_queue;
34 std::unordered_set<tCONN_ID> BtaGattServerQueue::gatts_op_queue_executing;
35 std::unordered_map<tCONN_ID, bool> BtaGattServerQueue::congestion_queue;
36 
mark_as_not_executing(tCONN_ID conn_id)37 void BtaGattServerQueue::mark_as_not_executing(tCONN_ID conn_id) {
38   gatts_op_queue_executing.erase(conn_id);
39 }
40 
gatts_execute_next_op(tCONN_ID conn_id)41 void BtaGattServerQueue::gatts_execute_next_op(tCONN_ID conn_id) {
42   log::verbose("conn_id=0x{:x}", conn_id);
43 
44   if (gatts_op_queue.empty()) {
45     log::verbose("op queue is empty");
46     return;
47   }
48 
49   auto ptr = congestion_queue.find(conn_id);
50 
51   if (ptr != congestion_queue.end()) {
52     bool is_congested = ptr->second;
53     log::verbose("congestion queue exist, conn_id: {}, is_congested: {}", conn_id, is_congested);
54     if (is_congested) {
55       log::verbose("lower layer is congested");
56       return;
57     }
58   }
59 
60   auto map_ptr = gatts_op_queue.find(conn_id);
61 
62   if (map_ptr == gatts_op_queue.end()) {
63     log::verbose("Queue is null");
64     return;
65   }
66 
67   if (map_ptr->second.empty()) {
68     log::verbose("queue is empty for conn_id: {}", conn_id);
69     return;
70   }
71 
72   if (gatts_op_queue_executing.count(conn_id)) {
73     log::verbose("can't enqueue next op, already executing");
74     return;
75   }
76 
77   gatts_operation op = map_ptr->second.front();
78   log::verbose("op.type={}, attr_id={}", op.type, op.attr_id);
79 
80   if (op.type == GATT_NOTIFY) {
81     BTA_GATTS_HandleValueIndication(conn_id, op.attr_id, op.value, op.need_confirm);
82     gatts_op_queue_executing.insert(conn_id);
83   }
84 }
85 
Clean(tCONN_ID conn_id)86 void BtaGattServerQueue::Clean(tCONN_ID conn_id) {
87   log::verbose("conn_id=0x{:x}", conn_id);
88 
89   gatts_op_queue.erase(conn_id);
90   gatts_op_queue_executing.erase(conn_id);
91 }
92 
SendNotification(tCONN_ID conn_id,uint16_t handle,std::vector<uint8_t> value,bool need_confirm)93 void BtaGattServerQueue::SendNotification(tCONN_ID conn_id, uint16_t handle,
94                                           std::vector<uint8_t> value, bool need_confirm) {
95   gatts_op_queue[conn_id].emplace_back(gatts_operation{
96           .type = GATT_NOTIFY, .attr_id = handle, .value = value, .need_confirm = need_confirm});
97   gatts_execute_next_op(conn_id);
98 }
99 
NotificationCallback(tCONN_ID conn_id)100 void BtaGattServerQueue::NotificationCallback(tCONN_ID conn_id) {
101   auto map_ptr = gatts_op_queue.find(conn_id);
102   if (map_ptr == gatts_op_queue.end() || map_ptr->second.empty()) {
103     log::verbose("no more operations queued for conn_id {}", conn_id);
104     return;
105   }
106 
107   gatts_operation op = map_ptr->second.front();
108   map_ptr->second.pop_front();
109   mark_as_not_executing(conn_id);
110   gatts_execute_next_op(conn_id);
111 }
112 
CongestionCallback(tCONN_ID conn_id,bool congested)113 void BtaGattServerQueue::CongestionCallback(tCONN_ID conn_id, bool congested) {
114   log::verbose("conn_id: {}, congested: {}", conn_id, congested);
115 
116   congestion_queue[conn_id] = congested;
117   if (!congested) {
118     gatts_execute_next_op(conn_id);
119   }
120 }
121