diff --git a/src/hfp.c b/src/hfp.c index 7e7ef274e..1e2bd5eeb 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -680,7 +680,17 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ } if (strncmp(line_buffer+offset, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){ - return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; + + if (isHandsFree) return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; + + if (strncmp(line_buffer+strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)+offset, "=?", 2) == 0){ + return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; + } + if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=", 1) == 0){ + return HFP_CMD_CALL_HOLD; + } + + return HFP_CMD_UNKNOWN; } if (strncmp(line_buffer+offset, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){ diff --git a/src/hfp.h b/src/hfp.h index 649ead4e7..82c368648 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -163,6 +163,7 @@ typedef enum { HFP_CMD_AG_SUGGESTED_CODEC, HFP_CMD_HF_CONFIRMED_CODEC, HFP_CMD_CALL_ANSWERED, + HFP_CMD_CALL_HOLD, HFP_CMD_AG_ANSWER_CALL, HFP_CMD_HANG_UP_CALL, HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING, @@ -316,6 +317,8 @@ typedef enum { HFP_CALL_RINGING, HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE, HFP_CALL_ACTIVE, + HFP_CALL_W2_SEND_CALL_WAITING, + HFP_CALL_W4_CHLD, HFP_CALL_OUTGOING_INITIATED, HFP_CALL_OUTGOING_DIALING, HFP_CALL_OUTGOING_RINGING @@ -429,7 +432,6 @@ typedef struct hfp_connection { uint8_t establish_audio_connection; uint8_t release_audio_connection; - uint8_t run_call_state_machine; uint8_t change_in_band_ring_tone_setting; uint8_t ag_ring; uint8_t ag_send_clip; diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 5a0cd6eb0..b2369e5c0 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -208,11 +208,23 @@ static int hfp_ag_ring(uint16_t cid){ } static int hfp_ag_send_clip(uint16_t cid){ + if (!clip_type){ + clip_number[0] = 0; + } char buffer[50]; sprintf(buffer, "\r\n+CLIP: \"%s\",%u\r\n", clip_number, clip_type); return send_str_over_rfcomm(cid, buffer); } +static int hfp_ag_send_call_waiting_notification(uint16_t cid){ + if (!clip_type){ + clip_number[0] = 0; + } + char buffer[50]; + sprintf(buffer, "\r\n+CCWA: \"%s\",%u\r\n", clip_number, clip_type); + return send_str_over_rfcomm(cid, buffer); +} + static int hfp_ag_error(uint16_t cid){ char buffer[10]; sprintf(buffer, "\r\nERROR\r\n"); @@ -679,18 +691,26 @@ static void hfp_timeout_stop(hfp_connection_t * context){ // // only reason for this: wait for audio connection established event // -static int incoming_call_state_machine(hfp_connection_t * context){ - if (context->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; - - // we got event: audio connection established - switch (context->call_state){ +static int incoming_call_state_machine(hfp_connection_t * connection){ + switch (connection->call_state){ case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: - context->call_state = HFP_CALL_RINGING; + if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; + // we got event: audio connection established + connection->call_state = HFP_CALL_RINGING; hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0); break; case HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE: - context->call_state = HFP_CALL_ACTIVE; + if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; + // we got event: audio connection established + connection->call_state = HFP_CALL_ACTIVE; break; + case HFP_CALL_W2_SEND_CALL_WAITING: { + hfp_ag_send_call_waiting_notification(connection->rfcomm_cid); + int indicator_index = get_ag_indicator_index_for_name("callsetup"); + connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, indicator_index, 1); + connection->call_state = HFP_CALL_W4_CHLD; + break; + } default: break; } @@ -732,9 +752,11 @@ static void hfp_ag_trigger_incoming_call(void){ hfp_ag_establish_service_level_connection(connection->remote_addr); if (connection->call_state == HFP_CALL_IDLE){ connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, indicator_index, 1); - connection->run_call_state_machine = 1; hfp_ag_hf_start_ringing(connection); } + if (connection->call_state == HFP_CALL_ACTIVE){ + connection->call_state = HFP_CALL_W2_SEND_CALL_WAITING; + } hfp_run_for_context(connection); } } @@ -780,7 +802,6 @@ static void hfp_ag_hf_accept_call(hfp_connection_t * source){ connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING) continue; hfp_ag_hf_stop_ringing(connection); - connection->run_call_state_machine = 1; if (connection == source){ connection->ok_pending = 1; @@ -795,7 +816,6 @@ static void hfp_ag_hf_accept_call(hfp_connection_t * source){ connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); } else { - connection->run_call_state_machine = 0; connection->call_state = HFP_CALL_IDLE; } hfp_run_for_context(connection); @@ -814,7 +834,6 @@ static void hfp_ag_ag_accept_call(void){ if (connection->call_state != HFP_CALL_RINGING) continue; hfp_ag_hf_stop_ringing(connection); - connection->run_call_state_machine = 1; connection->call_state = HFP_CALL_TRIGGER_AUDIO_CONNECTION; connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); @@ -835,7 +854,6 @@ static void hfp_ag_trigger_reject_call(void){ connection->call_state != HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING) continue; hfp_ag_hf_stop_ringing(connection); connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); - connection->run_call_state_machine = 0; connection->call_state = HFP_CALL_IDLE; hfp_run_for_context(connection); break; // only single @@ -851,7 +869,6 @@ static void hfp_ag_trigger_terminate_call(void){ hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it); hfp_ag_establish_service_level_connection(connection->remote_addr); if (connection->call_state == HFP_CALL_IDLE) continue; - connection->run_call_state_machine = 0; connection->call_state = HFP_CALL_IDLE; connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); hfp_run_for_context(connection); @@ -904,10 +921,18 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect break; } break; - default: + case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: + switch (hfp_ag_callsetup_state){ + case HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS: + hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS); + hfp_ag_trigger_incoming_call(); + printf("TODO AG call waiting\n"); + break; + default: + break; + } break; } - break; case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG: // clear CLIP @@ -1189,6 +1214,34 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ context->ok_pending = 1; hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_HF, context); break; + case HFP_CMD_CALL_HOLD: + // TODO: fully implement this + log_error("HFP: unhandled call hold type %c", context->line_buffer[0]); + int indicator_index = get_ag_indicator_index_for_name("callsetup"); + switch (context->line_buffer[0]){ + case '0': + context->command = HFP_CMD_NONE; + context->ok_pending = 1; + hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, indicator_index, 1); + context->call_state = HFP_CALL_IDLE; + printf("AG: Call Waiting, User Busy\n"); + break; + case '1': + break; + case '2': + break; + case '3': + break; + case '4': + break; + case '?': + // handled by for feature + break; + default: + break; + } + break; case HFP_CMD_CALL_PHONE_NUMBER: context->command = HFP_CMD_NONE; hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_INITIATED, context); diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 650197353..3085746c1 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -118,8 +118,8 @@ static void show_usage(void){ printf("b - establish AUDIO connection\n"); printf("B - release AUDIO connection\n"); - printf("c - simulate incoming call\n"); - printf("C - simulate call dropped\n"); + printf("c - simulate incoming call from 1234567\n"); + printf("C - simulate call from 1234567 dropped\n"); printf("d - report AG failure\n"); @@ -149,6 +149,9 @@ static void show_usage(void){ printf("l - Clear last number\n"); printf("L - Set last number\n"); + printf("m - simulate incoming call from 7654321\n"); + // printf("M - simulate call from 7654321 dropped\n"); + printf("t - terminate connection\n"); printf("---\n"); @@ -186,10 +189,15 @@ static int stdin_process(struct data_source *ds){ hfp_ag_release_audio_connection(device_addr); break; case 'c': - printf("Simulate incoming call\n"); + printf("Simulate incoming call from 1234567\n"); hfp_ag_set_clip(129, "1234567"); hfp_ag_incoming_call(); break; + case 'm': + printf("Simulate incoming call from 7654321\n"); + hfp_ag_set_clip(129, "7654321"); + hfp_ag_incoming_call(); + break; case 'C': printf("Simulate terminate call\n"); hfp_ag_call_dropped();