xref: /btstack/src/classic/avrcp_browsing_target.c (revision 06de70210fb92b5590bfcf6c42ad2e406ae30b88)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "avrcp_browsing_target.c"
39 
40 #include <stdint.h>
41 #include <string.h>
42 #include <inttypes.h>
43 #include "classic/avrcp.h"
44 #include "classic/avrcp_browsing.h"
45 #include "classic/avrcp_browsing_target.h"
46 #include "classic/avrcp_target.h"
47 #include "classic/avrcp_controller.h"
48 
49 #include "bluetooth_sdp.h"
50 #include "btstack_debug.h"
51 #include "btstack_event.h"
52 
53 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
54 
55 static int avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection){
56     int pos = 0;
57 
58     // l2cap_reserve_packet_buffer();
59     // uint8_t * packet = l2cap_get_outgoing_buffer();
60     uint8_t packet[400];
61     connection->packet_type = AVRCP_SINGLE_PACKET;
62 
63     packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
64     // Profile IDentifier (PID)
65     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
66     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
67     (void)memcpy(packet + pos, connection->cmd_operands,
68                  connection->cmd_operands_length);
69 
70     pos += connection->cmd_operands_length;
71     connection->wait_to_send = false;
72     // return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
73     return l2cap_send(connection->l2cap_browsing_cid, packet, pos);
74 }
75 
76 
77 static uint8_t avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
78     // AVRCP_CTYPE_RESPONSE_REJECTED
79     int pos = 0;
80     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
81     // connection->message_body[pos++] = 0;
82     // param length
83     big_endian_store_16(connection->cmd_operands, pos, 1);
84     pos += 2;
85     connection->cmd_operands[pos++] = status;
86     connection->cmd_operands_length = 4;
87     connection->state = AVCTP_W2_SEND_RESPONSE;
88     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
89     return ERROR_CODE_SUCCESS;
90 }
91 
92 static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
93     btstack_assert(callback != NULL);
94 
95     uint8_t event[18];
96     int pos = 0;
97     event[pos++] = HCI_EVENT_AVRCP_META;
98     event[pos++] = sizeof(event) - 2;
99     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
100     little_endian_store_16(event, pos, browsing_cid);
101     pos += 2;
102     event[pos++] = connection->scope;
103     little_endian_store_32(event, pos, connection->start_item);
104     pos += 4;
105     little_endian_store_32(event, pos, connection->end_item);
106     pos += 4;
107     little_endian_store_32(event, pos, connection->attr_bitmap);
108     pos += 4;
109     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
110 }
111 
112 static void avrcp_browsing_target_emit_search(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
113     btstack_assert(callback != NULL);
114 
115     uint8_t event[11 + AVRCP_SEARCH_STRING_MAX_LENGTH];
116     int pos = 0;
117     event[pos++] = HCI_EVENT_AVRCP_META;
118     event[pos++] = sizeof(event) - 2;
119     event[pos++] = AVRCP_SUBEVENT_BROWSING_SEARCH;
120     little_endian_store_16(event, pos, browsing_cid);
121     pos += 2;
122     little_endian_store_16(event, pos, connection->target_search_characterset);
123     pos += 2;
124     little_endian_store_16(event, pos, connection->target_search_str_len);
125     pos += 2;
126     uint16_t target_search_str_len = btstack_min(AVRCP_SEARCH_STRING_MAX_LENGTH, strlen(connection->target_search_str));
127     little_endian_store_16(event, pos, target_search_str_len);
128     pos += 2;
129     if (target_search_str_len > 0){
130         memcpy(&event[pos], connection->target_search_str, target_search_str_len);
131         connection->target_search_str[target_search_str_len - 1] = 0;
132         pos += target_search_str_len;
133     }
134     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
135 }
136 
137 static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
138     btstack_assert(callback != NULL);
139 
140     uint8_t event[10];
141     int pos = 0;
142     event[pos++] = HCI_EVENT_AVRCP_META;
143     event[pos++] = sizeof(event) - 2;
144     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_TOTAL_NUM_ITEMS;
145     little_endian_store_16(event, pos, browsing_cid);
146     pos += 2;
147     event[pos++] = connection->scope;
148     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
149 }
150 
151 static void avrcp_browsing_target_emit_set_browsed_player(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t browsed_player_id){
152     btstack_assert(callback != NULL);
153 
154     uint8_t event[10];
155     int pos = 0;
156     event[pos++] = HCI_EVENT_AVRCP_META;
157     event[pos++] = sizeof(event) - 2;
158     event[pos++] = AVRCP_SUBEVENT_BROWSING_SET_BROWSED_PLAYER;
159     little_endian_store_16(event, pos, browsing_cid);
160     pos += 2;
161     little_endian_store_16(event, pos, browsed_player_id);
162     pos += 2;
163     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
164 }
165 
166 static void avrcp_browsing_target_emit_change_path(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, avrcp_browsing_direction_t direction, uint8_t * item_id){
167     btstack_assert(callback != NULL);
168 
169     uint8_t event[19];
170     int pos = 0;
171     event[pos++] = HCI_EVENT_AVRCP_META;
172     event[pos++] = sizeof(event) - 2;
173     event[pos++] = AVRCP_SUBEVENT_BROWSING_CHANGE_PATH;
174     little_endian_store_16(event, pos, browsing_cid);
175     pos += 2;
176     little_endian_store_16(event, pos, uid_counter);
177     pos += 2;
178     event[pos++] = direction;
179     memcpy(&event[pos], item_id, 8);
180     pos += 8;
181     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
182 }
183 
184 static void avrcp_browsing_target_emit_get_item_attributes(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, uint8_t scope, uint8_t * item_id, uint8_t attr_num, uint8_t * attr_list){
185     btstack_assert(callback != NULL);
186     btstack_assert(attr_num <= AVRCP_MEDIA_ATTR_NUM);
187 
188     uint8_t event[19 + 4 * AVRCP_MEDIA_ATTR_NUM];
189     int pos = 0;
190     event[pos++] = HCI_EVENT_AVRCP_META;
191     event[pos++] = sizeof(event) - 2;
192     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_ITEM_ATTRIBUTES;
193     little_endian_store_16(event, pos, browsing_cid);
194     pos += 2;
195     little_endian_store_16(event, pos, uid_counter);
196     pos += 2;
197     event[pos++] = scope;
198     memcpy(&event[pos], item_id, 8);
199     pos += 8;
200     uint16_t attr_len = attr_num * 4;
201     little_endian_store_16(event, pos, attr_len);
202     pos += 2;
203 
204     memcpy(&event[pos], attr_list, attr_len);
205     pos += attr_len;
206     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
207 }
208 
209 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
210     UNUSED(size);
211     avrcp_browsing_connection_t * browsing_connection;
212     avrcp_browsing_direction_t direction;
213 
214     switch (packet_type) {
215         case L2CAP_DATA_PACKET:{
216             browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
217             if (!browsing_connection) break;
218             int pos = 0;
219             uint8_t transport_header = packet[pos++];
220             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
221             browsing_connection->transaction_label = transport_header >> 4;
222             avctp_packet_type_t avctp_packet_type = (avctp_packet_type_t)((transport_header & 0x0F) >> 2);
223             switch (avctp_packet_type){
224                 case AVCTP_SINGLE_PACKET:
225                 case AVCTP_START_PACKET:
226                     browsing_connection->subunit_type = packet[pos++] >> 2;
227                     browsing_connection->subunit_id = 0;
228                     browsing_connection->command_opcode = packet[pos++];
229                     browsing_connection->num_packets = 1;
230                     if (avctp_packet_type == AVCTP_START_PACKET){
231                         browsing_connection->num_packets = packet[pos++];
232                     }
233                     browsing_connection->pdu_id = packet[pos++];
234                     uint16_t parameter_length = big_endian_read_16(packet, pos);
235                     pos += 2;
236 
237                     switch(browsing_connection->pdu_id){
238                         case AVRCP_PDU_ID_SEARCH:{
239                             if (parameter_length < 4){
240                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
241                                 break;
242                             }
243                             browsing_connection->target_search_characterset = big_endian_read_16(packet, pos);
244                             pos += 2;
245                             browsing_connection->target_search_str_len = big_endian_read_16(packet, pos);
246                             pos += 2;
247                             browsing_connection->target_search_str = (char *) &packet[pos];
248 
249                             if (parameter_length < (4 + browsing_connection->target_search_str_len)){
250                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
251                                 break;
252                             }
253 
254                             uint16_t string_len = strlen(browsing_connection->target_search_str);
255                             if ((browsing_connection->target_search_str_len != string_len) || (browsing_connection->target_search_str_len > (size-pos))){
256                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PARAMETER);
257                                 break;
258                             }
259                             avrcp_browsing_target_emit_search(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
260                             break;
261                         }
262                         case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
263                             if (parameter_length < 10){
264                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
265                                 break;
266                             }
267 
268                             browsing_connection->scope = packet[pos++];
269                             browsing_connection->start_item = big_endian_read_32(packet, pos);
270                             pos += 4;
271                             browsing_connection->end_item = big_endian_read_32(packet, pos);
272                             pos += 4;
273                             uint8_t attr_count = packet[pos++];
274                             browsing_connection->attr_bitmap = 0;
275 
276                             while (attr_count){
277                                 uint32_t attr_id = big_endian_read_32(packet, pos);
278                                 pos += 4;
279                                 browsing_connection->attr_bitmap |= (1 << attr_id);
280                                 attr_count--;
281                             }
282                             avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
283                             break;
284 
285                         case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
286                             // send total num items
287                             if (parameter_length != 1){
288                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_SCOPE);
289                                 break;
290                             }
291 
292                             browsing_connection->scope = packet[pos++];
293                             avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
294                             break;
295                         }
296                         case AVRCP_PDU_ID_SET_BROWSED_PLAYER:
297                             // player_id (2)
298                             if (parameter_length != 2){
299                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
300                                 break;
301                             }
302                             if ( (pos + 2) > size ){
303                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PLAYER_ID);
304                                 break;
305                             }
306                             avrcp_browsing_target_emit_set_browsed_player(avrcp_target_context.browsing_avrcp_callback, channel, big_endian_read_16(packet, pos));
307                             break;
308 
309                         case AVRCP_PDU_ID_CHANGE_PATH:
310                             // one level up or down in the virtual filesystem
311                             if (parameter_length != 11){
312                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
313                                 break;
314                             }
315                             browsing_connection->uid_counter = big_endian_read_16(packet, pos);
316                             pos += 2;
317                             browsing_connection->direction = (avrcp_browsing_direction_t)packet[pos++];
318 
319                             if (browsing_connection->direction > AVRCP_BROWSING_DIRECTION_FOLDER_RFU){
320                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_DIRECTION);
321                                 break;
322                             }
323                             memcpy(browsing_connection->item_uid, &packet[pos], 8);
324                             avrcp_browsing_target_emit_change_path(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->direction, browsing_connection->item_uid);
325                             break;
326 
327                         case AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES:{
328                             if (parameter_length < 12){
329                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
330                                 break;
331                             }
332 
333                             browsing_connection->scope = packet[pos++];
334                             memcpy(browsing_connection->item_uid, &packet[pos], 8);
335                             pos += 8;
336                             browsing_connection->uid_counter = big_endian_read_16(packet, pos);
337                             pos += 2;
338                             browsing_connection->attr_list_size = packet[pos++];
339                             browsing_connection->attr_list = &packet[pos];
340 
341                             avrcp_browsing_target_emit_get_item_attributes(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter,
342                                                                            browsing_connection->scope, browsing_connection->item_uid, browsing_connection->attr_list_size, browsing_connection->attr_list);
343                             break;
344                         }
345 
346                         default:
347                             avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
348                             log_info("not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
349                             break;
350                     }
351                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
352                     break;
353 
354                 default:
355                     break;
356             }
357             break;
358         }
359 
360         case HCI_EVENT_PACKET:
361             switch (hci_event_packet_get_type(packet)){
362                 case L2CAP_EVENT_CAN_SEND_NOW:
363                     browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
364                     if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
365                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
366                     avrcp_browsing_target_handle_can_send_now(browsing_connection);
367                     break;
368                 default:
369                     break;
370             }
371             break;
372 
373         default:
374             break;
375     }
376 }
377 
378 void avrcp_browsing_target_init(void){
379     avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler;
380     avrcp_browsing_register_target_packet_handler(avrcp_browsing_target_packet_handler);
381 }
382 
383 void avrcp_browsing_target_deinit(void){
384     avrcp_controller_context.browsing_packet_handler = NULL;
385 }
386 
387 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){
388     btstack_assert(callback != NULL);
389     avrcp_target_context.browsing_avrcp_callback = callback;
390 }
391 
392 uint8_t avrcp_browsing_target_send_get_folder_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint8_t * attr_list, uint16_t attr_list_size, uint16_t num_items){
393     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
394     if (!avrcp_connection){
395         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
396         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
397     }
398 
399     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
400     if (!connection){
401         log_info("Could not find a browsing connection.");
402         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
403     }
404     if (connection->state != AVCTP_CONNECTION_OPENED){
405         return ERROR_CODE_COMMAND_DISALLOWED;
406     }
407 
408     // TODO: handle response to SetAddressedPlayer
409 
410     uint16_t pos = 0;
411     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
412     uint8_t param_length = 5;
413     uint8_t param_length_pos = pos;
414     pos += 2;
415 
416     avrcp_status_code_t status = AVRCP_STATUS_SUCCESS;
417     uint8_t status_pos = pos;
418     pos++;
419 
420     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
421     pos += 2;
422 
423     // TODO: fragmentation
424     if (attr_list_size >  sizeof(connection->cmd_operands)){
425         connection->attr_list = attr_list;
426         connection->attr_list_size = attr_list_size;
427         log_info(" todo: list too big, invoke fragmentation");
428         return 1;
429     }
430 
431     uint16_t items_byte_len = 0;
432     if (connection->start_item < num_items) {
433         if (connection->end_item < num_items) {
434             items_byte_len =  connection->end_item - connection->start_item + 1;
435         } else {
436             items_byte_len = num_items - connection->start_item;
437         }
438 
439     } else {
440         big_endian_store_16(connection->cmd_operands, pos, 0);
441         pos += 2;
442     }
443     big_endian_store_16(connection->cmd_operands, pos, items_byte_len);
444     pos += 2;
445     param_length += items_byte_len;
446 
447     if (items_byte_len > 0){
448         (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
449         pos += attr_list_size;
450         connection->cmd_operands_length = pos;
451     } else {
452         status = AVRCP_STATUS_RANGE_OUT_OF_BOUNDS;
453         param_length = 1;
454         connection->cmd_operands_length = status_pos + 1;
455     }
456 
457     big_endian_store_16(connection->cmd_operands, param_length_pos, param_length);
458     connection->cmd_operands[status_pos] = status;
459 
460     btstack_assert(pos <= 400);
461 
462 
463     connection->state = AVCTP_W2_SEND_RESPONSE;
464     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
465     return ERROR_CODE_SUCCESS;
466 }
467 
468 uint8_t avrcp_browsing_target_send_change_path_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, avrcp_status_code_t status, uint32_t num_items){
469     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
470     if (!avrcp_connection){
471         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
472         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
473     }
474 
475     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
476     if (!connection){
477         log_info("Could not find a browsing connection.");
478         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
479     }
480     if (connection->state != AVCTP_CONNECTION_OPENED){
481         return ERROR_CODE_COMMAND_DISALLOWED;
482     }
483 
484     uint16_t pos = 0;
485     uint16_t param_length = (status == AVRCP_STATUS_SUCCESS) ? 5 : 1;
486     connection->cmd_operands[pos++] = AVRCP_PDU_ID_CHANGE_PATH;
487 
488     // Param length
489     big_endian_store_16(connection->cmd_operands, pos, param_length);
490     pos += 2;
491     connection->cmd_operands[pos++] = status;
492 
493     if (status == AVRCP_STATUS_SUCCESS){
494         big_endian_store_32(connection->cmd_operands, pos, num_items);
495         pos += 4;
496     }
497 
498     connection->cmd_operands_length = pos;
499     connection->state = AVCTP_W2_SEND_RESPONSE;
500     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
501     return ERROR_CODE_SUCCESS;
502 }
503 
504 uint8_t avrcp_browsing_target_send_get_item_attributes_response(uint16_t avrcp_browsing_cid, avrcp_status_code_t status, uint8_t * attr_list, uint16_t attr_list_size, uint8_t num_items){
505     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
506     if (!avrcp_connection){
507         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
508         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
509     }
510 
511     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
512     if (!connection){
513         log_info("Could not find a browsing connection.");
514         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
515     }
516     if (connection->state != AVCTP_CONNECTION_OPENED){
517         return ERROR_CODE_COMMAND_DISALLOWED;
518     }
519 
520     // TODO: fragmentation
521     if (attr_list_size >  (sizeof(connection->cmd_operands) - 5)){
522         connection->attr_list = attr_list;
523         connection->attr_list_size = attr_list_size;
524         log_info(" todo: list too big, invoke fragmentation");
525         return 1;
526     }
527 
528     uint16_t pos = 0;
529     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES;
530 
531     uint8_t param_length_pos = pos;
532     big_endian_store_16(connection->cmd_operands, pos, 1);
533     pos += 2;
534     connection->cmd_operands[pos++] = status;
535 
536     if (status != AVRCP_STATUS_SUCCESS){
537         connection->cmd_operands_length = pos;
538         connection->state = AVCTP_W2_SEND_RESPONSE;
539         avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
540         return ERROR_CODE_SUCCESS;
541     }
542 
543     connection->cmd_operands[pos++] = num_items;
544     (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
545     pos += attr_list_size;
546 
547     big_endian_store_16(connection->cmd_operands, param_length_pos, pos - 3);
548     connection->cmd_operands_length = pos;
549     connection->state = AVCTP_W2_SEND_RESPONSE;
550     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
551     return ERROR_CODE_SUCCESS;
552 }
553 
554 uint8_t avrcp_browsing_target_send_accept_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint16_t browsed_player_id, uint8_t * response, uint16_t response_size){
555     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
556     if (!avrcp_connection){
557         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
558         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
559     }
560 
561     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
562     if (!connection){
563         log_info("Could not find a browsing connection.");
564         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
565     }
566 
567     if (connection->state != AVCTP_CONNECTION_OPENED) {
568         log_info("Browsing connection wrong state.");
569         return ERROR_CODE_COMMAND_DISALLOWED;
570     }
571 
572     connection->browsed_player_id = browsed_player_id;
573 
574     uint16_t pos = 0;
575     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
576     big_endian_store_16(connection->cmd_operands, pos, response_size + 2 + 1); // uuid counter + status
577     pos += 2;
578 
579     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
580     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
581     pos += 2;
582 
583     // TODO: fragmentation
584     if (response_size >  sizeof(connection->cmd_operands)){
585         connection->attr_list = response;
586         connection->attr_list_size = response_size;
587         log_info(" todo: list too big, invoke fragmentation");
588         return 1;
589     }
590 
591     (void)memcpy(&connection->cmd_operands[pos], response, response_size);
592     pos += response_size;
593 	btstack_assert(pos <= 255);
594     connection->cmd_operands_length = (uint8_t) pos;
595 
596     connection->state = AVCTP_W2_SEND_RESPONSE;
597     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
598     return ERROR_CODE_SUCCESS;
599 }
600 
601 uint8_t avrcp_browsing_target_send_reject_set_browsed_player(uint16_t avrcp_browsing_cid, avrcp_status_code_t status){
602     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
603     if (!avrcp_connection){
604         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
605         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
606     }
607 
608     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
609     if (!connection){
610         log_info("Could not find a browsing connection.");
611         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
612     }
613 
614     if (connection->state != AVCTP_CONNECTION_OPENED) {
615         log_info("Browsing connection wrong state.");
616         return ERROR_CODE_COMMAND_DISALLOWED;
617     }
618 
619     int pos = 0;
620     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
621     big_endian_store_16(connection->cmd_operands, pos, 1);
622     pos += 2;
623     connection->cmd_operands[pos++] = status;
624     connection->cmd_operands_length = pos;
625 
626     connection->state = AVCTP_W2_SEND_RESPONSE;
627     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
628     return ERROR_CODE_SUCCESS;
629 }
630 
631 uint8_t avrcp_browsing_target_send_get_total_num_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint32_t total_num_items){
632     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
633     if (!avrcp_connection){
634         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
635         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
636     }
637 
638     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
639     if (!connection){
640         log_info("Could not find a browsing connection.");
641         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
642     }
643 
644     if (connection->state != AVCTP_CONNECTION_OPENED) {
645         log_info("Browsing connection wrong state.");
646         return ERROR_CODE_COMMAND_DISALLOWED;
647     }
648 
649     int pos = 0;
650     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
651     big_endian_store_16(connection->cmd_operands, pos, 7);
652     pos += 2;
653     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
654     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
655     pos += 2;
656     big_endian_store_32(connection->cmd_operands, pos, total_num_items);
657     pos += 4;
658     connection->cmd_operands_length = pos;
659 
660     connection->state = AVCTP_W2_SEND_RESPONSE;
661     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
662     return ERROR_CODE_SUCCESS;
663 }
664 
665 uint8_t avrcp_browsing_target_send_search_response(uint16_t avrcp_browsing_cid, avrcp_status_code_t status, uint16_t uid_counter, uint32_t num_items){
666     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
667     if (!avrcp_connection){
668         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
669         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
670     }
671 
672     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
673     if (!connection){
674         log_info("Could not find a browsing connection.");
675         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
676     }
677     if (connection->state != AVCTP_CONNECTION_OPENED){
678         return ERROR_CODE_COMMAND_DISALLOWED;
679     }
680 
681     uint16_t pos = 0;
682     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SEARCH;
683     // param len
684     big_endian_store_16(connection->cmd_operands, pos, 7);
685     pos += 2;
686     connection->cmd_operands[pos++] = status;
687     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
688     pos += 2;
689 
690 //    if (status != AVRCP_STATUS_SUCCESS){
691 //        connection->cmd_operands_length = pos;
692 //        connection->state = AVCTP_W2_SEND_RESPONSE;
693 //        avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
694 //        return ERROR_CODE_SUCCESS;
695 //    }
696 
697     big_endian_store_32(connection->cmd_operands, pos, num_items);
698     pos += 4;
699     connection->cmd_operands_length = pos;
700     connection->state = AVCTP_W2_SEND_RESPONSE;
701     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
702     return ERROR_CODE_SUCCESS;
703 }