diff --git a/src/call/call.c b/src/call/call.c index 2605f9a1..39ad82f3 100644 --- a/src/call/call.c +++ b/src/call/call.c @@ -231,12 +231,15 @@ static void call_publish_phone_status(struct UTUN_INSTANCE* inst) { } } -void call_set_phone_state(struct UTUN_INSTANCE* inst, int state, int publish, int notify) { +void call_set_phone_state(struct UTUN_INSTANCE* inst, int state, int publish, int notify, int reject) { if (!inst || state < 0 || state > 2) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "invalid phone state=%d", state); return; } - if (inst->phone_state == state && inst->phone_status_hidden == !publish && inst->phone_notify_enabled == !!notify) return; - DEBUG_INFO(DEBUG_CATEGORY_CALL, "phone state old=%u new=%d publish=%d notify=%d", inst->phone_state, state, !!publish, !!notify); + if (inst->phone_state == state && inst->phone_status_hidden == !publish && inst->phone_notify_enabled == !!notify && + inst->phone_reject_enabled == !!reject) return; + DEBUG_INFO(DEBUG_CATEGORY_CALL, "phone state old=%u new=%d publish=%d notify=%d reject=%d", + inst->phone_state, state, !!publish, !!notify, !!reject); inst->phone_state = (uint8_t)state; inst->phone_status_hidden = !publish; inst->phone_notify_enabled = !!notify; + inst->phone_reject_enabled = !!reject; call_publish_phone_status(inst); } @@ -577,6 +580,9 @@ static void call_handle_invite(struct call_ctx* ctx, uint64_t from, uint64_t gro DEBUG_WARN(DEBUG_CATEGORY_CALL, "call invite tuple/role mismatch id=%016llx", (unsigned long long)call_id); return; } if (s->state == CALL_ST_RINGING || s->state == CALL_ST_WAIT_DECISION) { + if (s->state == CALL_ST_WAIT_DECISION && ctx->inst->phone_state == 2 && ctx->inst->phone_reject_enabled) { + call_end(ctx, s, CALL_REASON_PHONE_BUSY, CALL_SUBCMD_DECLINE); return; + } if (get_time_tb() - s->started_tb >= CALL_RING_TIMEOUT_TB) { call_end(ctx, s, CALL_REASON_RING_TIMEOUT, CALL_SUBCMD_CANCEL); return; } @@ -634,6 +640,10 @@ static void call_handle_invite(struct call_ctx* ctx, uint64_t from, uint64_t gro DEBUG_ERROR(DEBUG_CATEGORY_CALL, "%s: session alloc failed id=%016llx", CALL_ID, (unsigned long long)call_id); return; } + if (ctx->inst->phone_state == 2 && ctx->inst->phone_reject_enabled) { + DEBUG_INFO(DEBUG_CATEGORY_CALL, "phone busy; incoming call rejected id=%016llx", (unsigned long long)call_id); + call_end(ctx, s, CALL_REASON_PHONE_BUSY, CALL_SUBCMD_DECLINE); return; + } s->state = ctx->inst->phone_notify_enabled && ctx->inst->phone_state == 2 ? CALL_ST_WAIT_DECISION : CALL_ST_RINGING; call_publish_phone_status(ctx->inst); DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: INVITE id=%016llx from=0x%016llx group=0x%016llx (declared group=0x%016llx)", @@ -701,7 +711,7 @@ static void call_handle_decline(struct call_ctx* ctx, uint64_t from, const uint8 if (!s || s->state == CALL_ST_ENDED) return; DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: DECLINE id=%016llx reason=%d", CALL_ID, (unsigned long long)call_id, reason); call_event_id(ctx, CHAT_EVT_CALL_DECLINED, call_id, reason); - call_end(ctx, s, CALL_REASON_DECLINE, 0); + call_end(ctx, s, reason == CALL_REASON_PHONE_BUSY ? CALL_REASON_PHONE_BUSY : CALL_REASON_DECLINE, 0); (void)from; } diff --git a/src/radio/radio_call.c b/src/radio/radio_call.c index 9e8bc947..7e6dce83 100644 --- a/src/radio/radio_call.c +++ b/src/radio/radio_call.c @@ -73,7 +73,7 @@ static void rc_schedule(struct radio_call_context* ctx) { /* Формат события: id/group/peer:8LE, state/reason/incoming:1 (27 байт). */ static void rc_event(struct radio_call_context* ctx, const struct radio_call_session* s) { static const char* states[] = {"invalid", "outgoing", "incoming", "ringing", "finished", "wait_decision"}; - static const char* reasons[] = {"none", "accepted", "declined", "busy", "cancelled", "unavailable", "no_answer", "audio_error"}; + static const char* reasons[] = {"none", "accepted", "declined", "busy", "cancelled", "unavailable", "no_answer", "audio_error", "phone_busy"}; uint8_t data[27]; memcpy(data, &s->id, 8); memcpy(data + 8, &s->group, 8); memcpy(data + 16, &s->peer, 8); data[24] = s->state; data[25] = s->reason; data[26] = s->incoming; @@ -202,7 +202,7 @@ static void rc_receive(struct ETCP_CONN* conn, struct ll_entry* entry) { uint8_t command = b[0], reason = b[17]; DEBUG_TRACE(DEBUG_CATEGORY_RADIO, "radio call RX id=%016llx group=%016llx peer=%016llx command=%u reason=%u", (unsigned long long)id, (unsigned long long)group, (unsigned long long)peer, command, reason); - if (!id || command < RC_INVITE || command > RC_FORCE || reason > RADIO_CALL_AUDIO_ERROR || + if (!id || command < RC_INVITE || command > RC_FORCE || reason > RADIO_CALL_PHONE_BUSY || ((command == RC_RESULT || command == RC_ACK) ? !reason : reason != 0)) { DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio call invalid command=%u reason=%u id=%llu", command, reason, (unsigned long long)id); goto done; } @@ -223,8 +223,10 @@ static void rc_receive(struct ETCP_CONN* conn, struct ll_entry* entry) { struct radio_call_session rejected = {.id = id, .group = group, .peer = peer}; rc_send(ctx, &rejected, RC_RESULT, RADIO_CALL_BUSY); goto done; } - s->incoming = 1; s->state = ctx->inst->phone_state == 2 ? RADIO_CALL_WAIT_DECISION : RADIO_CALL_INCOMING; + s->incoming = 1; + s->state = ctx->inst->phone_notify_enabled && ctx->inst->phone_state == 2 ? RADIO_CALL_WAIT_DECISION : RADIO_CALL_INCOMING; if (busy) rc_finish(ctx, s, RADIO_CALL_BUSY, 1); + else if (ctx->inst->phone_state == 2 && ctx->inst->phone_reject_enabled) rc_finish(ctx, s, RADIO_CALL_PHONE_BUSY, 1); else if (s->state == RADIO_CALL_WAIT_DECISION) { DEBUG_INFO(DEBUG_CATEGORY_RADIO, "phone busy; incoming radio call suppressed id=%016llx", (unsigned long long)id); rc_send(ctx, s, RC_PHONE_BUSY, 0); @@ -247,7 +249,8 @@ static void rc_receive(struct ETCP_CONN* conn, struct ll_entry* entry) { DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "radio call result acknowledged id=%016llx reason=%u", (unsigned long long)id, reason); } } else if (command == RC_RESULT) { - if (s->incoming && (reason == RADIO_CALL_ACCEPTED || reason == RADIO_CALL_DECLINED || reason == RADIO_CALL_BUSY)) { + if (s->incoming && (reason == RADIO_CALL_ACCEPTED || reason == RADIO_CALL_DECLINED || reason == RADIO_CALL_BUSY || + reason == RADIO_CALL_PHONE_BUSY)) { DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio call result invalid for callee id=%016llx reason=%u", (unsigned long long)id, reason); goto done; } if (!s->incoming && s->state == RADIO_CALL_OUTGOING && get_time_tb() - s->response_started >= RADIO_CALL_RESPONSE_TB) @@ -258,6 +261,9 @@ static void rc_receive(struct ETCP_CONN* conn, struct ll_entry* entry) { rc_finish(ctx, s, reason, 0); } else if (s->state == RADIO_CALL_FINISHED) rc_send(ctx, s, RC_RESULT, s->reason); else if ((command == RC_INVITE || command == RC_FORCE) && s->incoming) { + if (s->state == RADIO_CALL_WAIT_DECISION && ctx->inst->phone_state == 2 && ctx->inst->phone_reject_enabled) { + rc_finish(ctx, s, RADIO_CALL_PHONE_BUSY, 1); goto done; + } if (get_time_tb() - s->started >= RADIO_CALL_RING_TB) { rc_finish(ctx, s, RADIO_CALL_NO_ANSWER, 1); goto done; } s->last_peer = get_time_tb(); if (s->state == RADIO_CALL_WAIT_DECISION && command == RC_FORCE) { diff --git a/src/radio/radio_call.h b/src/radio/radio_call.h index 428ec67d..c0b0bbeb 100644 --- a/src/radio/radio_call.h +++ b/src/radio/radio_call.h @@ -19,7 +19,7 @@ enum radio_call_state { }; enum radio_call_reason { RADIO_CALL_NONE, RADIO_CALL_ACCEPTED, RADIO_CALL_DECLINED, RADIO_CALL_BUSY, - RADIO_CALL_CANCELLED, RADIO_CALL_UNAVAILABLE, RADIO_CALL_NO_ANSWER, RADIO_CALL_AUDIO_ERROR + RADIO_CALL_CANCELLED, RADIO_CALL_UNAVAILABLE, RADIO_CALL_NO_ANSWER, RADIO_CALL_AUDIO_ERROR, RADIO_CALL_PHONE_BUSY }; enum radio_call_action { RADIO_CALL_START = 1, RADIO_CALL_READY, RADIO_CALL_ACCEPT, RADIO_CALL_DECLINE,