call.cpp 16 KB
Newer Older
yanmorin's avatar
yanmorin committed
1
/*
2
 *  Copyright (C) 2004-2017 Savoir-faire Linux Inc.
3
 *
jpbl's avatar
jpbl committed
4
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
5 6
 *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
 *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
yanmorin's avatar
yanmorin committed
7
 *
jpbl's avatar
jpbl committed
8 9
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
11
 *  (at your option) any later version.
yanmorin's avatar
yanmorin committed
12
 *
jpbl's avatar
jpbl committed
13 14 15 16
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
yanmorin's avatar
yanmorin committed
17
 *
jpbl's avatar
jpbl committed
18 19
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
jpbl's avatar
jpbl committed
21
 */
22

jpbl's avatar
jpbl committed
23
#include "call.h"
24
#include "account.h"
25
#include "manager.h"
26
#include "audio/ringbufferpool.h"
27
#include "dring/call_const.h"
28
#include "client/ring_signal.h"
29
#include "audio/audiorecord.h"
30 31
#include "sip/sip_utils.h"
#include "ip_utils.h"
32
#include "array_size.h"
33 34
#include "map_utils.h"
#include "call_factory.h"
35
#include "string_utils.h"
36
#include "enumclass_utils.h"
37

38 39
#include "errno.h"

40 41
namespace ring {

42
Call::Call(Account& account, const std::string& id, Call::CallType type)
Adrien Béraud's avatar
Adrien Béraud committed
43
    : id_(id)
44
    , type_(type)
45
    , account_(account)
46 47
{
    time(&timestamp_start_);
48
    account_.attachCall(id_);
49
}
jpbl's avatar
jpbl committed
50

Julien Bonjean's avatar
Julien Bonjean committed
51
Call::~Call()
52 53 54
{
    account_.detachCall(id_);
}
jpbl's avatar
jpbl committed
55

56 57 58
void
Call::removeCall()
{
59
    auto this_ = shared_from_this();
60
    Manager::instance().callFactory.removeCall(*this);
61
    iceTransport_.reset();
62
    setState(CallState::OVER);
Loïc Siret's avatar
Loïc Siret committed
63
    recAudio_->closeFile();
64 65 66 67
}

const std::string&
Call::getAccountId() const
68 69 70 71
{
    return account_.getAccountID();
}

yanmorin's avatar
yanmorin committed
72
Call::ConnectionState
73
Call::getConnectionState() const
Julien Bonjean's avatar
Julien Bonjean committed
74
{
75
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
76
    return connectionState_;
jpbl's avatar
jpbl committed
77 78
}

79 80 81 82 83 84 85
Call::CallState
Call::getState() const
{
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return callState_;
}

86
bool
87
Call::validStateTransition(CallState newState)
88
{
89 90 91
    // Notice to developper:
    // - list only permitted transition (return true)
    // - let non permitted ones as default case (return false)
92 93 94 95 96

    // always permited
    if (newState == CallState::OVER)
        return true;

97
    switch (callState_) {
98
        case CallState::INACTIVE:
99
            switch (newState) {
100 101 102
                case CallState::ACTIVE:
                case CallState::BUSY:
                case CallState::MERROR:
103
                    return true;
104 105
                default: // INACTIVE, HOLD
                    return false;
106 107
            }

108
        case CallState::ACTIVE:
109
            switch (newState) {
110
                case CallState::HOLD:
111
                case CallState::MERROR:
112
                    return true;
113
                default: // INACTIVE, ACTIVE, BUSY
114 115 116
                    return false;
            }

117
        case CallState::HOLD:
118
            switch (newState) {
119
                case CallState::ACTIVE:
120 121 122 123 124 125 126 127 128
                case CallState::MERROR:
                    return true;
                default: // INACTIVE, HOLD, BUSY, MERROR
                    return false;
            }

        case CallState::BUSY:
            switch (newState) {
                case CallState::MERROR:
129
                    return true;
130
                default: // INACTIVE, ACTIVE, HOLD, BUSY
131 132 133
                    return false;
            }

134
        default: // MERROR
135 136 137 138 139
            return false;
    }
}

bool
140
Call::setState(CallState call_state, ConnectionState cnx_state, signed code)
Julien Bonjean's avatar
Julien Bonjean committed
141
{
142 143
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    RING_DBG("[call:%s] state change %u/%u, cnx %u/%u, code %d", id_.c_str(),
144 145
             (unsigned)callState_, (unsigned)call_state, (unsigned)connectionState_,
             (unsigned)cnx_state, code);
146 147 148 149

    if (callState_ != call_state) {
        if (not validStateTransition(call_state)) {
            RING_ERR("[call:%s] invalid call state transition from %u to %u",
150
                     id_.c_str(), (unsigned)callState_, (unsigned)call_state);
151 152 153 154 155 156 157 158 159 160 161
            return false;
        }
    } else if (connectionState_ == cnx_state)
        return true; // no changes as no-op

    // Emit client state only if changed
    auto old_client_state = getStateStr();
    callState_ = call_state;
    connectionState_ = cnx_state;
    auto new_client_state = getStateStr();

162
    if (call_state == CallState::OVER) {
163
        RING_DBG("[call:%s] %lu subcalls %lu listeners", id_.c_str(), subcalls.size(), stateChangedListeners_.size());
164 165 166 167
        if (not subcalls.empty()) {
            auto subs = std::move(subcalls);
            for (auto c : subs)
                c->hangup(0);
168 169
            pendingInMessages_.clear();
            pendingOutMessages_.clear();
170
        }
171
    } else if (call_state == CallState::ACTIVE and connectionState_ == ConnectionState::CONNECTED and not pendingOutMessages_.empty()) {
172 173 174
        for (const auto& msg : pendingOutMessages_)
            sendTextMessage(msg.first, msg.second);
        pendingOutMessages_.clear();
175 176 177 178 179
    }

    for (auto& l : stateChangedListeners_)
        l(callState_, connectionState_, code);

180
    if (old_client_state != new_client_state) {
181 182 183 184 185
        if (not quiet) {
            RING_DBG("[call:%s] emit client call state change %s, code %d",
                     id_.c_str(), new_client_state.c_str(), code);
            emitSignal<DRing::CallSignal::StateChange>(id_, new_client_state, code);
        }
186 187 188
    }

    return true;
jpbl's avatar
jpbl committed
189 190
}

191 192
bool
Call::setState(CallState call_state, signed code)
Julien Bonjean's avatar
Julien Bonjean committed
193
{
194 195 196 197 198 199 200 201 202
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return setState(call_state, connectionState_, code);
}

bool
Call::setState(ConnectionState cnx_state, signed code)
{
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return setState(callState_, cnx_state, code);
jpbl's avatar
jpbl committed
203 204
}

205
std::string
206
Call::getStateStr() const
Julien Bonjean's avatar
Julien Bonjean committed
207
{
208 209
    using namespace DRing::Call;

210
    switch (getState()) {
211
        case CallState::ACTIVE:
212
            switch (getConnectionState()) {
213 214 215
                case ConnectionState::PROGRESSING:
                    return StateEvent::CONNECTING;

216
                case ConnectionState::RINGING:
217 218 219 220 221
                    return isIncoming() ? StateEvent::INCOMING : StateEvent::RINGING;

                case ConnectionState::DISCONNECTED:
                    return StateEvent::HUNGUP;

222
                case ConnectionState::CONNECTED:
223
                default:
224
                    return StateEvent::CURRENT;
225 226
            }

227
        case CallState::HOLD:
228 229
            if(getConnectionState() == ConnectionState::DISCONNECTED)
                return StateEvent::HUNGUP;
230 231
            return StateEvent::HOLD;

232
        case CallState::BUSY:
233
            return StateEvent::BUSY;
234

235
        case CallState::INACTIVE:
236
            switch (getConnectionState()) {
237 238 239
                case ConnectionState::PROGRESSING:
                    return StateEvent::CONNECTING;

240
                case ConnectionState::RINGING:
241 242
                    return isIncoming() ? StateEvent::INCOMING : StateEvent::RINGING;

243
                case ConnectionState::CONNECTED:
244 245
                    return StateEvent::CURRENT;

246
                default:
247
                    return StateEvent::INACTIVE;
248 249
            }

250 251 252
        case CallState::OVER:
            return StateEvent::OVER;

253
        case CallState::MERROR:
254
        default:
255
            return StateEvent::FAILURE;
256 257 258
    }
}

Adrien Béraud's avatar
Adrien Béraud committed
259
IpAddr
260
Call::getLocalIp() const
Julien Bonjean's avatar
Julien Bonjean committed
261
{
262
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
Adrien Béraud's avatar
Adrien Béraud committed
263
    return localAddr_;
264 265
}

266
unsigned int
267
Call::getLocalAudioPort() const
Julien Bonjean's avatar
Julien Bonjean committed
268
{
269
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
270
    return localAudioPort_;
271 272
}

273
unsigned int
274
Call::getLocalVideoPort() const
275
{
276
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
277
    return localVideoPort_;
278 279
}

280
bool
281
Call::toggleRecording()
Julien Bonjean's avatar
Julien Bonjean committed
282
{
283
    const bool startRecording = Recordable::toggleRecording();
284
    std::string process_id = Recordable::recAudio_->getRecorderID();
285
    RingBufferPool &rbPool = Manager::instance().getRingBufferPool();
286

287
    if (startRecording) {
288
        rbPool.bindHalfDuplexOut(process_id, id_);
289
        rbPool.bindHalfDuplexOut(process_id, RingBufferPool::DEFAULT_ID);
290
    } else {
291
        rbPool.unBindHalfDuplexOut(process_id, id_);
292
        rbPool.unBindHalfDuplexOut(process_id, RingBufferPool::DEFAULT_ID);
293 294
    }

295
    return startRecording;
296
}
297

298
std::map<std::string, std::string>
299
Call::getDetails() const
300
{
301
    return {
302
        {DRing::Call::Details::CALL_TYPE,        ring::to_string((unsigned)type_)},
303
        {DRing::Call::Details::PEER_NUMBER,      peerNumber_},
304
        {DRing::Call::Details::DISPLAY_NAME,     peerDisplayName_},
305 306 307 308
        {DRing::Call::Details::CALL_STATE,       getStateStr()},
        {DRing::Call::Details::CONF_ID,          confID_},
        {DRing::Call::Details::TIMESTAMP_START,  ring::to_string(timestamp_start_)},
        {DRing::Call::Details::ACCOUNTID,        getAccountId()},
309 310
        {DRing::Call::Details::AUDIO_MUTED,      std::string(bool_to_str(isAudioMuted_))},
        {DRing::Call::Details::VIDEO_MUTED,      std::string(bool_to_str(isVideoMuted_))},
311
    };
312 313 314 315 316
}

std::map<std::string, std::string>
Call::getNullDetails()
{
317 318 319
    return {
        {DRing::Call::Details::CALL_TYPE,        "0"},
        {DRing::Call::Details::PEER_NUMBER,      ""},
320
        {DRing::Call::Details::DISPLAY_NAME,     ""},
321 322 323 324
        {DRing::Call::Details::CALL_STATE,       "UNKNOWN"},
        {DRing::Call::Details::CONF_ID,          ""},
        {DRing::Call::Details::TIMESTAMP_START,  ""},
        {DRing::Call::Details::ACCOUNTID,        ""},
325
        {DRing::Call::Details::VIDEO_SOURCE,     "UNKNOWN"},
326
    };
327
}
328

329
bool
330 331 332
Call::initIceTransport(bool master, unsigned channel_num)
{
    auto& iceTransportFactory = Manager::instance().getIceTransportFactory();
333 334 335
    iceTransport_ = iceTransportFactory.createTransport(getCallId().c_str(),
                                                        channel_num, master,
                                                        account_.getIceOptions());
336
    return static_cast<bool>(iceTransport_);
337 338 339 340 341
}

int
Call::waitForIceInitialization(unsigned timeout)
{
342
    return iceTransport_->waitForInitialization(timeout);
343 344 345 346 347
}

int
Call::waitForIceNegotiation(unsigned timeout)
{
348
    return iceTransport_->waitForNegotiation(timeout);
349 350 351 352 353
}

bool
Call::isIceUsed() const
{
354
    return iceTransport_ and iceTransport_->isInitialized();
355 356 357 358 359
}

bool
Call::isIceRunning() const
{
360
    return iceTransport_ and iceTransport_->isRunning();
361 362
}

363 364
std::unique_ptr<IceSocket>
Call::newIceSocket(unsigned compId)
365
{
366
    return std::unique_ptr<IceSocket> {new IceSocket(iceTransport_, compId)};
367
}
368

369 370 371 372
void
Call::onTextMessage(std::map<std::string, std::string>&& messages)
{
    if (quiet)
373
        pendingInMessages_.emplace_back(std::move(messages), "");
374 375 376 377
    else
        Manager::instance().incomingMessage(getCallId(), getPeerNumber(), messages);
}

378 379 380 381 382 383 384 385 386
void
Call::peerHungup()
{
    const auto state = getState();
    const auto aborted = state == CallState::ACTIVE or state == CallState::HOLD;
    setState(ConnectionState::DISCONNECTED,
             aborted ? ECONNABORTED : ECONNREFUSED);
}

387 388 389 390 391 392 393 394 395 396 397 398
void
Call::addSubCall(const std::shared_ptr<Call>& call)
{
    if (connectionState_ == ConnectionState::CONNECTED
           || callState_ == CallState::ACTIVE
           || callState_ == CallState::OVER) {
        call->removeCall();
    } else {
        std::lock_guard<std::recursive_mutex> lk (callMutex_);
        if (not subcalls.emplace(call).second)
            return;
        call->quiet = true;
399

400
        for (auto& pmsg : pendingOutMessages_)
401 402
            call->sendTextMessage(pmsg.first, pmsg.second);

403 404
        std::weak_ptr<Call> wthis = shared_from_this();
        std::weak_ptr<Call> wcall = call;
405 406 407
        call->addStateListener([wcall, wthis](Call::CallState new_state,
                                              Call::ConnectionState new_cstate,
                                              UNUSED int code) {
408 409 410 411 412 413
            if (auto call = wcall.lock()) {
                if (auto sthis = wthis.lock()) {
                    auto& this_ = *sthis;
                    auto sit = this_.subcalls.find(call);
                    if (sit == this_.subcalls.end())
                        return;
414 415 416
                    RING_WARN("[call:%s] DeviceCall call %s state changed %d %d",
                              this_.getCallId().c_str(), call->getCallId().c_str(),
                              static_cast<int>(new_state), static_cast<int>(new_cstate));
417 418 419 420 421 422
                    if (new_state == CallState::OVER) {
                        std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
                        this_.subcalls.erase(call);
                    } else if (new_state == CallState::ACTIVE && this_.callState_ == CallState::INACTIVE) {
                        this_.setState(new_state);
                    }
423
                    if ((unsigned)this_.connectionState_ < (unsigned)new_cstate && (unsigned)new_cstate <= (unsigned)ConnectionState::RINGING) {
424 425 426
                        this_.setState(new_cstate);
                    } else if (new_cstate == ConnectionState::DISCONNECTED && new_state == CallState::ACTIVE) {
                        std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
427
                        RING_WARN("[call:%s] peer hangup", this_.getCallId().c_str());
428 429 430 431 432 433
                        auto subcalls = std::move(this_.subcalls);
                        for (auto& sub : subcalls) {
                            if (sub != call)
                                try {
                                    sub->hangup(0);
                                } catch(const std::exception& e) {
434 435
                                    RING_WARN("[call:%s] error hanging up: %s",
                                              this_.getCallId().c_str(), e.what());
436 437 438 439 440 441
                                }
                        }
                        this_.peerHungup();
                    }
                    if (new_state == CallState::ACTIVE && new_cstate == ConnectionState::CONNECTED) {
                        std::lock_guard<std::recursive_mutex> lk (this_.callMutex_);
442
                        RING_WARN("[call:%s] peer answer", this_.getCallId().c_str());
443 444 445 446 447 448
                        auto subcalls = std::move(this_.subcalls);
                        for (auto& sub : subcalls) {
                            if (sub != call)
                                sub->hangup(0);
                        }
                        this_.merge(call);
449
                        Manager::instance().peerAnsweredCall(this_);
450
                    }
451 452
                    RING_WARN("[call:%s] Remaining %zu subcalls", this_.getCallId().c_str(),
                              this_.subcalls.size());
453
                    if (this_.subcalls.empty())
454
                        this_.pendingOutMessages_.clear();
455
                } else {
456 457 458
                    RING_WARN("DeviceCall IGNORED call %s state changed %d %d",
                              call->getCallId().c_str(), static_cast<int>(new_state),
                              static_cast<int>(new_cstate));
459 460 461 462 463 464 465 466
                }
            }
        });
        setState(ConnectionState::TRYING);
    }
}

void
467
Call::merge(const std::shared_ptr<Call>& scall)
468
{
469
    RING_WARN("[call:%s] merge to -> [call:%s]", scall->getCallId().c_str(), getCallId().c_str());
470 471 472 473
    auto& call = *scall;
    std::lock(callMutex_, call.callMutex_);
    std::lock_guard<std::recursive_mutex> lk1 (callMutex_, std::adopt_lock);
    std::lock_guard<std::recursive_mutex> lk2 (call.callMutex_, std::adopt_lock);
474
    auto pendingInMessages = std::move(call.pendingInMessages_);
475
    iceTransport_ = std::move(call.iceTransport_);
476 477
    if (peerNumber_.empty())
        peerNumber_ = std::move(call.peerNumber_);
478 479 480 481 482 483
    peerDisplayName_ = std::move(call.peerDisplayName_);
    localAddr_ = call.localAddr_;
    localAudioPort_ = call.localAudioPort_;
    localVideoPort_ = call.localVideoPort_;
    setState(call.getState());
    setState(call.getConnectionState());
484
    for (const auto& msg : pendingInMessages)
485
        Manager::instance().incomingMessage(getCallId(), getPeerNumber(), msg.first);
486
    scall->removeCall();
487 488 489
}


490
} // namespace ring