mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-04 05:43:14 +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
 | 
						|
 | 
						|
 |