1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "has_ctp.h"
19 
20 #include <bluetooth/log.h>
21 
22 #include <cstdint>
23 #include <cstring>
24 #include <optional>
25 #include <ostream>
26 #include <type_traits>
27 #include <variant>
28 #include <vector>
29 
30 #include "has_preset.h"
31 #include "os/logging/log_adapter.h"
32 #include "stack/include/bt_types.h"
33 #include "types/raw_address.h"
34 
35 using namespace bluetooth;
36 
37 namespace bluetooth::le_audio {
38 namespace has {
39 
ParsePresetGenericUpdate(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)40 static bool ParsePresetGenericUpdate(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
41   if (len < sizeof(ntf.prev_index) + HasPreset::kCharValueMinSize) {
42     log::error("Invalid preset value length={} for generic update.", len);
43     return false;
44   }
45 
46   STREAM_TO_UINT8(ntf.index, value);
47   len -= 1;
48 
49   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
50   return true;
51 }
52 
ParsePresetIndex(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)53 static bool ParsePresetIndex(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
54   if (len < sizeof(ntf.index)) {
55     log::error("Invalid preset value length={} for generic update.", len);
56     return false;
57   }
58 
59   STREAM_TO_UINT8(ntf.index, value);
60   len -= 1;
61   return true;
62 }
63 
ParsePresetReadResponse(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)64 static bool ParsePresetReadResponse(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
65   if (len < sizeof(ntf.is_last) + HasPreset::kCharValueMinSize) {
66     log::error("Invalid preset value length={}", len);
67     return false;
68   }
69 
70   STREAM_TO_UINT8(ntf.is_last, value);
71   len -= 1;
72 
73   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
74   return true;
75 }
76 
ParsePresetChanged(uint16_t len,const uint8_t * value,HasCtpNtf & ntf)77 static bool ParsePresetChanged(uint16_t len, const uint8_t* value, HasCtpNtf& ntf) {
78   if (len < sizeof(ntf.is_last) + sizeof(ntf.change_id)) {
79     log::error("Invalid preset value length={}", len);
80     return false;
81   }
82 
83   uint8_t change_id;
84   STREAM_TO_UINT8(change_id, value);
85   len -= 1;
86   if (change_id >
87       static_cast<std::underlying_type_t<PresetCtpChangeId>>(PresetCtpChangeId::CHANGE_ID_MAX_)) {
88     log::error("Invalid preset chenge_id={}", change_id);
89     return false;
90   }
91   ntf.change_id = PresetCtpChangeId(change_id);
92   STREAM_TO_UINT8(ntf.is_last, value);
93   len -= 1;
94 
95   switch (ntf.change_id) {
96     case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
97       return ParsePresetGenericUpdate(len, value, ntf);
98     case PresetCtpChangeId::PRESET_AVAILABLE:
99       return ParsePresetIndex(len, value, ntf);
100     case PresetCtpChangeId::PRESET_UNAVAILABLE:
101       return ParsePresetIndex(len, value, ntf);
102     case PresetCtpChangeId::PRESET_DELETED:
103       return ParsePresetIndex(len, value, ntf);
104     default:
105       return false;
106   }
107 
108   return true;
109 }
110 
FromCharacteristicValue(uint16_t len,const uint8_t * value)111 std::optional<HasCtpNtf> HasCtpNtf::FromCharacteristicValue(uint16_t len, const uint8_t* value) {
112   if (len < 3) {
113     log::error("Invalid Cp notification.");
114     return std::nullopt;
115   }
116 
117   uint8_t op;
118   STREAM_TO_UINT8(op, value);
119   --len;
120 
121   if ((op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
122                      PresetCtpOpcode::READ_PRESET_RESPONSE)) &&
123       (op !=
124        static_cast<std::underlying_type_t<PresetCtpOpcode>>(PresetCtpOpcode::PRESET_CHANGED))) {
125     log::error("Received invalid opcode in control point notification: {}", op);
126     return std::nullopt;
127   }
128 
129   HasCtpNtf ntf;
130   ntf.opcode = PresetCtpOpcode(op);
131   if (ntf.opcode == bluetooth::le_audio::has::PresetCtpOpcode::PRESET_CHANGED) {
132     if (!ParsePresetChanged(len, value, ntf)) {
133       return std::nullopt;
134     }
135 
136   } else if (ntf.opcode == bluetooth::le_audio::has::PresetCtpOpcode::READ_PRESET_RESPONSE) {
137     if (!ParsePresetReadResponse(len, value, ntf)) {
138       return std::nullopt;
139     }
140   }
141 
142   return ntf;
143 }
144 
145 uint16_t HasCtpOp::last_op_id_ = 0;
146 
ToCharacteristicValue() const147 std::vector<uint8_t> HasCtpOp::ToCharacteristicValue() const {
148   std::vector<uint8_t> value;
149   auto* pp = value.data();
150 
151   switch (opcode) {
152     case PresetCtpOpcode::READ_PRESETS:
153       value.resize(3);
154       pp = value.data();
155       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
156       UINT8_TO_STREAM(pp, index);
157       UINT8_TO_STREAM(pp, num_of_indices);
158       break;
159     case PresetCtpOpcode::SET_ACTIVE_PRESET:
160     case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
161       value.resize(2);
162       pp = value.data();
163       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
164       UINT8_TO_STREAM(pp, index);
165       break;
166 
167     case PresetCtpOpcode::SET_NEXT_PRESET:
168     case PresetCtpOpcode::SET_NEXT_PRESET_SYNC:
169     case PresetCtpOpcode::SET_PREV_PRESET:
170     case PresetCtpOpcode::SET_PREV_PRESET_SYNC:
171       value.resize(1);
172       pp = value.data();
173       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
174       break;
175 
176     case PresetCtpOpcode::WRITE_PRESET_NAME: {
177       auto name_str = name.value_or("");
178       value.resize(2 + name_str.length());
179       pp = value.data();
180 
181       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
182       UINT8_TO_STREAM(pp, index);
183       memcpy(pp, name_str.c_str(), name_str.length());
184     } break;
185 
186     default:
187       log::fatal("Bad control point operation!");
188       break;
189   }
190 
191   return value;
192 }
193 
194 #define CASE_SET_PTR_TO_TOKEN_STR(en) \
195   case (en):                          \
196     ch = #en;                         \
197     break;
198 
operator <<(std::ostream & out,const PresetCtpChangeId value)199 std::ostream& operator<<(std::ostream& out, const PresetCtpChangeId value) {
200   const char* ch = 0;
201   switch (value) {
202     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_GENERIC_UPDATE);
203     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_DELETED);
204     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_AVAILABLE);
205     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_UNAVAILABLE);
206     default:
207       ch = "INVALID_CHANGE_ID";
208       break;
209   }
210   return out << ch;
211 }
212 
operator <<(std::ostream & out,const PresetCtpOpcode value)213 std::ostream& operator<<(std::ostream& out, const PresetCtpOpcode value) {
214   const char* ch = 0;
215   switch (value) {
216     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESETS);
217     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESET_RESPONSE);
218     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::PRESET_CHANGED);
219     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::WRITE_PRESET_NAME);
220     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET);
221     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET);
222     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET);
223     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC);
224     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET_SYNC);
225     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET_SYNC);
226     default:
227       ch = "NOT_A_VALID_OPCODE";
228       break;
229   }
230   return out << ch;
231 }
232 #undef SET_CH_TO_TOKENIZED
233 
operator <<(std::ostream & out,const HasCtpOp & op)234 std::ostream& operator<<(std::ostream& out, const HasCtpOp& op) {
235   out << "\"HasCtpOp\": {";
236   if (std::holds_alternative<int>(op.addr_or_group)) {
237     out << "\"group_id\": " << std::get<int>(op.addr_or_group);
238   } else if (std::holds_alternative<RawAddress>(op.addr_or_group)) {
239     out << "\"address\": \"" << ADDRESS_TO_LOGGABLE_STR(std::get<RawAddress>(op.addr_or_group))
240         << "\"";
241   } else {
242     out << "\"bad value\"";
243   }
244   out << ", \"id\": " << op.op_id << ", \"opcode\": \"" << op.opcode << "\""
245       << ", \"index\": " << +op.index << ", \"name\": \"" << op.name.value_or("<none>") << "\""
246       << "}";
247   return out;
248 }
249 
operator <<(std::ostream & out,const HasCtpNtf & ntf)250 std::ostream& operator<<(std::ostream& out, const HasCtpNtf& ntf) {
251   out << "\"HasCtpNtf\": {";
252   out << "\"opcode\": \"" << ntf.opcode << "\"";
253 
254   if (ntf.opcode == PresetCtpOpcode::READ_PRESET_RESPONSE) {
255     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
256     if (ntf.preset.has_value()) {
257       out << ", \"preset\": " << ntf.preset.value();
258     } else {
259       out << ", \"preset\": \"None\"";
260     }
261 
262   } else if (ntf.opcode == PresetCtpOpcode::PRESET_CHANGED) {
263     out << ", \"change_id\": " << ntf.change_id;
264     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
265     switch (ntf.change_id) {
266       case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
267         out << ", \"prev_index\": " << +ntf.prev_index;
268         if (ntf.preset.has_value()) {
269           out << ", \"preset\": {" << ntf.preset.value() << "}";
270         } else {
271           out << ", \"preset\": \"None\"";
272         }
273         break;
274       case PresetCtpChangeId::PRESET_DELETED:
275       case PresetCtpChangeId::PRESET_AVAILABLE:
276       case PresetCtpChangeId::PRESET_UNAVAILABLE:
277         out << ", \"index\": " << +ntf.index;
278         break;
279       default:
280         break;
281     }
282   }
283   out << "}";
284 
285   return out;
286 }
287 
288 }  // namespace has
289 }  // namespace bluetooth::le_audio
290