Files
openbts/Control/L3TranEntry.h

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 &params);
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 &params,
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 &params,
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