-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Bluetooth: Classic: HFP: Support ongoing calls before SLC established #88331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bluetooth: Classic: HFP: Support ongoing calls before SLC established #88331
Conversation
d8d4f65
to
ad0cdab
Compare
@gzh-terry , please try the changes. |
4a55c15
to
ae848c8
Compare
* | ||
* @param number Phone number of the call. | ||
* @param type Specify the format of the phone number. | ||
* @param dir Call direction (true for incoming, false for outgoing). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a very intuitive way to use or name a boolean. At the very least its name should reflect its meaning, i.e. it should be called incoming
(that way the code using it will look saner). However, probably an enum with two possible values would be even better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. An enum is more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
/* existing calls */ | ||
struct bt_hfp_ag_existing_call existing_calls[CONFIG_BT_HFP_AG_MAX_CALLS]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, is "existing" the term used in the spec for such calls? I'd have otherwise used "ongoing" or "active".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I tend to use ongoing
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
struct bt_hfp_hf_call *call; | ||
int count = 0; | ||
|
||
for (size_t index = 0; index < ARRAY_SIZE(hf->calls); index++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please just start using ARRAY_FOR_EACH()
for any new array iteration, so we gradually move toward a common and consistent pattern for this throughout the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@@ -1229,6 +1257,7 @@ static struct bt_hfp_ag_cb ag_cb = { | |||
.disconnected = ag_disconnected, | |||
.sco_connected = ag_sco_connected, | |||
.sco_disconnected = ag_sco_disconnected, | |||
.get_existing_call = ag_get_existing_call, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @lylezhu2012
It looks like the AG stack itself would invoke a callback to acquire all call information upon SLC connected.
I've checked the implementations on the other stacks, and they look a little bit different. The HFP service (an app in Zephyr's view) will query the call status from the telecom (also called telephony) and notify the stack (Zephyr) as if new calls arrived.
See queryPhoneState and phoneStateChanged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a result, nothing to do at Zephyr's side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HFP service (an app in Zephyr's view) will query the call status from the telecom (also called telephony) and notify the stack (Zephyr) as if new calls arrived.
I am not sure whether there is a subsystem/module/driver likes as telecom
in zephyr. But I think even if there is, it should be integrated at a higher level with telecom
and hfp ag
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a result, nothing to do at Zephyr's side.
I added a command existing_calls
ongoing_calls
in hfp ag
to simulate there are some calles ongoing. Please refer to the changes of shell.
* | ||
* @return 0 in case of success and will get next one or negative value in case of error. | ||
*/ | ||
int (*get_existing_call)(struct bt_hfp_ag *ag, struct bt_hfp_ag_existing_call *call); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the AT+CIND=?
command, I would recommend separating the callback and the CIND response. You can refer to the Android code where a CIND callback only indicates that the AT+CIND=?
command is received, and another method is used to send the response.
If we combine them into one callback, it would be very difficult to transfer the work to another loop/thread (which is actually the majority of cases).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is not clear. I mean the callback will be notified after the command AT+CIND=?
has been handled. The result of the callback will be replied in the response of AT+CIND?
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @lylezhu2012
I thought your code works like this:
- We are at AG side.
- We received an
AT+CIND?
command to check the current call status. - We post this callback to ask app about current calls.
- App should return the call status directly via
*call
in this callback. - We transfer these call status to HF via
+CIND:...
My concern is, the upper layer can not return immediately in step 4. Because in most cases the app need to get these information in another loop/thread, and sometimes another core.
So what app did is to hold this loop, and post a message to somewhere else, and wait until the call informations are collected. In these period, all activities in zephyr are blocked (>100ms in case of IPC).
Besides, here comes another issue: the app may call some zephyr's interface again during this waiting, and can lead to deadlock issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So a typicall method is to make them happen asynchronously:
One callback to inform AT+CIND?
is received.
Another API to respond +CIND:...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue happens in A2DP, where the Start/Suspend request needs to be responded in a synchronous call (return 0 to accept, otherwise reject).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the PR. Please try it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gzh-terry , Did you try the patch?
ae848c8
to
eae65b2
Compare
@@ -280,7 +280,11 @@ static struct bt_hfp_ag_call *get_new_call(struct bt_hfp_ag *ag, const char *num | |||
|
|||
if (!atomic_test_and_set_bit(call->flags, BT_HFP_AG_CALL_IN_USING)) { | |||
/* Copy number to ag->number including null-character */ | |||
strcpy(call->number, number); | |||
if (number != NULL) { | |||
strcpy(call->number, number); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better use strncpy to avoid buffer overflow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There shouldn't be such a situation. Before calling get_new_call()
, the length of number
has been checked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even then, it's possible this will trigger warnings from static analyzers like Coverity. Ideally we should be using strlcpy()
. I don't see many uses of it in the main tree, perhaps because minimal libc lacks support for it, however that should be easy to fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I will try use strncpy()
to replace strcpy()
.
|
||
err = bt_ag->get_ongoing_call(ag, call); | ||
if (err != 0) { | ||
LOG_DBG("No ongoing call retrieved"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG_WRN?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a normal case for err != 0
. Instead, I think the err == 0
is a corner case.
@@ -1042,20 +1197,439 @@ static int bt_hfp_ag_cind_handler(struct bt_hfp_ag *ag, struct net_buf *buf) | |||
ag_ind[BT_HFP_AG_ROAM_IND].max, ag_ind[BT_HFP_AG_BATTERY_IND].name, | |||
ag_ind[BT_HFP_AG_BATTERY_IND].min, ag_ind[BT_HFP_AG_BATTERY_IND].connector, | |||
ag_ind[BT_HFP_AG_BATTERY_IND].max); | |||
|
|||
bt_hfp_ag_get_ongoing_calls(ag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bt_hfp_ag_get_ongoing_calls can be return int function? otherwise "err" need initialized 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err
is used to save the reture value of hfp_ag_send_data()
. bt_hfp_ag_get_ongoing_calls()
only tries to get the ongoing calls. If there are ongoing calls, save them.
ag->indicator_value[BT_HFP_AG_BATTERY_IND]); | ||
} | ||
|
||
for (size_t index = 0; index < ag->ongoing_call_count; index++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar with hf, also can use ARRAY_FOR_EACH
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARRAY_FOR_EACH()
is used to iterate over an array. The MAX is ARRAY_SIZE()
. In current case, the MAX is ag->ongoing_call_count
.
433726d
to
bd619cf
Compare
f7a1305
to
4e24f7c
Compare
case BT_HFP_CLCC_STATUS_ACTIVE: | ||
hf_call_state_update(call, BT_HFP_HF_CALL_STATE_ACTIVE); | ||
if (bt_hf->accept) { | ||
bt_hf->accept(call); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May change this name to active
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a case that incoming held
is also active
status.
incoming = true; | ||
} | ||
|
||
if (incoming != !!dir) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shoud generate a callback as well.
This usually happens when a call is taken over by another call (i.e., from VoIP to normal call)
We may receive two different calls with the same index in consecutive AT+CLCC procedure.
For example:
AT+CLCC
+CLCC: 1,0,0,0,0,"123456",129
OK
AT+CLCC
+CLCC: 1,1,0,0,0,"654321",129
OK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shoud generate a callback as well. This usually happens when a call is taken over by another call (i.e., from VoIP to normal call) We may receive two different calls with the same index in consecutive AT+CLCC procedure. For example:
AT+CLCC +CLCC: 1,0,0,0,0,"123456",129 OK AT+CLCC +CLCC: 1,1,0,0,0,"654321",129 OK
In the above case, we need to report the second call as a new, and the previous one is considered terminated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logically, the previous call should be removed first by AG, and then the new call should be notified.
Or, AG report these two calls at same time. And than remove the first one.
It seems that the phenomena you describe are not within the scope of HFP protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, the exchange of AT commands is considerably slower compared to the speed at which call state changes. This scenario frequently occurs in real products.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's still fine in this PR since it does not change the logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I think we can add this case down the road.
4e24f7c
to
7353e63
Compare
7353e63
to
c6b67df
Compare
Support the case that there are some calls existed before SLC established. Add a callback to get the ongoing calls one by one from upper layer when the response of the AT command `AT+CIND=?` from HF has been sent. And set the Call, Call Setup, and Held Call indicators and report the values int the response of AT command `AT+CIND?`. Then report all ongoing calls in the `+CLCC` response. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
For read-only access to fields, `lock/unlock` is unnecessary. Remove unnecessary `lock/unlock` protection for read-only access fields of AG. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
If the any value of Call, Call Setup, and Held Call indicators is not zero in the response of `AT+CIND?`, get all calls via `AT+CLCC`. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
Add shell command `ongoing_calls` to set the ongoing calls. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
Change the callback `get_ongoing_call()` of the AG from synchronous to asynchronous mode. It will help to avoid the Bluetooth host stack be blocked in the context of callback `get_ongoing_call()`. Add a function `bt_hfp_ag_ongoing_calls()` to set the ongoing calls and reply the AT command `AT+CIND?` after the callback `get_ongoing_call()` has been notified. Add a delayable worker to avoid the AT command `AT+CIND?` never being replied. After the time exceeds @kconfig{CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT}, the response of the AT command `AT+CIND?` will be replied. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
c6b67df
to
25750ee
Compare
Add shell command `auto_select_codec` to select codec automatically when codec negotiation callback is notified. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
25750ee
to
218e709
Compare
Support the case that there are some calls existed before SLC established.
Add a callback to get the ongoing calls one by one from upper layer when the response of the AT command
AT+CIND=?
from HF has been sent.And set the Call, Call Setup, and Held Call indicators and report the values int the response of AT command
AT+CIND?
. Then report all ongoing calls in the+CLCC
response.If the any value of Call, Call Setup, and Held Call indicators is not zero in the response of
AT+CIND?
, get all calls viaAT+CLCC
.ongoing_calls
Add shell command
ongoing_calls
to set the ongoing calls.get_ongoing_call()
to async modeChange the callback
get_ongoing_call()
of the AG from synchronous to asynchronous mode. It will help to avoid the Bluetooth host stack be blocked in the context of callbackget_ongoing_call()
.Add a function
bt_hfp_ag_ongoing_calls()
to set the ongoing calls and reply the AT commandAT+CIND?
after the callbackget_ongoing_call()
has been notified.Add a delayable worker to avoid the AT command
AT+CIND?
never being replied. After the time exceeds @kconfig{CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT}, the response of the AT commandAT+CIND?
will be replied.