1/******************************************************************************
2 *
3 *  Copyright (c) 2014 The Android Open Source Project
4 *  Copyright (C) 2004-2012 Broadcom Corporation
5 *
6 *  Licensed under the Apache License, Version 2.0 (the "License");
7 *  you may not use this file except in compliance with the License.
8 *  You may obtain a copy of the License at:
9 *
10 *  http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 ******************************************************************************/
19#include <string.h>
20
21#include "bt_trace.h"
22#include "bt_utils.h"
23#include "bta_ag_api.h"
24#include "bta_hf_client_int.h"
25#include "device/include/esco_parameters.h"
26#include "osi/include/osi.h"
27
28#define BTA_HF_CLIENT_NO_EDR_ESCO                                \
29  (ESCO_PKT_TYPES_MASK_NO_2_EV3 | ESCO_PKT_TYPES_MASK_NO_3_EV3 | \
30   ESCO_PKT_TYPES_MASK_NO_2_EV5 | ESCO_PKT_TYPES_MASK_NO_3_EV5)
31
32enum {
33  BTA_HF_CLIENT_SCO_LISTEN_E,
34  BTA_HF_CLIENT_SCO_OPEN_E,       /* open request */
35  BTA_HF_CLIENT_SCO_CLOSE_E,      /* close request */
36  BTA_HF_CLIENT_SCO_SHUTDOWN_E,   /* shutdown request */
37  BTA_HF_CLIENT_SCO_CONN_OPEN_E,  /* SCO opened */
38  BTA_HF_CLIENT_SCO_CONN_CLOSE_E, /* SCO closed */
39};
40
41/*******************************************************************************
42 *
43 * Function         bta_hf_client_remove_sco
44 *
45 * Description      Removes the specified SCO from the system.
46 *
47 * Returns          bool   - true if SCO removal was started
48 *
49 ******************************************************************************/
50static bool bta_hf_client_sco_remove(tBTA_HF_CLIENT_CB* client_cb) {
51  bool removed_started = false;
52  tBTM_STATUS status;
53
54  APPL_TRACE_DEBUG("%s", __func__);
55
56  if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) {
57    status = BTM_RemoveSco(client_cb->sco_idx);
58
59    APPL_TRACE_DEBUG("%s: idx 0x%04x, status:0x%x", __func__,
60                     client_cb->sco_idx, status);
61
62    if (status == BTM_CMD_STARTED) {
63      removed_started = true;
64    }
65    /* If no connection reset the SCO handle */
66    else if ((status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR)) {
67      client_cb->sco_idx = BTM_INVALID_SCO_INDEX;
68    }
69  }
70  return removed_started;
71}
72
73/*******************************************************************************
74 *
75 * Function         bta_hf_client_cback_sco
76 *
77 * Description      Call application callback function with SCO event.
78 *
79 *
80 * Returns          void
81 *
82 ******************************************************************************/
83void bta_hf_client_cback_sco(tBTA_HF_CLIENT_CB* client_cb, uint8_t event) {
84  tBTA_HF_CLIENT evt;
85
86  memset(&evt, 0, sizeof(evt));
87  evt.bd_addr = client_cb->peer_addr;
88
89  /* call app cback */
90  bta_hf_client_app_callback(event, (tBTA_HF_CLIENT*)&evt);
91}
92
93/*******************************************************************************
94 *
95 * Function         bta_hf_client_sco_conn_rsp
96 *
97 * Description      Process the SCO connection request
98 *
99 *
100 * Returns          void
101 *
102 ******************************************************************************/
103static void bta_hf_client_sco_conn_rsp(tBTA_HF_CLIENT_CB* client_cb,
104                                       tBTM_ESCO_CONN_REQ_EVT_DATA* p_data) {
105  enh_esco_params_t resp;
106  uint8_t hci_status = HCI_SUCCESS;
107
108  APPL_TRACE_DEBUG("%s", __func__);
109
110  if (client_cb->sco_state == BTA_HF_CLIENT_SCO_LISTEN_ST) {
111    if (p_data->link_type == BTM_LINK_TYPE_SCO) {
112      resp = esco_parameters_for_codec(ESCO_CODEC_CVSD);
113    } else {
114      if (client_cb->negotiated_codec == BTA_AG_CODEC_MSBC) {
115        resp = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1);
116      } else {
117        // default codec
118        resp = esco_parameters_for_codec(ESCO_CODEC_CVSD);
119      }
120    }
121
122    /* tell sys to stop av if any */
123    bta_sys_sco_use(BTA_ID_HS, 1, client_cb->peer_addr);
124  } else {
125    hci_status = HCI_ERR_HOST_REJECT_DEVICE;
126  }
127
128  BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp);
129}
130
131/*******************************************************************************
132 *
133 * Function         bta_hf_client_sco_connreq_cback
134 *
135 * Description      BTM eSCO connection requests and eSCO change requests
136 *                  Only the connection requests are processed by BTA.
137 *
138 * Returns          void
139 *
140 ******************************************************************************/
141static void bta_hf_client_esco_connreq_cback(tBTM_ESCO_EVT event,
142                                             tBTM_ESCO_EVT_DATA* p_data) {
143  APPL_TRACE_DEBUG("%s: %d", __func__, event);
144
145  tBTA_HF_CLIENT_CB* client_cb =
146      bta_hf_client_find_cb_by_sco_handle(p_data->conn_evt.sco_inx);
147  if (client_cb == NULL) {
148    APPL_TRACE_ERROR("%s: wrong SCO handle to control block %d", __func__,
149                     p_data->conn_evt.sco_inx);
150    return;
151  }
152
153  if (event != BTM_ESCO_CONN_REQ_EVT) {
154    return;
155  }
156
157  bta_hf_client_sco_conn_rsp(client_cb, &p_data->conn_evt);
158
159  client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
160}
161
162/*******************************************************************************
163 *
164 * Function         bta_hf_client_sco_conn_cback
165 *
166 * Description      BTM SCO connection callback.
167 *
168 *
169 * Returns          void
170 *
171 ******************************************************************************/
172static void bta_hf_client_sco_conn_cback(uint16_t sco_idx) {
173  APPL_TRACE_DEBUG("%s: %d", __func__, sco_idx);
174
175  tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_sco_handle(sco_idx);
176  if (client_cb == NULL) {
177    APPL_TRACE_ERROR("%s: wrong SCO handle to control block %d", __func__,
178                     sco_idx);
179    return;
180  }
181
182  BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
183  p_buf->event = BTA_HF_CLIENT_SCO_OPEN_EVT;
184  p_buf->layer_specific = client_cb->handle;
185  bta_sys_sendmsg(p_buf);
186}
187
188/*******************************************************************************
189 *
190 * Function         bta_hf_client_sco_disc_cback
191 *
192 * Description      BTM SCO disconnection callback.
193 *
194 *
195 * Returns          void
196 *
197 ******************************************************************************/
198static void bta_hf_client_sco_disc_cback(uint16_t sco_idx) {
199  APPL_TRACE_DEBUG("%s: sco_idx %d", __func__, sco_idx);
200
201  tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_sco_handle(sco_idx);
202  if (client_cb == NULL) {
203    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, sco_idx);
204    return;
205  }
206
207  BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
208  p_buf->event = BTA_HF_CLIENT_SCO_CLOSE_EVT;
209  p_buf->layer_specific = client_cb->handle;
210  bta_sys_sendmsg(p_buf);
211}
212
213/*******************************************************************************
214 *
215 * Function         bta_hf_client_create_sco
216 *
217 * Description
218 *
219 *
220 * Returns          void
221 *
222 ******************************************************************************/
223static void bta_hf_client_sco_create(tBTA_HF_CLIENT_CB* client_cb,
224                                     bool is_orig) {
225  tBTM_STATUS status;
226
227  APPL_TRACE_DEBUG("%s: %d", __func__, is_orig);
228
229  /* Make sure this SCO handle is not already in use */
230  if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) {
231    APPL_TRACE_WARNING("%s: Index 0x%04x already in use", __func__,
232                       client_cb->sco_idx);
233    return;
234  }
235
236  enh_esco_params_t params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1);
237
238  /* if initiating set current scb and peer bd addr */
239  if (is_orig) {
240    BTM_SetEScoMode(&params);
241    /* tell sys to stop av if any */
242    bta_sys_sco_use(BTA_ID_HS, 1, client_cb->peer_addr);
243  }
244
245  status = BTM_CreateSco(&client_cb->peer_addr, is_orig, params.packet_types,
246                         &client_cb->sco_idx, bta_hf_client_sco_conn_cback,
247                         bta_hf_client_sco_disc_cback);
248  if (status == BTM_CMD_STARTED && !is_orig) {
249    if (!BTM_RegForEScoEvts(client_cb->sco_idx,
250                            bta_hf_client_esco_connreq_cback))
251      APPL_TRACE_DEBUG("%s: SCO registration success", __func__);
252  }
253
254  APPL_TRACE_API("%s: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x",
255                 __func__, is_orig, client_cb->sco_idx, status,
256                 params.packet_types);
257}
258
259/*******************************************************************************
260 *
261 * Function         bta_hf_client_sco_event
262 *
263 * Description      Handle SCO events
264 *
265 *
266 * Returns          void
267 *
268 ******************************************************************************/
269static void bta_hf_client_sco_event(tBTA_HF_CLIENT_CB* client_cb,
270                                    uint8_t event) {
271  APPL_TRACE_DEBUG("%s: before state: %d event: %d", __func__,
272                   client_cb->sco_state, event);
273
274  switch (client_cb->sco_state) {
275    case BTA_HF_CLIENT_SCO_SHUTDOWN_ST:
276      switch (event) {
277        // For WBS we only listen to SCO requests. Even for outgoing SCO
278        // requests we first do a AT+BCC and wait for remote to initiate SCO
279        case BTA_HF_CLIENT_SCO_LISTEN_E:
280          /* create SCO listen connection */
281          bta_hf_client_sco_create(client_cb, false);
282          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
283          break;
284
285        // For non WBS cases and enabling outgoing SCO requests we need to force
286        // open a SCO channel
287        case BTA_HF_CLIENT_SCO_OPEN_E:
288          /* remove listening connection */
289          bta_hf_client_sco_remove(client_cb);
290
291          /* create SCO connection to peer */
292          bta_hf_client_sco_create(client_cb, true);
293          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
294          break;
295
296        default:
297          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event %d",
298                             event);
299          break;
300      }
301      break;
302
303    case BTA_HF_CLIENT_SCO_LISTEN_ST:
304      switch (event) {
305        case BTA_HF_CLIENT_SCO_LISTEN_E:
306          /* create SCO listen connection */
307          bta_hf_client_sco_create(client_cb, false);
308
309        case BTA_HF_CLIENT_SCO_OPEN_E:
310          /* remove listening connection */
311          bta_hf_client_sco_remove(client_cb);
312
313          /* create SCO connection to peer */
314          bta_hf_client_sco_create(client_cb, true);
315          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
316          break;
317
318        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
319        case BTA_HF_CLIENT_SCO_CLOSE_E:
320          /* remove listening connection */
321          bta_hf_client_sco_remove(client_cb);
322
323          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
324          break;
325
326        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
327          /* SCO failed; create SCO listen connection */
328          bta_hf_client_sco_create(client_cb, false);
329          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
330          break;
331
332        default:
333          APPL_TRACE_WARNING(
334              "%s: BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", __func__,
335              event);
336          break;
337      }
338      break;
339
340    case BTA_HF_CLIENT_SCO_OPENING_ST:
341      switch (event) {
342        case BTA_HF_CLIENT_SCO_CLOSE_E:
343          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_CL_ST;
344          break;
345
346        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
347          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
348          break;
349
350        case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
351          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_ST;
352          break;
353
354        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
355          /* SCO failed; create SCO listen connection */
356          bta_hf_client_sco_create(client_cb, false);
357          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
358          break;
359
360        default:
361          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPENING_ST: Ignoring event %d",
362                             event);
363          break;
364      }
365      break;
366
367    case BTA_HF_CLIENT_SCO_OPEN_CL_ST:
368      switch (event) {
369        case BTA_HF_CLIENT_SCO_OPEN_E:
370          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
371          break;
372
373        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
374          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
375          break;
376
377        case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
378          /* close SCO connection */
379          bta_hf_client_sco_remove(client_cb);
380
381          client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
382          break;
383
384        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
385          /* SCO failed; create SCO listen connection */
386
387          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
388          break;
389
390        default:
391          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_CL_ST: Ignoring event %d",
392                             event);
393          break;
394      }
395      break;
396
397    case BTA_HF_CLIENT_SCO_OPEN_ST:
398      switch (event) {
399        case BTA_HF_CLIENT_SCO_CLOSE_E:
400          if (bta_hf_client_sco_remove(client_cb)) {
401            client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
402          }
403          break;
404
405        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
406          /* remove listening connection */
407          bta_hf_client_sco_remove(client_cb);
408
409          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
410          break;
411
412        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
413          /* peer closed SCO */
414          bta_hf_client_sco_create(client_cb, false);
415          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
416          break;
417
418        default:
419          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_ST: Ignoring event %d",
420                             event);
421          break;
422      }
423      break;
424
425    case BTA_HF_CLIENT_SCO_CLOSING_ST:
426      switch (event) {
427        case BTA_HF_CLIENT_SCO_OPEN_E:
428          client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSE_OP_ST;
429          break;
430
431        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
432          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
433          break;
434
435        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
436          /* peer closed sco; create SCO listen connection */
437          bta_hf_client_sco_create(client_cb, false);
438          client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
439          break;
440
441        default:
442          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSING_ST: Ignoring event %d",
443                             event);
444          break;
445      }
446      break;
447
448    case BTA_HF_CLIENT_SCO_CLOSE_OP_ST:
449      switch (event) {
450        case BTA_HF_CLIENT_SCO_CLOSE_E:
451          client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
452          break;
453
454        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
455          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
456          break;
457
458        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
459          /* open SCO connection */
460          bta_hf_client_sco_create(client_cb, true);
461          client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
462          break;
463
464        default:
465          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSE_OP_ST: Ignoring event %d",
466                             event);
467          break;
468      }
469      break;
470
471    case BTA_HF_CLIENT_SCO_SHUTTING_ST:
472      switch (event) {
473        case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
474          /* close SCO connection; wait for conn close event */
475          bta_hf_client_sco_remove(client_cb);
476          break;
477
478        case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
479          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
480          break;
481
482        case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
483          client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
484          break;
485
486        default:
487          APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTTING_ST: Ignoring event %d",
488                             event);
489          break;
490      }
491      break;
492
493    default:
494      break;
495  }
496
497  APPL_TRACE_DEBUG("%s: after state: %d", __func__, client_cb->sco_state);
498}
499
500/*******************************************************************************
501 *
502 * Function         bta_hf_client_sco_listen
503 *
504 * Description      Initialize SCO listener
505 *
506 *
507 * Returns          void
508 *
509 ******************************************************************************/
510void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA* p_data) {
511  APPL_TRACE_DEBUG("%s", __func__);
512
513  tBTA_HF_CLIENT_CB* client_cb =
514      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
515  if (client_cb == NULL) {
516    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
517                     p_data->hdr.layer_specific);
518    return;
519  }
520
521  bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_LISTEN_E);
522}
523
524/*******************************************************************************
525 *
526 * Function         bta_hf_client_sco_shutdown
527 *
528 * Description
529 *
530 *
531 * Returns          void
532 *
533 ******************************************************************************/
534void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_CB* client_cb) {
535  APPL_TRACE_DEBUG("%s", __func__);
536
537  bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_SHUTDOWN_E);
538}
539
540/*******************************************************************************
541 *
542 * Function         bta_hf_client_sco_conn_open
543 *
544 * Description
545 *
546 *
547 * Returns          void
548 *
549 ******************************************************************************/
550void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA* p_data) {
551  APPL_TRACE_DEBUG("%s", __func__);
552
553  tBTA_HF_CLIENT_CB* client_cb =
554      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
555  if (client_cb == NULL) {
556    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
557                     p_data->hdr.layer_specific);
558    return;
559  }
560
561  bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_OPEN_E);
562
563  bta_sys_sco_open(BTA_ID_HS, 1, client_cb->peer_addr);
564
565  if (client_cb->negotiated_codec == BTM_SCO_CODEC_MSBC) {
566    bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT);
567  } else {
568    bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_OPEN_EVT);
569  }
570}
571
572/*******************************************************************************
573 *
574 * Function         bta_hf_client_sco_conn_close
575 *
576 * Description
577 *
578 *
579 * Returns          void
580 *
581 ******************************************************************************/
582void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA* p_data) {
583  APPL_TRACE_DEBUG("%s", __func__);
584
585  tBTA_HF_CLIENT_CB* client_cb =
586      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
587  if (client_cb == NULL) {
588    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
589                     p_data->hdr.layer_specific);
590    return;
591  }
592
593  /* clear current scb */
594  client_cb->sco_idx = BTM_INVALID_SCO_INDEX;
595
596  bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_CLOSE_E);
597
598  bta_sys_sco_close(BTA_ID_HS, 1, client_cb->peer_addr);
599
600  bta_sys_sco_unuse(BTA_ID_HS, 1, client_cb->peer_addr);
601
602  /* call app callback */
603  bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_CLOSE_EVT);
604
605  if (client_cb->sco_close_rfc == true) {
606    client_cb->sco_close_rfc = false;
607    bta_hf_client_rfc_do_close(p_data);
608  }
609}
610
611/*******************************************************************************
612 *
613 * Function         bta_hf_client_sco_open
614 *
615 * Description
616 *
617 *
618 * Returns          void
619 *
620 ******************************************************************************/
621void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA* p_data) {
622  APPL_TRACE_DEBUG("%s", __func__);
623
624  tBTA_HF_CLIENT_CB* client_cb =
625      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
626  if (client_cb == NULL) {
627    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
628                     p_data->hdr.layer_specific);
629    return;
630  }
631
632  bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_OPEN_E);
633}
634
635/*******************************************************************************
636 *
637 * Function         bta_hf_client_sco_close
638 *
639 * Description
640 *
641 *
642 * Returns          void
643 *
644 ******************************************************************************/
645void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA* p_data) {
646  tBTA_HF_CLIENT_CB* client_cb =
647      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
648  if (client_cb == NULL) {
649    APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
650                     p_data->hdr.layer_specific);
651    return;
652  }
653
654  APPL_TRACE_DEBUG("%s: sco_idx 0x%x", __func__, client_cb->sco_idx);
655
656  if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) {
657    bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CLOSE_E);
658  }
659}
660