mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
794 lines
32 KiB
C++
794 lines
32 KiB
C++
/**@file Declarations for TransactionTable and related classes. */
|
|
/*
|
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
|
* Copyright 2010 Kestrel Signal Processing, Inc.
|
|
* Copyright 2011, 2012, 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef L3TRANSACTIONTABLE_H
|
|
#define L3TRANSACTIONTABLE_H
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <list>
|
|
|
|
#include <Logger.h>
|
|
#include <Interthread.h>
|
|
#include <Timeval.h>
|
|
#include <Sockets.h>
|
|
|
|
|
|
#include <GSML3CommonElements.h>
|
|
#include <GSML3MMElements.h>
|
|
#include <GSML3CCElements.h>
|
|
#include <GSML3RRElements.h>
|
|
#include <SIPExport.h>
|
|
//#include <SIPBase.h>
|
|
#include "ControlTransfer.h"
|
|
#include "L3StateMachine.h"
|
|
#include "L3MobilityManagement.h"
|
|
#include "L3Utils.h"
|
|
|
|
extern int gCountTranEntry;
|
|
|
|
struct sqlite3;
|
|
|
|
|
|
/**@namespace Control This namepace is for use by the control layer. */
|
|
namespace Control {
|
|
class CSL3StateMachine;
|
|
class MachineBase;
|
|
class MMContext;
|
|
|
|
typedef std::map<std::string, GSM::Z100Timer> TimerTable; // Not used for l3rewrite
|
|
|
|
|
|
|
|
// During handover from BS1 to BS2, BS1 sends a message and BS2 stores the info here.
|
|
class HandoverEntry {
|
|
protected:
|
|
friend class TranEntry;
|
|
friend class NewTransactionTable;
|
|
|
|
TranEntryId mMyTranID; // Back pointer to TranEntry that owns this, only we use the ID not a pointer.
|
|
public: TranEntryId tranID() const { return mMyTranID; }
|
|
protected:
|
|
unsigned mHandoverOtherBSTransactionID;
|
|
|
|
public:
|
|
struct ::sockaddr_in mInboundPeer; ///< other BTS in inbound handover
|
|
// The inboundReference is generated in BS2 and placed in the L3HandoverCommand
|
|
// and sent to L1FEC to be recognized as the horeference, and is not further needed,
|
|
// but here it is anyway:
|
|
unsigned mInboundReference; ///< handover reference.
|
|
|
|
string mSipReferStr;
|
|
string mHexEncodedL3HandoverCommand;
|
|
|
|
struct ::sockaddr_in mOutboundPeer;
|
|
|
|
protected:
|
|
void initHandoverEntry(
|
|
const struct sockaddr_in* peer,
|
|
unsigned wHandoverReference,
|
|
unsigned wHandoverOtherBSTransactionID,
|
|
SimpleKeyValue ¶ms);
|
|
|
|
public:
|
|
HandoverEntry(const TranEntry*tran);
|
|
};
|
|
|
|
// Comments by pat:
|
|
// === Prior to l3write: ===
|
|
// The various Procedures (MTC, MOC, etc) were handled by functions, and the fact that
|
|
// a procedure was underway was implicit in the fact that a handler function (eg MOCController) was being run.
|
|
// The TransactionEntry creation was delayed until the IMSI was known.
|
|
// All the DCCHDispatch code is only involved in the initial message starting an L3 Procedure - after that the handler functions poll.
|
|
// The TransactionEntry was not involved in message delivery except for the responses to ImmediateAssignment, paging, and handover.
|
|
// The LocationUpdating procedure did not use a TransactionEntry at all.
|
|
// The TI [Transaction Identifiers] were simply allocated round robin, which is incorrect but at least
|
|
// reduces the risk of accidently using an active one with an MS.
|
|
|
|
// === Changes with l3rewrite: ===
|
|
// The TranEntry is always associated with a radio-link to an MS or UE, represented by an L3LogicalChannel.
|
|
// I repeat: If we have a link to a phone, we have one or more TranEntrys, which is the defining characteristic of TranEntry.
|
|
// In GSM the L3LogicalChannel is identified by association with an L2LogicalChannel (SDCCH or TCH.)
|
|
// In UMTS the L3LogicalChannel is identified by a UNRTI identity.
|
|
// (fyi, in UMTS many UE share the physical radio channel and the URNTI is used to tell them apart;
|
|
// the URNTI is assigned by the BTS to the UE the very first thing, before the UE identity is known.)
|
|
// A TranEntry is always created for each L3 procedure, which is a series of L3 messages with the phone,
|
|
// including LUR [Location Update Request.]
|
|
// In 3GPP parlance an "L3 Transaction" involves a CC or SMS transaction with an associated TI [Transaction Identifier]".
|
|
// Our TranEntry may or may not represent an "L3 Transaction" in this sense, for example, the LUR case is not an "L3 Transaction",
|
|
// and we also allocate TranEntrys for the MO case as soon as we have a channel, which is before the TI is allocated.
|
|
// A TranEntry is not allocated a TI until it is started on the MS, so queued MT transactions do not yet have a TI.
|
|
// There may be multiple TranEntrys per L3LogicalChannel.
|
|
// Specifically, the spec allows four simultaneous active L3 Transactions on the phone:
|
|
// two CS [Circuit Switched, ie voice call] Transactions (the active one and the "on-hold" one) plus one SMS transaction in each direction.
|
|
// Additional MT CS or SMS transactions are queued in the MM Context until they can be started.
|
|
// The TranEntry may optionally be associated with a SipDialog, which is created only if/when the SIP Dialog is created.
|
|
// Specifically, BTS originated SMS transactions never have a SIP Dialog, and the LUR only uses a SipDialog briefly for registration purposes.
|
|
|
|
// === Mobility Management ===
|
|
// The MM Context is used to group all the transactions occuring for a single MS, identified by IMSI,
|
|
// to support and coordinate simultaneous Transactions.
|
|
// The specs do not clearly articulate the massive differences in MobilityManagement between MT and MO transactions.
|
|
// For MT transactions, the transaction identity is by IMSI, even if a TMSI is used for communication with the phone.
|
|
// The incoming MT Transactions are queued in an MM Context until an "MM Connection"
|
|
// is established with the phone, which just means that we have a radio-link with an established IMSI identity.
|
|
// For MO transactions, including LUR, the MS identity is not initially known and the TranEntry is identified only by the channel.
|
|
// (This is true for GSM, UMTS, and even GPRS.)
|
|
// Rarely, the MS may identify itself immediately by IMSI, but usually we must run an identification and/or authorization process.
|
|
// At some point we will learn the MS identity (IMSI), at which time we can attach it to its IMSI-based MM Context, and may initiate
|
|
// transactions that are queued in the MM Context. (Except for MO-SOS calls, which may never have an IMSI identity,
|
|
// which means they may never link to an MM Context.)
|
|
// The MM Context is responsible for deciding what channel to use for a transaction, specifically, if there is already
|
|
// a channel to an MS then a queued MT transaction will try to start on that channel, otherwise page the MS.
|
|
// When a CS connection ends, if there are queued SMS transactions we are supposed to move the MS from TCH to SDCCH.
|
|
// When a radio-link is lost, the MM Context must be notified so it can clean up the SIP side of all queued MT transactions.
|
|
// NOTE: 3GPP 04.11 2.2 indicates that SMS messages use the following channels:
|
|
// TCH not allocated : use SDCCH
|
|
// TCH not allocated -> TCH allocated : SDCCH -> SACCH
|
|
// TCH allocated : SACCH
|
|
// TCH allocated -> TCH not allocated : SACCH -> SACCH opt. SDCCH3
|
|
// I believe this means when we start a call we have to recognize the new SACCH channel for the existing SMS procedures.
|
|
// It looks like SMS over GPRS uses separate transport with different messages.
|
|
|
|
// There are weird cases related to TMSIs, because in the MO case the TMSI->IMSI mapping, even if saved in the TMSI table or received from the VLR,
|
|
// is not known authoritatively until we have authenticated the phone. While we are doing any MM procedure with an MS identified by TMSI,
|
|
// any MT transactions queued in the MM Context for the IMSI associated with the TMSI must be blocked until the TMSI is resolved
|
|
// one way or the other.
|
|
// TMSI collisions are possible. For example, multiple MS could answer a page by TMSI; we could process them serially
|
|
// or conceivably we could run authorization procedures on both of them simultaneously.
|
|
// An MS that is initially identified by TMSI and subsequently fails authentication is supposed to stop using that TMSI,
|
|
// allowing other MS in the cell with the same TMSI to try answering the page.
|
|
// Reasons for TMSI collisions include reboot of the TMSI allocation authority (the VLR or SubscriberRegistry),
|
|
// or because phones roaming in have established TMSIs from a previously visited VLR with the same PLMN+LAC.
|
|
// The MS remembers its assigned TMSI *forever* in the SIM card until it is reassigned
|
|
// (or over-written by running out of memory slots to remember TMSIs as a result of visiting too many different PLMNs),
|
|
// so even in a perfect world it would still be impossible to avoid TMSI collisions.
|
|
|
|
// === State Machines ===
|
|
// Each active TranEntry is running a single L3Procedure state machine.
|
|
// L3Procedures may call others, so there is a stack of L3Procedures in the TranEntry, but only the top one is active.
|
|
// Almost all message delivery (excluding the initial response to paging and ImmediateAssignment) is done by
|
|
// funneling all messages through a dispatcher (CSL3StateMachine) which assocates the message with a TranEntry,
|
|
// and then invokes the current L3Procedure that is pointed to by that TranEntry.
|
|
// For GSM:
|
|
// The uplink messages are associated with the TranEntry by arriving on a L3LogicalChannel dedicated to that MS.
|
|
// This works because each L3LogicalChannel (SDCCH, SACCH, FACCH) is dedicated to a single MS.
|
|
// The same MS may have both a SACCH and either a FACCH or DCCH; these messages are steered to the same TransactionEntries.
|
|
// Note that there does not appear to be anything preventing an MS from allocating multiple TCH+FACCH or SDCCH,
|
|
// but we dont worry about that. I believe the current code would even work as long as the MS used the same
|
|
// LogicalChannels for all the messages associated with a particular Procedure, but we dont currently bother to check
|
|
// the IMSI to try to match up possible multiple logical channels.
|
|
// Multiple TranEntrys on the same MS are differentiated by the TI [Transaction Identifier], which was formerly just ignored.
|
|
|
|
// Destruction: Formerly TransactionEntrys were kept around a long time partly because they were also the parent of SIP session.
|
|
// Now when the radio-link to the MS is released, all TransactionEntries can be released immediately.
|
|
// The MM Context must also be deleted immediately - it is not designed to be a permanent TMSI-IMSI mapping repository.
|
|
// SIP Dialog destruction is handled separately in the SIP directory by timer.
|
|
// There are timers on the underlying L2LogicalChannel that prevent channel reuse which prevents stray messages
|
|
// from reaching L3 after a channel release.
|
|
|
|
// These variables may not be modified even by TranEntry except via the accessor methods.
|
|
class TranEntryProtected
|
|
{
|
|
// No one may set mGSMState directly, call setGSMState
|
|
CallState mGSMState; ///< the GSM/ISDN/Q.931 call state (pat) No it is not; the enum has been contaminated.
|
|
Timeval mStateTimer; ///< timestamp of last state change.
|
|
|
|
public:
|
|
CallState getGSMState() const; // (pat) This is the old call state and will eventually just go away.
|
|
void setGSMState(CallState wState);
|
|
bool isStuckOrRemoved() const;
|
|
|
|
|
|
unsigned stateAge() const { /*ScopedLock lock(mLock);*/ return mStateTimer.elapsed(); }
|
|
|
|
/** Return true if clearing is in progress in the GSM side. */
|
|
bool clearingGSM() const;
|
|
void stateText(ostream &os) const;
|
|
|
|
virtual TranEntryId tranID() const = 0; // This is our private transaction id, not the layer 3 TI mL3TI
|
|
TranEntryProtected() : mGSMState(CCState::NullState) { mStateTimer.now(); }
|
|
// (pat 6-2014) It looks like Dave added this. It would be needed to preserve the state timer
|
|
// if the TranEntry were ever copied. Is it? This may have been part of the staletranentry that was deleted.
|
|
TranEntryProtected(TranEntryProtected &old)
|
|
{
|
|
mGSMState = old.mGSMState;
|
|
mStateTimer = old.mStateTimer;
|
|
}
|
|
};
|
|
|
|
// pat added 6-2014.
|
|
// Call Data Record, created from a TranEntry.
|
|
// The information we dont know is left empty.
|
|
struct L3CDR {
|
|
string cdrType; // MOC, MTC, Emergency.
|
|
TranEntryId cdrTid;
|
|
string cdrToImsi, cdrFromImsi;
|
|
string cdrToNumber, cdrFromNumber;
|
|
string cdrPeer;
|
|
time_t cdrConnectTime;
|
|
long cdrDuration; // Connect duration, as distinct from total duration.
|
|
int cdrMessageSize; // For SMS
|
|
string cdrToHandover, cdrFromHandover;
|
|
TermCause cdrCause;
|
|
|
|
static void cdrWriteHeader(FILE *pf);
|
|
void cdrWriteEntry(FILE *pf);
|
|
};
|
|
|
|
// pat added 6-2014.
|
|
// This is sent L3CDR records. It writes them to a file.
|
|
class CdrService {
|
|
Mutex cdrLock;
|
|
InterthreadQueue<L3CDR> mCdrQueue;
|
|
Thread cdrServiceThread;
|
|
FILE *mpf;
|
|
int cdrCurrentDay;
|
|
Bool_z cdrServiceRunning;
|
|
public:
|
|
CdrService() : mpf(NULL), cdrCurrentDay(-1) {}
|
|
void cdrServiceStart();
|
|
void cdrOpenFile();
|
|
static void *cdrServiceLoop(void*);
|
|
void cdrAdd(L3CDR*cdrp) { mCdrQueue.write(cdrp); }
|
|
};
|
|
extern CdrService gCdrService;
|
|
|
|
DEFINE_MEMORY_LEAK_DETECTOR_CLASS(TranEntry,MemCheckTranEntry)
|
|
class TranEntry : public MemCheckTranEntry, public RefCntBase, public TranEntryProtected, public L3TimerList
|
|
{
|
|
friend class NewTransactionTable;
|
|
friend class MachineBase;
|
|
friend class MMContext;
|
|
//mutable Mutex mLock; ///< thread-safe control, shared from gTransactionTable
|
|
mutable HandoverEntry *mHandover; // Usually null. see getHandoverEntry()
|
|
|
|
|
|
protected:
|
|
|
|
// (pat) After l3rewrite sip-side and radio-side contention is handled by funneling all messages into a single queue.
|
|
// I am keeping a lock for safety anyway in case contention is added later, for example, by handling timers in a different thread.
|
|
// We lock the TranEntry while we are running a state machine procedure.
|
|
// Cant use the old lock without either breaking the existing code or rewriting all the accessor functions,
|
|
// so here is a new lock for the l3rewrite.
|
|
mutable Mutex mL3RewriteLock; ///< thread-safe control, shared from gTransactionTable
|
|
mutable Mutex mAnotherLock; // This one is for the few items in TranEntry that are shared between threads.
|
|
|
|
private:
|
|
/**@name Stable variables, fixed in the constructor or written only once. */
|
|
//@{
|
|
TranEntryId mID; ///< the internal transaction ID, assigned by a TransactionTable
|
|
|
|
// (pat) Even though this is of type L3MobileIdentity, it is the subscriber id used for SIP messages,
|
|
// and currently it must be an IMSI. It is not something sent to the MS.
|
|
FullMobileId mSubscriber; ///< some kind of subscriber ID, preferably IMSI
|
|
private:
|
|
GSM::L3CMServiceType mService; ///< the associated service type
|
|
|
|
// 24.007 11.2.3.1.3: The TI [Transaction Identifier] is 3 bits plus a flag.
|
|
// The flag is 0 when it belongs to a transaction initiated by its sender, else 1.
|
|
// The same TI can be used simultaneously in both direction, distinguished by the flag.
|
|
// The TI 7 is reserved for an extension mechanism which only applies to certain protocols (ie, Call Control)
|
|
// and is an error otherwise, but we should not use that value.
|
|
// The value of the flag we store here is 0 if we initiated the transaction, 1 if MS initiated.
|
|
unsigned mL3TI; ///< the L3 short transaction ID, the version we *send* to the MS
|
|
static const unsigned cL3TIInvalid = 16; // valid values are 0-7
|
|
|
|
GSM::L3CalledPartyBCDNumber mCalled; ///< the associated called party number, if known
|
|
GSM::L3CallingPartyBCDNumber mCalling; ///< the associated calling party number, if known
|
|
|
|
CodecSet mCodecs; // (pat) This comment is wrong: This is for the MS, saved from the L3Setup message.
|
|
public: CodecSet getCodecs() { return mCodecs; }
|
|
public: void setCodecs(CodecSet wCodecs) { mCodecs = wCodecs; }
|
|
|
|
public:
|
|
InterthreadQueue<SIP::DialogMessage> mTranInbox;
|
|
// SIP Message processing is blocked during the AssignTCHF procedure.
|
|
//Bool_z mSipDialogMessagesBlocked;
|
|
std::string mMessage; ///< text message payload
|
|
std::string mContentType; ///< text message payload content type
|
|
//@}
|
|
|
|
|
|
TermCause mFinalDisposition; // How transaction ended.
|
|
|
|
|
|
|
|
|
|
|
|
// (pat) In v1 (Pre-l3-rewrite), this was a value, not a pointer.
|
|
// In v2, the TranEntry represents a connection to an MS,
|
|
// and the SipDialog is a decoupled separate structure managed in the SIP directory.
|
|
// The TranEntry usually becomes tied to a SipDialog
|
|
// when the SipDialog is started, but some transactions
|
|
// (notably MTSMS initiated from the BTS) dont ever have a SipDialog.
|
|
// SipDialog and TranEntry are decoupled, so running all
|
|
// the SIP messages through TranEntry is no longer necessary.
|
|
// In the near future, SipDialog will survive past the end of the
|
|
// transaction and we will be able to destroy TranEntrys
|
|
// immediately upon loss of radio link.
|
|
private:
|
|
SIP::SipDialogRef mDialog; // (pat) post-l3-rewrite only.
|
|
|
|
SIP::SipDialog *getDialog() { return mDialog.self(); }
|
|
SIP::SipDialog *getDialog() const { return mDialog.self(); } // grrr
|
|
|
|
public:
|
|
void setDialog(SIP::SipDialog *dialog); // Also passed through to here from MachineBase
|
|
void teCloseDialog(TermCause cause);
|
|
|
|
private:
|
|
mutable SIP::SipState mPrevSipState; ///< previous SIP state, prior to most recent transactions
|
|
|
|
unsigned mNumSQLTries; ///< number of SQL tries for DB operations
|
|
|
|
MMContext *mContext;
|
|
// For MO Transactions the Context is set at construction.
|
|
// For MT Transactions the Transacton sits in a MMUser until it is started on a channel,
|
|
// and the Context is set then. This is used by MMContext.
|
|
friend class MMUser;
|
|
protected: void teSetContext(MMContext *wContext) { mContext = wContext; }
|
|
|
|
private:
|
|
|
|
L3Cause::AnyCause mTerminationRequested;
|
|
|
|
public: // But only used by MobilityManagement routines.
|
|
// TODO: Maybe this should move into the MMContext.
|
|
MMSharedData *mMMData;
|
|
|
|
// Constructor Methodology:
|
|
// The actual C++ constructors are private, which means they may not be used directly
|
|
// to create TranEntrys. You must use one of the static constructor methods below.
|
|
private:
|
|
void TranEntryInit(); // Basic initialization for TranEntry.
|
|
|
|
TranEntry(
|
|
SIP::SipDialog *wDialog,
|
|
//const GSM::L3MobileIdentity& wSubscriber,
|
|
const GSM::L3CMServiceType& wService);
|
|
|
|
private:
|
|
static TranEntry *newMO(MMContext *wChan, const GSM::L3CMServiceType& wService);
|
|
public:
|
|
static TranEntry *newMOSMS(MMContext* wChannel);
|
|
static TranEntry *newMOC(MMContext* wChannel, L3CMServiceType::TypeCode serviceType);
|
|
static TranEntry *newMOC(MMContext* wChannel);
|
|
static TranEntry *newMOMM(MMContext* wChannel);
|
|
static TranEntry *newMOSSD(MMContext* wChannel);
|
|
|
|
|
|
// This is the post-l3-rewrite
|
|
static TranEntry *newMTC(
|
|
SIP::SipDialog *wDialog,
|
|
const FullMobileId& msid,
|
|
const GSM::L3CMServiceType& wService, // MobileTerminatedCall or UndefinedType for generic page from CLI.
|
|
string wCallerId);
|
|
|
|
static TranEntry *newMTSMS(
|
|
SIP::SipDialog *dialog,
|
|
const FullMobileId& msid,
|
|
const GSM::L3CallingPartyBCDNumber& wCalling,
|
|
string smsBody,
|
|
string smsContentType);
|
|
|
|
|
|
static TranEntry *newHandover(const struct sockaddr_in* peer,
|
|
unsigned wHandoverReference,
|
|
SimpleKeyValue ¶ms,
|
|
L3LogicalChannel *wChannel,
|
|
unsigned wHandoverOtherBSTransactionID);
|
|
|
|
/** Form used for handover requests; argument is taken from the message string. */
|
|
/** unused
|
|
TranEntry(const struct ::sockaddr_in* peer,
|
|
unsigned wHandoverReference,
|
|
SimpleKeyValue ¶ms,
|
|
const char *proxy,
|
|
L3LogicalChannel* wChannel,
|
|
unsigned otherTransactionID);
|
|
**/
|
|
|
|
|
|
/** Set the outbound handover parameters and set the state to HandoverOutbound. */
|
|
void setOutboundHandover(
|
|
const GSM::L3HandoverReference& reference,
|
|
const GSM::L3CellDescription& cell,
|
|
const GSM::L3ChannelDescription2& chan,
|
|
const GSM::L3PowerCommandAndAccessType& pwrCmd,
|
|
const GSM::L3SynchronizationIndication& synch
|
|
);
|
|
|
|
/** Set the inbound handover parameters on the channel; state should alread be HandoverInbound. */
|
|
//void setInboundHandover(float wRSSI, float wTimingError);
|
|
|
|
|
|
/** Delete the database entry upon destruction. */
|
|
~TranEntry();
|
|
|
|
/**@name Accessors. */
|
|
//@{
|
|
unsigned getL3TI() const;
|
|
void setL3TI(unsigned wL3TI);
|
|
bool matchL3TI(unsigned ti, bool fromMS); // Does this ti match this transaction?
|
|
bool isL3TIValid() const {
|
|
return getL3TI() != TranEntry::cL3TIInvalid;
|
|
}
|
|
|
|
const L3LogicalChannel* channel() const;
|
|
L3LogicalChannel* channel();
|
|
GSM::L2LogicalChannel* getL2Channel() const;
|
|
L3LogicalChannel *getTCHFACCH(); // return channel but make sure it is a TCH/FACCH.
|
|
|
|
//void setChannel(L3LogicalChannel* wChannel);
|
|
|
|
FullMobileId& subscriber() { return mSubscriber; }
|
|
void setSubscriberImsi(string imsi, bool andAttach);
|
|
string subscriberIMSI() { return mSubscriber.mImsi; }
|
|
|
|
//const GSM::L3CMServiceType& service() const { return mService; }
|
|
const GSM::L3CMServiceType service() const { return mService; }
|
|
GSM::CMServiceTypeCode servicetype() const { return service().type(); }
|
|
|
|
const GSM::L3CalledPartyBCDNumber& called() const { return mCalled; }
|
|
void setCalled(const GSM::L3CalledPartyBCDNumber&);
|
|
|
|
const GSM::L3CallingPartyBCDNumber& calling() const { return mCalling; }
|
|
|
|
//const char* message() const { return mMessage.c_str(); }
|
|
//void message(const char *wMessage, size_t length);
|
|
//const char* messageType() const { return mContentType.c_str(); }
|
|
//void messageType(const char *wContentType);
|
|
|
|
TranEntryId tranID() const { return mID; }
|
|
|
|
// FIXME: This is where we need to do choose the codec from the ones in the SDP message
|
|
// the capabilities of the MS.
|
|
CodecSet chooseCodec() {
|
|
return CodecSet(GSM_FR); // punt for the moment.
|
|
}
|
|
|
|
//@}
|
|
|
|
L3Cause::AnyCause terminationRequested();
|
|
|
|
/**@name SIP-side operations */
|
|
//@{
|
|
|
|
//unused SIP::SipState getSipState() const;
|
|
unsigned getRTPPort() const;
|
|
|
|
|
|
// Obviously, these are only for TransactionEntries for voice calls.
|
|
void txFrame(SIP::AudioFrame* frame, unsigned numFlushed);
|
|
SIP::AudioFrame *rxFrame();
|
|
|
|
//@}
|
|
|
|
/** Retrns true if the transaction is "dead". */
|
|
//bool teDead() const;
|
|
|
|
/** Returns true if dead, or if removal already requested. */
|
|
bool deadOrRemoved() const;
|
|
|
|
/** Dump information as text for debugging. */
|
|
void text(std::ostream&) const;
|
|
string text() const;
|
|
|
|
/** Genrate an encoded string for handovers. */
|
|
std::string handoverString(string peer,string cause) const;
|
|
|
|
private:
|
|
|
|
/** Set up a new entry in gTransactionTable's sqlite3 database. */
|
|
//void insertIntoDatabase();
|
|
|
|
/** Run a database query. */
|
|
void runQuery(const char* query) const;
|
|
|
|
/** Echo latest SipState to the database. */
|
|
SIP::SipState echoSipState(SIP::SipState state) const;
|
|
|
|
// (pat) This is genuine removal, and partial cleanup if the TranEntry is dirty.
|
|
|
|
/** Removal status. */
|
|
private: void teRemove(TermCause cause); // Keep private. Dont call from Procedures. Call teCloseCall or teCancel or chanClose instead.
|
|
public:
|
|
void teCancel(TermCause cause) { teRemove(cause); } // Used from MM. Cancel dialog and delete transaction, do not notify MM layer. Used within MMLayer.
|
|
void teCloseCallNow(TermCause cause, bool sendCause);
|
|
// If the channel ends being released, this is the cause.
|
|
|
|
////////////////////////////////// l3rwrite ///////////////////////////////////////////
|
|
// (pat) l3rewrite stuff:
|
|
//MachineBase *mCurrentProcedure; // TODO: Delete in destructor.
|
|
//MachineBase *currentProcedure() const { return mCurrentProcedure; }
|
|
private:
|
|
list<MachineBase*> mProcStack;
|
|
MachineBase *tePopMachine();
|
|
void tePushProcedure(MachineBase *);
|
|
public:
|
|
MachineBase *currentProcedure() const { return mProcStack.size() ? mProcStack.back() : NULL; }
|
|
|
|
TranEntry *tran() { return this; } // virtual callback for class ProcCommon
|
|
|
|
void teSetProcedure(MachineBase *wProc, bool wDeleteCurrent=false);
|
|
MachineStatus handleRecursion(MachineStatus status);
|
|
bool handleMachineStatus(MachineStatus status);
|
|
|
|
// Start the specified Procedure. If it was invoked because of a L3Message, include it.
|
|
//void teStartProcedure(MachineBase *wProc, const GSM::L3Message *l3msg=0);
|
|
|
|
// Send a message to the current Procedure, either l3msg or lch.
|
|
// lch is the channel this message arrived on. It is information we have, but I dont think it is useful.
|
|
// I wonder if there are any cases where lch may not be the L3LogicalChannel that initiated the Procedure?
|
|
// It probably doesnt matter - we use the L3LogicalChannel to send return messages to the MS,
|
|
// and the initial channel that created the Procedure is probably the correct one.
|
|
// For example if lch is FACCH, we cannot send anything downstrem on that.
|
|
public:
|
|
// Update: We dont need to lock these in GSM because each channel runs in a separate thread.
|
|
bool lockAndInvokeL3Msg(const GSM::L3Message *l3msg /*, const L3LogicalChannel *lch*/);
|
|
bool lockAndInvokeFrame(const L3Frame *frame, const L3Message *l3msg);
|
|
bool lockAndInvokeSipMsg(const SIP::DialogMessage *sipmsg);
|
|
bool lockAndInvokeSipMsgs();
|
|
bool lockAndStart(MachineBase *wProc=NULL);
|
|
bool lockAndStart(MachineBase *wProc, GSM::L3Message *l3msg);
|
|
//bool teInvokeState(unsigned state);
|
|
//bool teInvokeStart(MachineBase *proc);
|
|
bool lockAndInvokeTimeout(L3Timer *timer);
|
|
void terminateHook();
|
|
MMContext *teGetContext() { return mContext; }
|
|
HandoverEntry *getHandoverEntry(bool create) const; // Creates if necessary.
|
|
TranEntry *unconst() const { return const_cast<TranEntry*>(this); }
|
|
|
|
// Is this handset ever communicated with us?
|
|
bool teIsTalking();
|
|
|
|
// The following block is used to contain the fields from the TRANSACTION_TABLE that are useful for statistics
|
|
public:
|
|
time_t mStartTime; // transaction creation time.
|
|
time_t mConnectTime; // When call is connected, or 0 if never.
|
|
L3CDR *createCDR(bool makeCMR, TermCause cause);
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TranEntry&);
|
|
std::ostream& operator<<(std::ostream& os, const TranEntry*);
|
|
|
|
|
|
|
|
/** A map of transactions keyed by ID. */
|
|
class NewTransactionMap : public std::map<TranEntryId,TranEntry*> {};
|
|
|
|
/**
|
|
A table for tracking the states of active transactions.
|
|
*/
|
|
class NewTransactionTable {
|
|
friend class TranEntry;
|
|
friend class CSL3StateMachine;
|
|
|
|
private:
|
|
|
|
#if EXTERNAL_TRANSACTION_TABLE
|
|
sqlite3 *mDB; ///< database connection
|
|
#endif
|
|
|
|
NewTransactionMap mTable;
|
|
mutable Mutex mttLock;
|
|
unsigned mIDCounter;
|
|
|
|
public:
|
|
/**
|
|
Initialize a transaction table.
|
|
@param path Path fto sqlite3 database file.
|
|
*/
|
|
void ttInit();
|
|
|
|
#if EXTERNAL_TRANSACTION_TABLE
|
|
NewTransactionTable() : mDB(0) {} // (pat) Make sure no garbage creeps in.
|
|
~NewTransactionTable();
|
|
#endif
|
|
|
|
/**
|
|
Return a new ID for use in the table.
|
|
*/
|
|
unsigned ttNewID();
|
|
|
|
/**
|
|
Insert a new entry into the table; deleted by the table later.
|
|
@param value The entry to insert into the table; will be deleted by the table later.
|
|
*/
|
|
void ttAdd(TranEntry* value);
|
|
|
|
/**
|
|
Find an entry and return a pointer into the table.
|
|
@param wID The TransactioEntry:mID to search
|
|
@return NULL if ID is not found or was dead
|
|
*/
|
|
TranEntry* ttFindById(TranEntryId wID);
|
|
bool ttIsTalking(TranEntryId tranid);
|
|
// (pat) This is a temporary routine to return the state asynchronously.
|
|
// Should be replaced by using a RefCntPointer in the NewTransactionTable and returning that.
|
|
CallState ttGetGSMStateById(TranEntryId wId);
|
|
bool ttIsDialogReleased(TranEntryId wID);
|
|
bool ttSetDialog(TranEntryId tid, SIP::SipDialog *dialog); // Unlike TranEntry::setDialog(), this can be used from other directories, other threads.
|
|
|
|
// (pat added)
|
|
// unused: TranEntry* ttFindByDialog(SIP::SipDialog *psip);
|
|
void ttAddMessage(TranEntryId tranid,SIP::DialogMessage *sipmsg);
|
|
// (pat added)
|
|
//TranEntry *ttFindByL3Msg(GSM::L3Message *l3msg, L3LogicalChannel *lch);
|
|
|
|
/**
|
|
Find the longest-running non-SOS call.
|
|
@return NULL if there are no calls or if all are SOS.
|
|
*/
|
|
TranEntryId findLongestCall();
|
|
|
|
/**
|
|
Return the availability of this particular RTP port
|
|
@return True if Port is available, False otherwise
|
|
*/
|
|
bool RTPAvailable(unsigned rtpPort);
|
|
|
|
/**
|
|
Fand an entry by its handover reference.
|
|
@param ref The 8-bit handover reference.
|
|
@return NULL if ID is not found or was dead
|
|
*/
|
|
TranEntry* ttFindByInboundHandoverRef(unsigned ref);
|
|
|
|
/**
|
|
Remove an entry from the table and from gSIPMessageMap.
|
|
@param wID The transaction ID to search.
|
|
@return True if the ID was really in the table and deleted.
|
|
*/
|
|
bool ttRemove(unsigned wID);
|
|
|
|
bool ttTerminate(TranEntryId tid, L3Cause::BSSCause cause);
|
|
|
|
|
|
//bool remove(TranEntry* transaction) { return remove(transaction->tranID()); }
|
|
|
|
/**
|
|
Remove an entry from the table and from gSIPMessageMap,
|
|
if it is in the Paging state.
|
|
@param wID The transaction ID to search.
|
|
@return True if the ID was really in the table and deleted.
|
|
*/
|
|
//bool removePaging(unsigned wID);
|
|
|
|
|
|
/**
|
|
Find an entry by its channel pointer; returns first entry found.
|
|
Also clears dead entries during search.
|
|
@param chan The channel pointer.
|
|
@return pointer to entry or NULL if no active match
|
|
*/
|
|
//TranEntry* ttFindByLCH(const L3LogicalChannel *chan);
|
|
|
|
// The channel died. Clean up all the TransactionEntries. This is a mobility management function.
|
|
//void ttLostChannel(const L3LogicalChannel *chan);
|
|
|
|
/** Find a transaction in the HandoverInbound state on the given channel. */
|
|
//TranEntry* ttFindByInboundHandoverChan(const L3LogicalChannel *chan);
|
|
|
|
/**
|
|
Find an entry by its SACCH channel pointer; returns first entry found.
|
|
Also clears dead entries during search.
|
|
@param chan The channel pointer.
|
|
@return pointer to entry or NULL if no active match
|
|
*/
|
|
//TranEntry* ttFindBySACCH(const GSM::SACCHLogicalChannel *chan);
|
|
|
|
/**
|
|
Find an entry by its channel type and offset.
|
|
Also clears dead entries during search.
|
|
@param chan The channel pointer to the first record found.
|
|
@return pointer to entry or NULL if no active match
|
|
*/
|
|
// (pat) unused, removed.
|
|
//TranEntry* ttFindByTypeAndOffset(GSM::TypeAndOffset chanDesc);
|
|
|
|
/**
|
|
Find an entry in the given state by its mobile ID.
|
|
Also clears dead entries during search.
|
|
@param mobileID The mobile to search for.
|
|
@return pointer to entry or NULL if no match
|
|
*/
|
|
// (pat) This one is unused too.
|
|
// TranEntry* ttFindByMobileIDState(const GSM::L3MobileIdentity& mobileID, CallState state);
|
|
|
|
/** Return true if there is an ongoing call for this user. */
|
|
//bool isBusy(const GSM::L3MobileIdentity& mobileID);
|
|
|
|
|
|
/** Find by subscriber and SIP call ID. */
|
|
//TranEntry* ttFindBySIPCallId(const GSM::L3MobileIdentity& mobileID, const char* callID);
|
|
|
|
/** Find by subscriber and handover other BS transaction ID. */
|
|
TranEntry* ttFindHandoverOther(const GSM::L3MobileIdentity& mobileID, unsigned transactionID);
|
|
|
|
/** Check for duplicated SMS delivery attempts. */
|
|
//bool duplicateMessage(const GSM::L3MobileIdentity& mobileID, const std::string& wMessage);
|
|
|
|
|
|
/**
|
|
Find the channel, if any, used for current transactions by this mobile ID.
|
|
@param mobileID The target mobile subscriber.
|
|
@return pointer to TCH/FACCH, SDCCH or NULL.
|
|
*/
|
|
//L3LogicalChannel* findChannel(const GSM::L3MobileIdentity& mobileID);
|
|
|
|
/** Count the number of transactions using a particular channel. */
|
|
//unsigned countChan(const L3LogicalChannel*);
|
|
|
|
size_t size() { ScopedLock lock(mttLock); return mTable.size(); }
|
|
|
|
size_t dump(std::ostream& os, bool showAll=false) const;
|
|
|
|
/** Generate a unique handover reference. */
|
|
//unsigned generateInboundHandoverReference(TranEntry* transaction);
|
|
|
|
private:
|
|
|
|
|
|
#if EXTERNAL_TRANSACTION_TABLE
|
|
/** Accessor to database connection. */
|
|
sqlite3* getDB() { return mDB; }
|
|
#endif
|
|
|
|
/**
|
|
Remove "dead" entries from the table.
|
|
A "dead" entry is a transaction that is no longer active.
|
|
The caller should hold mLock.
|
|
*/
|
|
//void clearDeadEntries();
|
|
|
|
/**
|
|
Remove and entry from the table and from gSIPInterface.
|
|
*/
|
|
//void innerRemove(TransactionMap::iterator);
|
|
|
|
|
|
/** Check to see if a given outbound handover reference is in use. */
|
|
//bool outboundReferenceUsed(unsigned ref);
|
|
|
|
// pat added - create an L3TI that is not currently in use.
|
|
// The gTMSITable.nextL3TI(wSubscriber.digits()) is just wrong - if anything it should be a bit-mask
|
|
// of the TIs that are currently in use.
|
|
// I dont think it needs to be in the TMSI table at all - if the BTS crashes all transactions are dead
|
|
// so we can start over with TIs.
|
|
//unsigned makeNewTI() {
|
|
//}
|
|
};
|
|
|
|
|
|
extern void TranInit();
|
|
|
|
}; // Control namespace
|
|
|
|
|
|
|
|
#endif
|
|
|
|
// vim: ts=4 sw=4
|
|
|
|
|