diff -r 22de2e391156 -r 20ac952a623c remotecontrol/remotecontrolfw/server/src/server.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotecontrol/remotecontrolfw/server/src/server.cpp Wed Oct 13 16:20:29 2010 +0300 @@ -0,0 +1,3826 @@ +// Copyright (c) 2004-2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// Remote Control server implementation. +// +// + +/** + @file + @internalComponent +*/ + +#include +#include +#include +#include +#include +#include "server.h" +#include "targetclientprocess.h" +#include "controllersession.h" +#include "targetsession.h" +#include "serversecuritypolicy.h" +#include "utils.h" +#include "bearermanager.h" +#include "messagequeue.h" +#include "convertermanager.h" +#include "remconmessage.h" +#include "connections.h" +#include "connectionhistory.h" +#include "messagerecipients.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_REMCON_SERVER); +#endif + +PANICCATEGORY("server"); + +#ifdef __FLOG_ACTIVE +#define LOGCONTROLLERSESSIONS LogControllerSessions() +#define LOGTARGETSESSIONS LogTargetSessions() +#define LOGREMOTES LogRemotes() +#define LOGCONNECTIONHISTORYANDINTEREST LogConnectionHistoryAndInterest() +#define LOGOUTGOINGCMDPENDINGTSP LogOutgoingCmdPendingTsp() +#define LOGOUTGOINGNOTIFYCMDPENDINGTSP LogOutgoingNotifyCmdPendingTsp() +#define LOGOUTGOINGRSPPENDINGTSP LogOutgoingRspPendingTsp() +#define LOGOUTGOINGPENDINGSEND LogOutgoingPendingSend() +#define LOGOUTGOINGSENT LogOutgoingSent() +#define LOGINCOMINGCMDPENDINGADDRESS LogIncomingCmdPendingAddress() +#define LOGINCOMINGNOTIFYCMDPENDINGADDRESS LogIncomingNotifyCmdPendingAddress() +#define LOGINCOMINGNOTIFYCMDPENDINGREADDRESS LogIncomingNotifyCmdPendingReAddress() +#define LOGINCOMINGPENDINGDELIVERY LogIncomingPendingDelivery() +#define LOGINCOMINGDELIVERED LogIncomingDelivered() +#else +#define LOGCONTROLLERSESSIONS +#define LOGTARGETSESSIONS +#define LOGREMOTES +#define LOGCONNECTIONHISTORYANDINTEREST +#define LOGOUTGOINGCMDPENDINGTSP +#define LOGOUTGOINGNOTIFYCMDPENDINGTSP +#define LOGOUTGOINGRSPPENDINGTSP +#define LOGOUTGOINGPENDINGSEND +#define LOGOUTGOINGSENT +#define LOGINCOMINGCMDPENDINGADDRESS +#define LOGINCOMINGNOTIFYCMDPENDINGADDRESS +#define LOGINCOMINGNOTIFYCMDPENDINGREADDRESS +#define LOGINCOMINGPENDINGDELIVERY +#define LOGINCOMINGDELIVERED +#endif // __FLOG_ACTIVE + +TInt BulkMain(TAny* aParam); +TBool ControllerSessionCompare(const TUint* aSessionId, const CRemConControllerSession& aSession) + { + return *aSessionId == aSession.Id(); + } + +TBool TargetClientCompareUsingSessionId(const TUint* aClientId, const CRemConTargetClientProcess& aClient) + { + return *aClientId == aClient.Id(); + } + +TBool TargetClientCompareUsingProcessId(const TProcessId* aProcessId, const CRemConTargetClientProcess& aClient) + { + return *aProcessId == aClient.ClientInfo().ProcessId(); + } + +CRemConServer* CRemConServer::NewLC() + { + LOG_STATIC_FUNC; + CRemConServer* self = new(ELeave) CRemConServer(); + CleanupStack::PushL(self); + // StartL is where the kernel checks that there isn't already an instance + // of the same server running, so do it before ConstructL. + self->StartL(KRemConServerName); + self->ConstructL(); + return self; + } + +CRemConServer::~CRemConServer() + { + LOG_FUNC; + + delete iBearerManager; + delete iShutdownTimer; + + // There should be no watcher as there should be no bulk thread running + ASSERT_DEBUG(!iBulkThreadWatcher); + + iControllerSessions.Close(); + + iTargetClientsLock.Wait(); + iTargetClients.Close(); + iTargetClientsLock.Close(); + + // Destroy TSP before iIncomingPendingAddress in case the TSP is + // addressing a message on it at the time. + // The TSP should not be handling outgoing commands or responses as all the sessions + // have gone, and they clean up their outgoing messages when they close. + ASSERT_DEBUG(!iTspHandlingOutgoingCommand); + ASSERT_DEBUG(!iTspHandlingOutgoingResponse); + ASSERT_DEBUG(!iTspHandlingOutgoingNotifyCommand); + // We can't assert anything about iTspAddressingIncomingCommand- it isn't + // interesting. + delete iTsp; + + delete iOutgoingCmdPendingTsp; + delete iOutgoingNotifyCmdPendingTsp; + delete iOutgoingRspPendingTsp; + delete iOutgoingRspPendingSend; + delete iOutgoingPendingSend; + delete iOutgoingSent; + delete iIncomingCmdPendingAddress; + delete iIncomingNotifyCmdPendingAddress; + delete iIncomingNotifyCmdPendingReAddress; + delete iIncomingPendingDelivery; + delete iIncomingDelivered; + + delete iMessageRecipientsList; + + iTspConnections.Reset(); + iTspIncomingCmdClients.Reset(); + iTspIncomingNotifyCmdClients.Reset(); + + delete iConverterManager; + + // Clean up the connection information (must be done after the bearer + // manager is destroyed). + LOGREMOTES; + LOGCONNECTIONHISTORYANDINTEREST; + delete iConnectionHistory; + + iSession2ConnHistory.Close(); + + // This is the odd ECOM code for cleaning our session. NB This must be + // done AFTER destroying all the other things in this thread which use + // ECOM! + if ( iEcom ) + { + iEcom->Close(); + } + REComSession::FinalClose(); + } + +CRemConServer::CRemConServer() + : CPolicyServer(CActive::EPriorityHigh, KRemConServerPolicy), + iTspConnections(_FOFF(TRemConAddress, iLink)), + iTspIncomingCmdClients(_FOFF(TClientInfo, iLink)), + iTspIncomingNotifyCmdClients(_FOFF(TClientInfo, iLink2)), + iTspIf4Stub(*this) + { + LOG_FUNC; + // NB CRemConServer uses CActive::EPriorityHigh to help it get priority + // over other AOs in its thread. (The fact that it's added to the AS + // before anything else helps too.) This is so that client requests are + // not blocked by other AOs in the thread being very busy. This relies of + // course on the cooperation of the other AOs running in RemCon's thread + // over which we have no control (e.g. those in externally-supplied + // bearers or the Target Selector Plugin). In Symbian OS, it's the best we + // can do. + + // This is needed for BC reasons, as the TUint32 padding in TClientInfo is now replaced with + // a second TSglQueLink. Therefore in order to maintain BC we need these two classes + // to be the same size. + __ASSERT_COMPILE(sizeof(TUint32) == sizeof(TSglQueLink)); + } + +void CRemConServer::ConstructL() + { + LOG_FUNC; + // Open ECOM session. + iEcom = &(REComSession::OpenL()); + LEAVEIFERRORL(iTargetClientsLock.CreateLocal()); + + iShutdownTimer = CPeriodic::NewL(CActive::EPriorityStandard); + + // Make the connection history holder before creating the bearer manager, + // as some bearers might call the bearer manager back synchronously with a + // new connection, and we need iConnectionHistory to be able to handle + // that. + iConnectionHistory = CConnectionHistory::NewL(); + + // Make the queues before making the bearer manager because otherwise a + // 'connection up' which is indicated synchronously with + // CBearerManager::NewL will blow us up (we address some of the queues + // when that happens). + iOutgoingCmdPendingTsp = CMessageQueue::NewL(); + iOutgoingNotifyCmdPendingTsp = CMessageQueue::NewL(); + iOutgoingRspPendingTsp = CMessageQueue::NewL(); + iOutgoingRspPendingSend = CMessageQueue::NewL(); + iOutgoingPendingSend = CMessageQueue::NewL(); + iOutgoingSent = CMessageQueue::NewL(); + iIncomingCmdPendingAddress = CMessageQueue::NewL(); + iIncomingNotifyCmdPendingAddress = CMessageQueue::NewL(); + iIncomingNotifyCmdPendingReAddress = CMessageQueue::NewL(); + iIncomingPendingDelivery = CMessageQueue::NewL(); + iIncomingDelivered = CMessageQueue::NewL(); + + iMessageRecipientsList = CMessageRecipientsList::NewL(); + + // Make bearer manager. This makes the bearers, and connects them up to + // the event handler. + iBearerManager = CBearerManager::NewL(*this); + + // We must load the bearers before the TSP as the TSP loader checks + // whether there are any bearers with interface V2 + ASSERT_ALWAYS(!iTspIf); + + iConverterManager = CConverterManager::NewL(); + + LoadTspL(); + + LOGREMOTES; + LOGCONNECTIONHISTORYANDINTEREST; + } + +CSession2* CRemConServer::NewSessionL(const TVersion& aVersion, + const RMessage2& aMessage) const + { + LOG(KNullDesC8); + LOG_FUNC; + LOG3(_L("\taVersion = (%d,%d,%d)"), aVersion.iMajor, aVersion.iMinor, aVersion.iBuild); + + // Version number check... + if ( aVersion.iMajor != KRemConSrvMajorVersionNumber || aVersion.iMinor != KRemConSrvMinorVersionNumber ) + { + LEAVEIFERRORL(KErrNotSupported); + } + + // We need a non-const copy of ourself so that: + // (a) New target clients may be added to iTargetClients (controller sessions are added to + // iControllerSessions when ControllerClientOpened() is invoked). + // (b) We may provide new sessions/clients with a non-const pointer to ourself. + CRemConServer* ncThis = const_cast(this); + + CRemConSession* sess = NULL; + ASSERT_DEBUG(iBearerManager); + + TInt err = KErrNotSupported; + if (aVersion.iBuild == KRemConSrvControllerSession) + { + TRAP(err, sess = ncThis->CreateControllerSessionL(aMessage)); + } + else if(aVersion.iBuild == KRemConSrvTargetSession) + { + TRAP(err, sess = ncThis->CreateTargetSessionL(aMessage)); + } + + if ( err != KErrNone) + { + // Session creation might have failed- if it has we need to check if + // we need to shut down again. + const_cast(this)->StartShutdownTimerIfNoClientsOrBulkThread(); + LEAVEIFERRORL(err); + } + + LOG1(_L("\tsess = 0x%08x"), sess); + return sess; + } + +CRemConControllerSession* CRemConServer::CreateControllerSessionL(const RMessage2& aMessage) + { + LOG_FUNC; + + CRemConControllerSession* sess=NULL; + + // Obtain client process ID. + TClientInfo clientInfo; + ClientProcessAndSecureIdL(clientInfo, aMessage); + + // Create the session and return + sess = CRemConControllerSession::NewL(*this, *iBearerManager, clientInfo, iSessionOrClientId++); + return sess; + } + +CRemConTargetSession* CRemConServer::CreateTargetSessionL(const RMessage2& aMessage) + { + LOG_FUNC; + + CRemConTargetSession* sess=NULL; + + // Obtain client process ID and search for clients server-side process representation. + TClientInfo clientInfo; + ClientProcessAndSecureIdL(clientInfo, aMessage); + + // We search for the client ourselves here as we need to know where it + // is in the array if we end up needing to destroy it. + iTargetClientsLock.Wait(); + CleanupSignalPushL(iTargetClientsLock); + + TInt clientIndex = iTargetClients.Find(clientInfo.ProcessId(), TargetClientCompareUsingProcessId); + + if (clientIndex == KErrNotFound) + { + // Client is new, create process representation for client and add to iTargetClients + CRemConTargetClientProcess* newClient = CRemConTargetClientProcess::NewLC(clientInfo, iSessionOrClientId++, *this, *iBearerManager); + + iTargetClients.AppendL(newClient); + + // Set clientIndex to point to the new client. + clientIndex = iTargetClients.Count()-1; + + CleanupStack::Pop(newClient); + } + + // Create session on client and return. + // On error, remove the clients process representation if client has no other sessions. + TRAPD(err, sess = iTargetClients[clientIndex]->NewSessionL(iSessionOrClientId++)); + if (err) + { + TryToDropClientProcess(clientIndex); + LEAVEL(err); + } + + CleanupStack::PopAndDestroy(&iTargetClientsLock); + + return sess; + } + +void CRemConServer::ClientProcessAndSecureIdL(TClientInfo& aClientInfo, const RMessage2& aMessage) const + { + LOG_FUNC; + + RThread thread; + LEAVEIFERRORL(aMessage.Client(thread)); + CleanupClosePushL(thread); + RProcess process; + LEAVEIFERRORL(thread.Process(process)); + aClientInfo.ProcessId() = process.Id(); + process.Close(); + aClientInfo.SecureId() = thread.SecureId(); + CleanupStack::PopAndDestroy(&thread); + } + +void CRemConServer::StartShutdownTimerIfNoClientsOrBulkThread() + { + LOG_FUNC; + iTargetClientsLock.Wait(); + if ( iControllerSessions.Count() == 0 && iTargetClients.Count() == 0 && !iBulkThreadOpen) + { + LOG(_L("\tno remaining sessions- starting shutdown timer")); + // Should have been created during our construction. + ASSERT_DEBUG(iShutdownTimer); + // Start the shutdown timer. It's actually a CPeriodic- the first + // event will be in KShutdownDelay microseconds' time. + // NB The shutdown timer might already be active, in the following + // case: this function is being called by NewSessionL because there + // was a failure creating a new session, BUT this function had already + // been called by the session's destructor (i.e. the failure was in + // the session's ConstructL, NOT its new(ELeave)). To protect against + // KERN-EXEC 15 just check for if the timer is already active. + if ( !iShutdownTimer->IsActive() ) + { + iShutdownTimer->Start(KShutdownDelay, + // Delay of subsequent firings (will not happen because we kill + // ourselves after the first). + 0, + TCallBack(CRemConServer::TimerFired, this) + ); + } + else + { + LOG(_L("\tshutdown timer was already active")); + } + } + iTargetClientsLock.Signal(); + } + +void CRemConServer::CancelShutdownTimer() + { + // Should have been created during our construction. + ASSERT_DEBUG(iShutdownTimer); + iShutdownTimer->Cancel(); + } + +void CRemConServer::TryToDropClientProcess(TUint aClientIndex) + { + ASSERT_DEBUG(iTargetClients.Count() > aClientIndex); + + iTargetClientsLock.Wait(); + + CRemConTargetClientProcess* client = iTargetClients[aClientIndex]; + if (client->TargetSessionCount() == 0) + { + // No sessions held by this client process representation, OK to destroy. + // The destructor of CRemConTargetClientProcess will call back to us, after which we will + // handle the client close. + delete client; + } + + iTargetClientsLock.Signal(); + } + +TInt CRemConServer::TimerFired(TAny* aThis) + { + LOG_STATIC_FUNC; + static_cast(aThis); + +#if defined(__FLOG_ACTIVE) || defined(_DEBUG) + CRemConServer* self = static_cast(aThis); + // We should have sent 'this' to this callback. + ASSERT_DEBUG(self); + LOG1(_L8("\tauto shutdown- terminating the server [0x%08x]"), self); +#endif // __FLOG_ACTIVE || _DEBUG + + // Stop our Active Scheduler. This returns the flow of execution to after + // the CActiveScheduler::Start call in the server startup code, and + // terminates the server. + CActiveScheduler::Stop(); + + return KErrNone; + } + +void CRemConServer::InitialiseBulkServerThreadL() + { + LOG_FUNC; + // Set up the configuration of the thread + iBulkServerThread.SetPriority(EPriorityLess); + + iBulkThreadWatcher = new(ELeave) CBulkThreadWatcher(*this); + CleanupDeleteAndNullPushL(iBulkThreadWatcher); + + // Create the communication between the servers. + LEAVEIFERRORL(iBulkServerMsgQueue.CreateLocal(2)); // only ever two outstanding messages + CleanupClosePushL(iBulkServerMsgQueue); // member variable, but closing again should be fine. + + // Load the server pointer (for TClientInfo info) + TBulkServerMsg ctrlMsg; + ctrlMsg.iType = EControlServer; + ctrlMsg.iData = this; + iBulkServerMsgQueue.SendBlocking(ctrlMsg); // this should not block as there is enough room in queue. + + // Load the bearer manager pointer + TBulkServerMsg manMsg; + manMsg.iType = EBearerManager; + manMsg.iData = iBearerManager; + iBulkServerMsgQueue.SendBlocking(manMsg); // this should not block as there is enough room in queue. + + TRequestStatus stat; + iBulkServerThread.Rendezvous(stat); + + // Mark bulk server thread ready to run + iBulkServerThread.Resume(); + User::WaitForRequest(stat); // wait for start or death + + // we can't use the 'exit reason' if the server panicked as this + // is the panic 'reason' and may be '0' which cannot be distinguished + // from KErrNone + TInt err = (iBulkServerThread.ExitType() == EExitPanic) ? KErrServerTerminated : stat.Int(); + LEAVEIFERRORL(err); + + // Everything appears to be running...so watch the thread until it dies... + iBulkThreadWatcher->StartL(); + + CleanupStack::Pop(2, &iBulkThreadWatcher); // iBulkServerMsgQueue, iBulkThreadWatcher + } + + +TInt CRemConServer::BulkServerRequired() + { + LOG_FUNC; + // If the bulk server is required then try to create it + TThreadFunction bulkServerThreadFunction(BulkMain); + _LIT(KBulkServerThreadName, "RemConBulkServerThread"); + const TInt KMaxBulkServerThreadHeapSize = 0x100000; + + TInt err = KErrNone; + if(!iBulkThreadOpen) + { + err = iBulkServerThread.Create(KBulkServerThreadName, bulkServerThreadFunction, KDefaultStackSize, KMinHeapSize, KMaxBulkServerThreadHeapSize, &iBulkServerMsgQueue, EOwnerThread); + if(err == KErrNone) + { + TRAP(err, InitialiseBulkServerThreadL()); + if(err == KErrNone) + { + iBulkThreadOpen = ETrue; + } + } + } + return err; + } + +TInt CRemConServer::ControllerClientOpened(CRemConControllerSession& aSession) + { + LOG_FUNC; + LOG1(_L("\t&aSession = 0x%08x"), &aSession); + LOGCONTROLLERSESSIONS; + + // Register the session by appending it to our array, and also making an + // item for it in the record of which points in the connection history + // sessions are interested in. + TInt ret = iControllerSessions.Append(&aSession); + if ( ret == KErrNone ) + { + TSessionPointerToConnectionHistory item; + item.iSessionId = aSession.Id(); + item.iIndex = 0; // there is always at least one item in the connection history + ret = iSession2ConnHistory.Append(item); + if ( ret != KErrNone ) + { + iControllerSessions.Remove(iControllerSessions.Count() - 1); + } + } + + if ( ret == KErrNone ) + { + // Should have been created during our construction. + ASSERT_DEBUG(iShutdownTimer); + iShutdownTimer->Cancel(); + } + + LOGCONTROLLERSESSIONS; + LOG1(_L("\tret = %d"), ret); + return ret; + } + +TInt CRemConServer::RegisterTargetSessionPointerToConnHistory(const CRemConTargetSession& aSession) + { + LOG_FUNC; + + TSessionPointerToConnectionHistory item; + item.iSessionId = aSession.Id(); + item.iIndex = 0; // there is always at least one item in the connection history + TInt ret = iSession2ConnHistory.Append(item); + + return ret; + } + + +// this function is called by the client when it has registered its features +void CRemConServer::TargetClientAvailable(const CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOG1(_L("\t&aClient = 0x%08x"), &aClient); + LOGTARGETSESSIONS; + + ASSERT_DEBUG(iBearerManager); + iBearerManager->TargetClientAvailable(aClient.Id(), aClient.PlayerType(), aClient.PlayerSubType(), aClient.Name()); + if(iTspIf5) + { + iTspIf5->TargetClientAvailable(aClient.ClientInfo()); + } + + LOGTARGETSESSIONS; + } + +void CRemConServer::TargetFeaturesUpdated(CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOG1(_L("\t&aClient = 0x%08x"), &aClient); + LOGTARGETSESSIONS; + + ASSERT_DEBUG(iBearerManager); + iBearerManager->TargetFeaturesUpdated(aClient.Id(), aClient.PlayerType(), aClient.PlayerSubType(), aClient.Name()); + + LOGTARGETSESSIONS; + } + +// this function is called by the session when the client has registered its features +void CRemConServer::ControllerClientAvailable() + { + LOG_FUNC; + LOGCONTROLLERSESSIONS; + + ASSERT_DEBUG(iBearerManager); + iBearerManager->ControllerClientAvailable(); + + LOGCONTROLLERSESSIONS; + } + +// this function is called by the session when it goes connection oriented +void CRemConServer::ClientGoConnectionOriented(CRemConControllerSession& aSession, TUid aUid) + { + LOG_FUNC; + LOG1(_L("\t&aSession = 0x%08x"), &aSession); + LOGCONTROLLERSESSIONS; + + (void)&aSession; // get rid of unused warning. We keep the param to enforce usage only + // by controller sessions + + /* now tell the bearer manager that someones went connection oriented + The bearer manager maintains controller and target counts for all bearers + and will tell bearers when they need to know things have changed */ + ASSERT_DEBUG(iBearerManager); + iBearerManager->ClientConnectionOriented(aUid); + + LOGCONTROLLERSESSIONS; + } + +// this is called by the session when the client goes connectionless +void CRemConServer::ClientGoConnectionless(CRemConControllerSession& aSession, TUid aUid) + { + LOG_FUNC; + LOG1(_L("\t&aSession = 0x%08x"), &aSession); + LOGCONTROLLERSESSIONS; + + (void)&aSession; // get rid of unused warning. We keep the param to enforce usage only + // by controller sessions + + /* now tell the bearer manager that someones went connection less + The bearer manager maintains controller and target counts for all bearers + and will tell bearers when they need to know things have changed */ + ASSERT_DEBUG(iBearerManager); + iBearerManager->ClientConnectionless(aUid); + + LOGCONTROLLERSESSIONS; + } + +// called by controller session when closed. +void CRemConServer::ControllerClientClosed(CRemConControllerSession& aSession, TUid aUid) + { + LOG_FUNC; + LOG1(_L("\t&aSession = 0x%08x"), &aSession); + LOGCONTROLLERSESSIONS; + + // Find this session in the array and remove it (if it's there). + TInt index = iControllerSessions.Find(&aSession); + if(index >= 0) + { + // We've found the session in our array. + // 1. Remove the session from our array. + iControllerSessions.Remove(index); + + // 2. Tell the bearers about the session going away, if it was the + // last controller. + // If the session hasn't already set its type, then it doesn't + // count (we won't have told the bearers about it to begin with). + // The bearer manager maintains controller and target counts for all bearers + // and will tell bearers when they need to know things have changed + if (aSession.ClientAvailable()) + { + ASSERT_DEBUG(iBearerManager); + iBearerManager->ClientClosed(ETrue, aUid, aSession.Id()); + } + + // 3. Remove queued messages belonging to this session that: + // (a) are outgoing, awaiting access to the TSP + // (OutgoingPendingTsp, OutgoingNotifyPendingTsp), + // (b) are outgoing, awaiting a bearer connection + // (OutgoingPendingSend), + // (c) have been sent (OutgoingSent) + // (d) are pending delivery to this session + // (IncomingPendingDelivery) + + // (3)(a) Outgoing, waiting access to the TSP: + TSglQueIter& cmdIter = OutgoingCmdPendingTsp().SetToFirst(); + CRemConMessage* msg; + TBool first = ETrue; + while ( ( msg = cmdIter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // If the message is currently being worked on by the + // TSP, cancel the TSP before destroying it. + if ( iTspHandlingOutgoingCommand && first ) + { + ASSERT_DEBUG(iTspIf); + iTspIf->CancelOutgoingCommand(); + iTspHandlingOutgoingCommand = EFalse; + } + OutgoingCmdPendingTsp().RemoveAndDestroy(*msg); + } + first = EFalse; + } + + cmdIter = OutgoingNotifyCmdPendingTsp().SetToFirst(); + first = ETrue; + while ( ( msg = cmdIter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // If the message is currently being worked on by the + // TSP, cancel the TSP before destroying it. + if ( iTspHandlingOutgoingNotifyCommand && first ) + { + ASSERT_DEBUG(iTspIf3); + iTspIf3->CancelOutgoingNotifyCommand(); + iTspHandlingOutgoingNotifyCommand = EFalse; + } + OutgoingNotifyCmdPendingTsp().RemoveAndDestroy(*msg); + } + first = EFalse; + } + + // (3)(b) Outgoing, awaiting a bearer connection: + TSglQueIter& sendIter = OutgoingPendingSend().SetToFirst(); + while ( ( msg = sendIter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // Only commands are sent by controllers + ASSERT_DEBUG(msg->MsgType() == ERemConCommand || msg->MsgType() == ERemConNotifyCommand); + OutgoingPendingSend().RemoveAndDestroy(*msg); + } + } + + // (3)(c) Have been sent: + OutgoingSent().RemoveAndDestroy(aSession.Id()); + + // (3)(d) Are pending delivery to this session: + TSglQueIter& pendingDeliveryIter = IncomingPendingDelivery().SetToFirst(); + while ( ( msg = pendingDeliveryIter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // Only responses or rejects are received by controllers + ASSERT_DEBUG(msg->MsgType() == ERemConResponse || msg->MsgType() == ERemConReject); + IncomingPendingDelivery().RemoveAndDestroy(*msg); + } + } + + // (3)(e) Have been delivered to this session and are awaiting responses: + TSglQueIter& deliveredIter = IncomingDelivered().SetToFirst(); + while ( ( msg = deliveredIter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // Only responses or rejects are received by controllers + ASSERT_DEBUG(msg->MsgType() == ERemConResponse || msg->MsgType() == ERemConReject); + IncomingDelivered().RemoveAndDestroy(*msg); + } + } + } + + // Also remove its record from the connection history record. + RemoveSessionFromConnHistory(aSession); + + StartShutdownTimerIfNoClientsOrBulkThread(); + + LOGCONTROLLERSESSIONS; + } + +/** +Called by CRemConTargetClientProcess when a session is closing. +We have some work to do here as we need to remove the messages pertaining to +that session. +**/ +void CRemConServer::TargetSessionClosed(CRemConTargetClientProcess& aClient, CRemConTargetSession& aSession) + { + LOG_FUNC; + LOG1(_L("\t&aSession = 0x%08x"), &aSession); + LOGTARGETSESSIONS; + + iTargetClientsLock.Wait(); + + // Find the client in our array (required for later removal) + TInt clientIndex = iTargetClients.Find(aClient.ClientInfo().ProcessId(), TargetClientCompareUsingProcessId); + + // We should always find the client. + ASSERT_DEBUG(clientIndex > KErrNotFound); + + // 1. Remove queued messages belonging to this session that: + // (a) are outgoing, awaiting access to the TSP + // (OutgoingRspPendingTsp) + // (b) are outgoing, awaiting a bearer connection + // (OutgoingPendingSend), + // (c) have been sent (OutgoingSent) + // (d) are pending delivery to this session + // (IncomingPendingDelivery) + // (e) have been delivered to this session and are awaiting + // responses (IncomingDelivered). + + // (1)(a) Outgoing, awaiting access to the TSP + // First remove the client pertaining to this session from the message recipients list. + ASSERT_DEBUG(iMessageRecipientsList); + TSglQueIter& messageRecipientsIter = iMessageRecipientsList->Iter(); + + messageRecipientsIter.SetToFirst(); + CMessageRecipients* message; + while ((message = messageRecipientsIter++) != NULL) + { + // First we need to find the message - it could be in + // OutgoingRspPendingTsp, IncomingDelivered or IncomingPendingDelivery + CRemConMessage* msg; + msg = OutgoingRspPendingTsp().Message(message->TransactionId()); + + if (!msg) + { + msg = IncomingDelivered().Message(message->TransactionId()); + } + if (!msg) + { + msg = IncomingPendingDelivery().Message(message->TransactionId()); + } + + if(msg) + { + // Try to remove this client from the message (this does nothing if we were not a recipient). + message->RemoveAndDestroyClient(aSession.ClientInfo()); + if (message->Clients().IsEmpty()) + { + iMessageRecipientsList->Messages().Remove(*message); + // Inform bearer that it won't be getting a response + SendReject(msg->Addr(), msg->InterfaceUid(), msg->OperationId(), msg->TransactionId()); + + delete message; + } + } + } + + TSglQueIter& rspIter = OutgoingRspPendingTsp().SetToFirst(); + CRemConMessage* msg; + TBool first = ETrue; + while ( ( msg = rspIter++ ) != NULL ) + { + if ( aClient.Id() == msg->SessionId() && aSession.SupportedMessage(*msg) ) + { + // If the message is currently being worked on by the + // TSP, cancel the TSP before destroying it. + if (iTspIf2 && iTspHandlingOutgoingResponse && first ) + { + iTspIf2->CancelOutgoingResponse(); + iTspHandlingOutgoingResponse = EFalse; + } + OutgoingRspPendingTsp().RemoveAndDestroy(*msg); + } + first = EFalse; + } + + // (1)(b) Outgoing, awaiting a bearer connection: + TSglQueIter& sendIter = OutgoingPendingSend().SetToFirst(); + while ( ( msg = sendIter++ ) != NULL ) + { + if ( aClient.Id() == msg->SessionId() && aSession.SupportedMessage(*msg) ) + { + if (msg->MsgType() == ERemConResponse) + { + SendReject(msg->Addr(), msg->InterfaceUid(), msg->OperationId(), msg->TransactionId()); + } + OutgoingPendingSend().RemoveAndDestroy(*msg); + } + } + + // (1)(c) Have been sent: + TSglQueIter& haveSentIter = OutgoingSent().SetToFirst(); + while ( ( msg = haveSentIter++ ) != NULL) + { + if ( aClient.Id() == msg->SessionId() && aSession.SupportedMessage(*msg) ) + { + OutgoingSent().RemoveAndDestroy(*msg); + } + } + + // (1)(d) Are pending delivery to this session: + TSglQueIter& pendingDeliveryIter = IncomingPendingDelivery().SetToFirst(); + while ( ( msg = pendingDeliveryIter++ ) != NULL ) + { + if ( aClient.Id() == msg->SessionId() && aSession.SupportedMessage(*msg) ) + { + if (msg->MsgType() == ERemConNotifyCommand) + { + SendReject(msg->Addr(), msg->InterfaceUid(), msg->OperationId(), msg->TransactionId()); + } + IncomingPendingDelivery().RemoveAndDestroy(*msg); + } + } + + // (1)(e) Have been delivered to this session and are awaiting responses: + TSglQueIter& deliveredIter = IncomingDelivered().SetToFirst(); + while ( ( msg = deliveredIter++ ) != NULL ) + { + if ( aClient.Id() == msg->SessionId() && aSession.SupportedMessage(*msg) ) + { + if (msg->MsgType() == ERemConNotifyCommand) + { + SendReject(msg->Addr(), msg->InterfaceUid(), msg->OperationId(), msg->TransactionId()); + } + IncomingDelivered().RemoveAndDestroy(*msg); + } + } + + // Remove the session's record from the connection history record. + RemoveSessionFromConnHistory(aSession); + + // Finally, try to delete client process representation if it now has no sessions + TryToDropClientProcess(clientIndex); + + iTargetClientsLock.Signal(); + + LOGTARGETSESSIONS; + } + +void CRemConServer::RemoveSessionFromConnHistory(const CRemConSession& aSession) + { + LOG_FUNC; + + const TUint count = iSession2ConnHistory.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + if ( iSession2ConnHistory[ii].iSessionId == aSession.Id() ) + { + iSession2ConnHistory.Remove(ii); + UpdateConnectionHistoryAndPointers(); + break; + } + } + } + +// called by client process representation on close. +void CRemConServer::TargetClientClosed(CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOG1(_L("\t&aClient = 0x%08x"), &aClient); + LOGTARGETSESSIONS; + + iTargetClientsLock.Wait(); + // Find this client in the array and remove it (if it's there). + const TUint clientCount = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < clientCount ; ++ii ) + { + if ( iTargetClients[ii] == &aClient ) + { + // We've found the client in our array. + + // 1. Remove the client from our array. + iTargetClients.Remove(ii); + + // 2a. Tell the TSP the client has gone away + if(iTspIf5) + { + iTspIf5->TargetClientUnavailable(aClient.ClientInfo()); + } + + // 2b. Tell the bearers about the client going away, if it was the + // last target. + // If the client hasn't already set its type, then it doesn't + // count (we won't have told the bearers about it to begin with). + // The bearer manager maintains controller and target counts for all bearers + // and will tell bearers when they need to know things have changed + if (aClient.ClientAvailable()) + { + ASSERT_DEBUG(iBearerManager); + iBearerManager->ClientClosed(EFalse, KNullUid, aClient.Id()); + } + + break; + } // End found session in our array + } + iTargetClientsLock.Signal(); + + StartShutdownTimerIfNoClientsOrBulkThread(); + + LOGTARGETSESSIONS; + } + +#ifdef __FLOG_ACTIVE + +void CRemConServer::LogControllerSessions() const + { + const TUint count = iControllerSessions.Count(); + LOG1(_L("\tNumber of controller sessions = %d"), count); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConSession* const session = iControllerSessions[ii]; + ASSERT_DEBUG(session); + LOG4(_L("\t\tsession %d [0x%08x], Id = %d, ProcessId = %d"), + ii, + session, + session->Id(), + static_cast(session->ClientInfo().ProcessId()) + ); + } + } + +void CRemConServer::LogTargetSessions() const + { + iTargetClientsLock.Wait(); + + const TUint count = iTargetClients.Count(); + LOG1(_L("\tNumber of target clients = %d"), count); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + LOG5(_L("\t\tclient %d [0x%08x], Id = %d, ProcessId = %d, SessionCount = %d"), + ii, + client, + client->Id(), + static_cast(client->ClientInfo().ProcessId()), + client->TargetSessionCount() + ); + } + iTargetClientsLock.Signal(); + } + +void CRemConServer::LogRemotes() const + { + // Called from dtor- this may not have been made yet. + if ( iConnectionHistory ) + { + CConnections& conns = iConnectionHistory->Last(); + conns.LogConnections(); + } + } + +void CRemConServer::LogConnectionHistoryAndInterest() const + { + LOG(_L("Logging connection history and interest in it")); + if ( iConnectionHistory ) + { + iConnectionHistory->LogConnectionHistory(); + + const TUint count = iSession2ConnHistory.Count(); + LOG1(_L("\tNumber of sessions = %d"), count); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + const TSessionPointerToConnectionHistory& interest = iSession2ConnHistory[ii]; + LOG3(_L("\t\tinterest %d, iSessionId = %d, iIndex = %d"), + ii, + interest.iSessionId, + interest.iIndex + ); + } + } + } +#endif // __FLOG_ACTIVE + +void CRemConServer::MrctspoDoOutgoingNotifyCommandAddressed(TRemConAddress* aConnection, TInt aError) + { + LOG(KNullDesC8()); + LOG_FUNC; + LOG1(_L("\taError = %d"), aError); + LOGOUTGOINGNOTIFYCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + + ASSERT_DEBUG(iTspHandlingOutgoingNotifyCommand); + iTspHandlingOutgoingNotifyCommand = EFalse; + + CRemConMessage& msg = OutgoingNotifyCmdPendingTsp().First(); + ASSERT_DEBUG(msg.Addr().IsNull()); + MRemConMessageSendObserver* const observer = ControllerSession(msg.SessionId()); + // Session closure removes messages from the outgoing queue and cancels + // the TSP request if relevant. If observer is NULL here, then this processing + // has gone wrong. + ASSERT_DEBUG(observer); + + if ( (aError != KErrNone) || !aConnection) + { + observer->MrcmsoMessageSendResult(msg, aError); + } + else + { + // Message addressed OK. + if ( aConnection != NULL ) + { + TBool sync = EFalse; + TRAPD(err, SendCmdToRemoteL(msg, *aConnection, sync)); + if ( err || sync ) + { + observer->MrcmsoMessageSendResult(msg, err); + } + + delete aConnection; + aConnection = NULL; + } + } + + // We've now finished with the addressed message, so destroy it. + OutgoingNotifyCmdPendingTsp().RemoveAndDestroy(msg); + + // Check for more notify commands to address. + if ( !OutgoingNotifyCmdPendingTsp().IsEmpty() ) + { + LOG(_L8("\tmore outgoing notify commands awaiting TSP...")); + TspOutgoingNotifyCommand(); + } + + LOGOUTGOINGNOTIFYCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + } + +void CRemConServer::MrctspoDoOutgoingCommandAddressed(TInt aError) + { + LOG(KNullDesC8()); + LOG_FUNC; + LOG1(_L("\taError = %d"), aError); + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + + ASSERT_DEBUG(iTspHandlingOutgoingCommand); + iTspHandlingOutgoingCommand = EFalse; + + // If aError is KErrNone, + // Remove the addressed message from the iOutgoingPendingTsp queue. + // Send the command to the requested bearer(s), putting items on the + // iOutgoingSent queue to await responses + // Complete the controller client's message. + + // Any error at any point during the above should roll back everything + // (apart from removing the original message from iOutgoingPendingTsp) + // and error the sending controller. + + // Finally, check iOutgoingPendingTsp for more commands to give to + // the TSP. + + // The head item, currently being dealt with, is always at index 0. + // NB This msg will be destroyed by the end of the function, and copies + // taken to add to iOutgoingSent. + // Note that if the client went away while the address request was + // outstanding, this item will still be on this queue because we protect + // it (see ClientClosed). + CRemConMessage& msg = OutgoingCmdPendingTsp().First(); + // Check that the message isn't addressed already. If this fails, it's + // possible that the TSP has called OutgoingCommandAddressed in response + // to a PermitOutgoingCommand request. + ASSERT_DEBUG(msg.Addr().IsNull()); + // The observer is the session which generated the message. + MRemConMessageSendObserver* const observer = ControllerSession(msg.SessionId()); + // Session closure removes messages from the outgoing queue and cancels + // the TSP request if relevant. If observer is NULL here, then this processing + // has gone wrong. + ASSERT_DEBUG(observer); + + TInt numRemotesToTry = 0; + + if ( aError != KErrNone ) + { + // Error prevented message send attempt from being made. + observer->MrcmsoMessageSendOneOrMoreAttemptFailed(msg, aError); + } + else + { + // Message addressed OK. + // Work out how many remotes the TSP said to send to. + TSglQueIter iter(iTspConnections); + while ( iter++ ) + { + ++numRemotesToTry; + } + // Notify session of send attempt. + observer->MrcmsoMessageSendOneOrMoreAttempt(msg, numRemotesToTry); + iter.SetToFirst(); + // Try to connect and send a message to each specified remote. + TRemConAddress* conn; + while ( ( conn = iter++ ) != NULL ) + { + LOG2(_L("\tsending message to remote [0x%08x] BearerUid = 0x%08x"), + conn, conn->BearerUid()); + + // We send to as many of the remotes as we can. The observer remembers + // how many remotes got sent to successfully, and completes the + // client's request with either KErrNone or _one of_ the + // errors that were raised. + TBool sync = EFalse; + TRAPD(err, SendCmdToRemoteL(msg, *conn, sync)); + if ( err || sync ) + { + // We have finished trying to process this (copy of this) + // message, so we can adjust our 'remotes' counter / + // completion error. + observer->MrcmsoMessageSendOneOrMoreResult(msg, err); + } + // else we didn't actually make a send attempt because conn was + // down. This particular message will undergo an actual + // (bearer-level) send attempt later on when the connection comes + // up. For now, however, we cannot legally complete the client's + // request. + + iTspConnections.Remove(*conn); + delete conn; + } // End while + } // End if TSP addressed command OK + + // We've now finished with the addressed message, so destroy it. + OutgoingCmdPendingTsp().RemoveAndDestroy(msg); + + // Check for more commands to address. + if ( !OutgoingCmdPendingTsp().IsEmpty() ) + { + LOG(_L("\tmore outgoing commands awaiting TSP...")); + TspOutgoingCommand(); + } + + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + } + +void CRemConServer::MrctspoDoOutgoingCommandPermitted(TBool aIsPermitted) + { + LOG(KNullDesC8()); + LOG_FUNC; + LOG1(_L("\taIsPermitted = %d"), aIsPermitted); + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + + ASSERT_DEBUG(iTspHandlingOutgoingCommand); + iTspHandlingOutgoingCommand = EFalse; + + // If aIsPermitted is EFalse, complete the sending session with + // KErrPermissionDenied and destroy the message. Otherwise try to send the + // message and complete the sending session. If it sent OK, move the + // message to the 'outgoing sent' queue. + // At the end, check the 'pending TSP' queue again. + // The head item, currently being dealt with, is always at index 0. + // Note that if the client went away while the address request was + // outstanding, this item will still be on this queue because we protect + // it (see ClientClosed). + CRemConMessage& msg = OutgoingCmdPendingTsp().First(); + // Check that the message is addressed already. If this fails, it's + // possible that the TSP has called OutgoingCommandPermitted in response + // to a AddressOutgoingCommand request. + ASSERT_DEBUG(!msg.Addr().IsNull()); + // The session is the observer + MRemConMessageSendObserver* const observer = ControllerSession(msg.SessionId()); + // Session closure removes messages from the outgoing queue and cancels + // the TSP request if relevant. If observer is NULL here, then this processing + // has gone wrong. + ASSERT_DEBUG(observer); + TInt err = KErrPermissionDenied; + if ( aIsPermitted ) + { + TBool sync = EFalse; + TRAP(err, SendCmdToRemoteL(msg, msg.Addr(), sync)); + if ( err || sync ) + { + // We made a send attempt at the bearer level, notify observer. + observer->MrcmsoMessageSendOneOrMoreResult(msg, err); + } + // else the message is waiting until a bearer-level connection + // comes up. Only then can we complete the client's message. + } + else + { + // The send wasn't permitted, notify observer. + // This should complete the client's message, as we're connection oriented + // (so only one remote to send to). + observer->MrcmsoMessageSendOneOrMoreResult(msg, KErrPermissionDenied); + } + + // We've now finished with the message, so destroy it. + OutgoingCmdPendingTsp().RemoveAndDestroy(msg); + + // Check for more commands to give to the TSP. + if ( !OutgoingCmdPendingTsp().IsEmpty() ) + { + LOG(_L("\tmore outgoing commands awaiting TSP...")); + TspOutgoingCommand(); + } + + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + } + +void CRemConServer::MrctspoDoOutgoingNotifyCommandPermitted(TBool aIsPermitted) + { + LOG(KNullDesC8()); + LOG_FUNC; + LOG1(_L("\taIsPermitted = %d"), aIsPermitted); + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + + ASSERT_DEBUG(iTspHandlingOutgoingNotifyCommand); + iTspHandlingOutgoingNotifyCommand = EFalse; + + // If aIsPermitted is EFalse, complete the sending session with + // KErrPermissionDenied and destroy the message. Otherwise try to send the + // message and complete the sending session. If it sent OK, move the + // message to the 'outgoing sent' queue. + // At the end, check the 'pending TSP' queue again. + // The head item, currently being dealt with, is always at index 0. + // Note that if the client went away while the address request was + // outstanding, this item will still be on this queue because we protect + // it (see ClientClosed). + CRemConMessage& msg = OutgoingNotifyCmdPendingTsp().First(); + // Check that the message is addressed already. If this fails, it's + // possible that the TSP has called OutgoingCommandPermitted in response + // to a AddressOutgoingCommand request. + ASSERT_DEBUG(!msg.Addr().IsNull()); + MRemConMessageSendObserver* const observer = ControllerSession(msg.SessionId()); + // Session closure removes messages from the outgoing queue and cancels + // the TSP request if relevant. If observer is NULL here, then this processing + // has gone wrong. + ASSERT_DEBUG(observer); + TInt err = KErrPermissionDenied; + if ( aIsPermitted ) + { + TBool sync = EFalse; + TRAP(err, SendCmdToRemoteL(msg, msg.Addr(), sync)); + if ( err || sync ) + { + observer->MrcmsoMessageSendResult(msg, err); + } + // else the message is waiting until a bearer-level connection + // comes up. Only then can we complete the client's message. + } + else + { + observer->MrcmsoMessageSendResult(msg, KErrPermissionDenied); + } + + // We've now finished with the message, so destroy it. + OutgoingNotifyCmdPendingTsp().RemoveAndDestroy(msg); + + // Check for more commands to give to the TSP. + if ( !OutgoingNotifyCmdPendingTsp().IsEmpty() ) + { + LOG(_L("\tmore outgoing commands awaiting TSP...")); + TspOutgoingNotifyCommand(); + } + + LOGOUTGOINGCMDPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + } + +void CRemConServer::MrctspoDoOutgoingResponsePermitted(TBool aIsPermitted) + { + LOG_FUNC; + LOGOUTGOINGRSPPENDINGTSP; + ASSERT_DEBUG(iTspHandlingOutgoingResponse); + iTspHandlingOutgoingResponse = EFalse; + ASSERT_DEBUG(iOutgoingRspPendingTsp); + CRemConMessage& msg = iOutgoingRspPendingTsp->First(); + + iOutgoingRspPendingTsp->Remove(msg); + + CRemConTargetClientProcess* const client = TargetClient(msg.SessionId()); + // Session closure removes messages from the outgoing queue and cancels + // the TSP request if relevant. If client is NULL here, then this processing + // has gone wrong. + ASSERT_DEBUG(client); + + if (aIsPermitted) + { + ASSERT_DEBUG(iMessageRecipientsList); + iMessageRecipientsList->RemoveAndDestroyMessage(msg.TransactionId()); + CompleteSendResponse(msg, *client); // Ownership of msg is always taken + } + else + { + CMessageRecipients* messageClients = iMessageRecipientsList->Message(msg.TransactionId()); + if (messageClients) + { + messageClients->RemoveAndDestroyClient(client->ClientInfo()); // Remove the current client info from the list + if (messageClients->Clients().IsEmpty()) + { + iMessageRecipientsList->RemoveAndDestroyMessage(msg.TransactionId()); + // The bearer won't be getting a response + + SendReject(msg.Addr(), msg.InterfaceUid(), msg.OperationId(), msg.TransactionId()); + } + } + + // Notify client that a send attempt to a remote was abandoned. + client->MrcmsoMessageSendOneOrMoreAbandoned(msg); + delete &msg; + } + if (!iOutgoingRspPendingTsp->IsEmpty()) + { + PermitOutgoingResponse(); + } + LOGOUTGOINGRSPPENDINGTSP; + } + +void CRemConServer::MrctspoDoIncomingNotifyAddressed(TClientInfo* aClientInfo, TInt aError) + { + LOG_FUNC; + LOG(KNullDesC8()); + LOG1(_L("\taError = %d"), aError); + LOGINCOMINGNOTIFYCMDPENDINGADDRESS; + LOGINCOMINGNOTIFYCMDPENDINGREADDRESS; + LOGINCOMINGDELIVERED; + + // Send the command to the requested target client, and remove the + // addressed message from the iIncomingNotifyCmdPendingAddress queue. Any error at + // any point should be ignored- just complete as much as we can. + // Then check iIncomingPendingAddress for more commands to give to the + // TSP. + + ASSERT_DEBUG(iTspAddressingIncomingNotifyCommand || iTspReAddressingIncomingNotifyCommands); + + if (iTspAddressingIncomingNotifyCommand) + { + iTspAddressingIncomingNotifyCommand = EFalse; + if (!iTspDropIncomingNotifyCommand) + { + // We know that the queue is not empty because we put something on the + // queue to call AddressIncomingNotifyCommand, which results in one call to this + // function, which is the only place where messages are removed from the + // queue. + CRemConMessage& msg = IncomingNotifyCmdPendingAddress().First(); + TBool cmdDelivered = EFalse; + + // If the TSP errored, can't complete to any clients. + if ( aError == KErrNone && aClientInfo) + { + LOG1(_L("\t\tprocess ID %d"), static_cast(aClientInfo->ProcessId())); + // Get the corresponding client. + CRemConTargetClientProcess* const client = TargetClient(aClientInfo->ProcessId()); + // NB The set of open clients may have changed while the request + // was out on the TSP. If the TSP indicates a client that has + // gone away, then ignore that client. + if ( client ) + { + TRAPD(err, DeliverCmdToClientL(msg, *client)); + if (err == KErrNone) + { + cmdDelivered = ETrue; + } + // If we couldn't deliver an instance of the command to a + // target, there's not much we can do. + } + } + + if (!cmdDelivered) + { + // The command wasn't delivered to any clients + + // The command wasn't delivered to any clients + // Tell bearer it won't be getting a response + + SendReject(msg.Addr(), msg.InterfaceUid(), msg.OperationId(), msg.TransactionId()); + } + + + // Destroy the message we've just dealt with. + IncomingNotifyCmdPendingAddress().RemoveAndDestroy(msg); + } + iTspDropIncomingNotifyCommand = EFalse; + if ( !IncomingNotifyCmdPendingAddress().IsEmpty() ) + { + LOG(_L("\tmore incoming commands awaiting addressing...")); + AddressIncomingNotifyCommand(); + } + } + else + { + if (!iTspDropIncomingNotifyCommand) + { + CRemConMessage& msg = IncomingNotifyCmdPendingReAddress().First(); + if(aError == KErrNone && aClientInfo) + { + LOG1(_L("\t\tprocess ID %d"), static_cast(aClientInfo->ProcessId())); + // Get the corresponding client. + CRemConTargetClientProcess* const client = TargetClient(aClientInfo->ProcessId()); + if (client) + { + if (client->Id() == msg.SessionId()) + { + // Don't do anything - it's already on IncomingDelivered + } + else + { + // Remove the original message from IncomingDelivered + CRemConMessage* deliveredMsg = NULL; + TSglQueIter iter = IncomingDelivered().SetToFirst(); + while ((deliveredMsg = iter++) != NULL) + { + if (deliveredMsg->TransactionId() == msg.TransactionId()) + { + // We need to update the subtype now, in case the client has sent an interim response while the notify + // was being readdressed. + msg.MsgSubType() = deliveredMsg->MsgSubType(); + // Deliver to the client + TRAPD(err, DeliverCmdToClientL(msg, *client)); + if (err == KErrNone) + { + // Only remove the current message if the delivery to the new client suceeded. + IncomingDelivered().RemoveAndDestroy(*deliveredMsg); + } + break; + } + } + } + } + else + { + SendReject(msg.Addr(), msg.InterfaceUid(), msg.OperationId(), msg.TransactionId()); + } + } + IncomingNotifyCmdPendingReAddress().RemoveAndDestroy(msg); + } + + iTspDropIncomingNotifyCommand = EFalse; + + if ( !IncomingNotifyCmdPendingReAddress().IsEmpty() ) + { + LOG(_L("\tmore incoming commands awaiting readdressing...")); + ReAddressIncomingNotifyCommand(); + } + else + { + iTspReAddressingIncomingNotifyCommands = EFalse; + if ( !IncomingNotifyCmdPendingAddress().IsEmpty() ) + { + LOG(_L("\tmore incoming commands awaiting addressing...")); + AddressIncomingNotifyCommand(); + } + } + } + LOGINCOMINGNOTIFYCMDPENDINGADDRESS; + LOGINCOMINGNOTIFYCMDPENDINGREADDRESS; + LOGINCOMINGDELIVERED; + } + +void CRemConServer::MrctspoDoReAddressNotifies() + { + LOG_FUNC; + LOGINCOMINGNOTIFYCMDPENDINGREADDRESS; + LOGINCOMINGDELIVERED; + ASSERT_DEBUG(iTspReAddressingIncomingNotifyCommands == EFalse && iTspAddressingIncomingNotifyCommand == EFalse); + TSglQueIter iter = IncomingDelivered().SetToFirst(); + CRemConMessage* deliveredMsg; + while ((deliveredMsg = iter++) != NULL) + { + if (deliveredMsg->MsgType() == ERemConNotifyCommand) + { + CRemConMessage* newMsg = NULL; + TRAPD(err, newMsg = CRemConMessage::CopyL(*deliveredMsg)); + if (err == KErrNone) + { + IncomingNotifyCmdPendingReAddress().Append(*newMsg); + } + // If we couldn't copy the message, there isn't much we can do now. + } + } + if (!IncomingNotifyCmdPendingReAddress().IsEmpty()) + { + iTspReAddressingIncomingNotifyCommands = ETrue; + ReAddressIncomingNotifyCommand(); + } + LOGINCOMINGNOTIFYCMDPENDINGREADDRESS; + } + +void CRemConServer::MrctspoDoIncomingCommandPermitted(TBool aIsPermitted) + { + LOG_FUNC; + + MrctspoDoIncomingCommandAddressed(aIsPermitted ? KErrNone : KErrAccessDenied); + } + +void CRemConServer::MrctspoDoIncomingNotifyPermitted(TBool aIsPermitted) + { + LOG_FUNC; + + if(aIsPermitted) + { + TClientInfo* clientInfo = TargetClientIdToClientInfo(IncomingNotifyCmdPendingAddress().First().Client()); + MrctspoDoIncomingNotifyAddressed(clientInfo, KErrNone); + } + else + { + MrctspoDoIncomingNotifyAddressed(NULL, KErrAccessDenied); + } + } + +void CRemConServer::TspOutgoingCommand() + { + LOG_FUNC; + + ASSERT_DEBUG(iTspIf); + ASSERT_DEBUG(iTspHandlingOutgoingCommand == EFalse); + // For TSPs which complete this request synchronously this will become + // recursive, but the depth is not expected to be great (== number of + // messages awaiting access to the TSP). + + // Work out whether the next command to deal with is awaiting (a) + // addressing or (b) permission. + // The head item is at index 0. This function should only be called if the + // queue is not empty. + CRemConMessage& msg = OutgoingCmdPendingTsp().First(); + CRemConControllerSession* const sess = ControllerSession(msg.SessionId()); + // The session should exist- if it closed after asking to send this + // message, then the message should have been removed from the outgoing + // pending TSP queue at that time. + ASSERT_DEBUG(sess); + iTspHandlingOutgoingCommand = ETrue; + if ( msg.Addr().IsNull() ) + { + // Null address means it's awaiting an address. + // Check the array of outgoing addresses is ready for this new request + // on the TSP. + ASSERT_DEBUG(iTspConnections.IsEmpty()); + ASSERT_DEBUG(iBearerManager); + iTspIf->AddressOutgoingCommand( + msg.InterfaceUid(), + msg.OperationId(), + sess->ClientInfo(), + iTspConnections, + iBearerManager->BearerSecurityPolicies()); + } + else + { + // Non-null address means it's awaiting permission to send. + sess->MrcmsoMessageSendOneOrMoreAttempt(msg, 1); + iTspIf->PermitOutgoingCommand( + msg.InterfaceUid(), + msg.OperationId(), + sess->ClientInfo(), + msg.Addr()); + } + } + +void CRemConServer::TspOutgoingNotifyCommand() + { + LOG_FUNC; + + ASSERT_DEBUG(iTspIf3); + ASSERT_DEBUG(iTspHandlingOutgoingNotifyCommand == EFalse); + // For TSPs which complete this request synchronously this will become + // recursive, but the depth is not expected to be great (== number of + // messages awaiting access to the TSP). + + // Work out whether the next command to deal with is awaiting (a) + // addressing or (b) permission. + // The head item is at index 0. This function should only be called if the + // queue is not empty. + CRemConMessage& msg = OutgoingNotifyCmdPendingTsp().First(); + CRemConControllerSession* const sess = ControllerSession(msg.SessionId()); + // The session should exist- if it closed after asking to send this + // message, then the message should have been removed from the outgoing + // pending TSP queue at that time. + ASSERT_DEBUG(sess); + iTspHandlingOutgoingNotifyCommand = ETrue; + if ( msg.Addr().IsNull() ) + { + ASSERT_DEBUG(iBearerManager); + + iTspIf3->AddressOutgoingNotify( + msg.InterfaceUid(), + msg.OperationId(), + sess->ClientInfo(), + iBearerManager->BearerSecurityPolicies()); + } + else + { + // Non-null address means it's awaiting permission to send. + // As this is a notify command, we don't need to adjust NumRemotes() or + // NumRemotesToTry() on the session. + iTspIf3->PermitOutgoingNotifyCommand( + msg.InterfaceUid(), + msg.OperationId(), + sess->ClientInfo(), + msg.Addr()); + } + } + +void CRemConServer::AddressIncomingCommand() + { + LOG_FUNC; + + ASSERT_DEBUG(iTspIf); + ASSERT_DEBUG(iTspAddressingIncomingCommand == EFalse); + // For TSPs which complete this request synchronously this will become + // recursive, but the depth is not expected to be great (== number of + // messages awaiting addressing). + + // There are two possibilities here, either the bearer has already provided + // us with an address, in which case we just provide the TSP the command + // for information, and to give it the opportunity to reject the command, + // or we don't have an address, in which case we ask the TSP for one + + // This function should only be called if we know this queue is not + // empty. + CRemConMessage& msg = IncomingCmdPendingAddress().First(); + iTspIncomingCmdClients.Reset(); + iTspAddressingIncomingCommand = ETrue; + + if(msg.Client() == KNullClientId) + { + // Prepare the array of target process IDs for the TSP. + iTargetClientsLock.Wait(); + const TUint count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + iTspIncomingCmdClients.AddLast(client->ClientInfo()); + } + iTargetClientsLock.Signal(); + + iTspIf->AddressIncomingCommand( + msg.InterfaceUid(), + msg.OperationId(), + iTspIncomingCmdClients); + } + else + { + iTspIncomingCmdClients.AddLast(*TargetClientIdToClientInfo(msg.Client())); + ASSERT_DEBUG(iTspIf4); + iTspIf4->PermitIncomingCommand( + msg.InterfaceUid(), + msg.OperationId(), + *iTspIncomingCmdClients.First()); + } + } + +void CRemConServer::AddressIncomingNotifyCommand() + { + LOG_FUNC; + + ASSERT_DEBUG(iTspIf2); + ASSERT_DEBUG(iTspAddressingIncomingNotifyCommand == EFalse); + // For TSPs which complete this request synchronously this will become + // recursive, but the depth is not expected to be great (== number of + // messages awaiting addressing). + + // There are two possibilities here, either the bearer has already provided + // us with an address, in which case we just provide the TSP the command + // for information, and to give it the opportunity to reject the command, + // or we don't have an address, in which case we ask the TSP for one + + // This function should only be called if we know this queue is not + // empty + CRemConMessage& msg = IncomingNotifyCmdPendingAddress().First(); + iTspIncomingNotifyCmdClients.Reset(); + iTspAddressingIncomingNotifyCommand = ETrue; + + if(!FindDuplicateNotify(msg)) + { + if(msg.Client() == KNullClientId) + { + // Prepare the array of target process IDs for the TSP. + iTargetClientsLock.Wait(); + const TUint count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + iTspIncomingNotifyCmdClients.AddLast(client->ClientInfo()); + } + iTargetClientsLock.Signal(); + + // Only send the notify to the TSP if there isn't an identical one on either incomingpendingdelivery or incomingdelivered + iTspIf2->AddressIncomingNotify( + msg.InterfaceUid(), + msg.OperationId(), + iTspIncomingNotifyCmdClients); + } + else + { + iTspIncomingNotifyCmdClients.AddLast(*TargetClientIdToClientInfo(msg.Client())); + ASSERT_DEBUG(iTspIf4); + iTspIf4->PermitIncomingNotify( + msg.InterfaceUid(), + msg.OperationId(), + *iTspIncomingNotifyCmdClients.First()); + } + } + else + { + // Otherwise, we can call IncomingNotifyAddressed with NULL, and it will be rejected back to the bearer + MrctspoDoIncomingNotifyAddressed(NULL, KErrArgument); + } + } + +void CRemConServer::ReAddressIncomingNotifyCommand() + { + LOG_FUNC; + LOGINCOMINGNOTIFYCMDPENDINGREADDRESS; + ASSERT_DEBUG(iTspIf2); + ASSERT_DEBUG(iTspReAddressingIncomingNotifyCommands); + // For TSPs which complete this request synchronously this will become + // recursive, but the depth is not expected to be great (== number of + // messages awaiting addressing). + // Prepare the array of target process IDs for the TSP. + iTspIncomingNotifyCmdClients.Reset(); + iTargetClientsLock.Wait(); + const TUint count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + iTspIncomingNotifyCmdClients.AddLast(client->ClientInfo()); + } + iTargetClientsLock.Signal(); + + // This function should only be called if we know this queue is not + // empty. + CRemConMessage& msg = IncomingNotifyCmdPendingReAddress().First(); + iTspIf2->AddressIncomingNotify( + msg.InterfaceUid(), + msg.OperationId(), + iTspIncomingNotifyCmdClients); + } + + +void CRemConServer::PermitOutgoingResponse() + { + LOG_FUNC; + LOGOUTGOINGRSPPENDINGTSP; + + ASSERT_DEBUG(iTspHandlingOutgoingResponse == EFalse); + ASSERT_DEBUG(!OutgoingRspPendingTsp().IsEmpty()); + while (!iTspHandlingOutgoingResponse && !OutgoingRspPendingTsp().IsEmpty()) + { + CRemConMessage& msg = OutgoingRspPendingTsp().First(); + CRemConTargetClientProcess* client = TargetClient(msg.SessionId()); + // The client should exist- if it closed after asking to send this + // message, then the message should have been removed from the outgoing + // pending TSP queue at that time. + ASSERT_DEBUG(client); + ASSERT_DEBUG(iMessageRecipientsList); + CMessageRecipients* message = iMessageRecipientsList->Message(msg.TransactionId()); + + if (message) // If we aren't returned a client list, this means that the message has been delivered elsewhere + { + iTspHandlingOutgoingResponse = ETrue; + if (iTspIf2) + { + iTspIf2->PermitOutgoingResponse( + msg.InterfaceUid(), + msg.OperationId(), + client->ClientInfo(), + message->ConstIter() + ); + } + else + { + OutgoingResponsePermitted(ETrue); + } + } + else + { + client->MrcmsoMessageSendOneOrMoreAbandoned(msg); + OutgoingRspPendingTsp().RemoveAndDestroy(msg); + } + } + LOGOUTGOINGRSPPENDINGTSP; + } + +void CRemConServer::SendCmdToRemoteL(const CRemConMessage& aMsg, const TRemConAddress& aConn, TBool& aSync) + { + LOG_FUNC; + + ASSERT_DEBUG( + (aMsg.MsgType() == ERemConCommand) + || (aMsg.MsgType() == ERemConNotifyCommand) + ); + + + // Populate aSync immediately in case the function leaves. + aSync = EFalse; + TConnectionState conState = ConnectionState(aConn); + if ( conState == EConnected) + { + aSync = ETrue; + } + + // Take a copy of the message and set the right address. + CRemConMessage* newMsg = CRemConMessage::CopyL(aMsg); + newMsg->Addr() = aConn; + CleanupStack::PushL(newMsg); + ASSERT_DEBUG(iBearerManager); + + // If we're connected (and not mid-connect/disconnect) we can send immediately, + // otherwise we queue and try to bring up the connection. + switch ( conState ) + { + case EConnected: + { + // We have a connection to send over. Try to send it. + LEAVEIFERRORL(iBearerManager->Send(*newMsg)); + // If Send worked, then the bearer has taken ownership of the + // message's data. + newMsg->OperationData().Assign(NULL); + CLEANUPSTACK_POP1(newMsg); + OutgoingSent().Append(*newMsg); + break; + } + case EDisconnected: + { + // No connection. Try to bring one up. If we can't then destroy the + // new message and leave. + CLEANUPSTACK_POP1(newMsg); + // We need to put the message on the queue before trying to connect + // because the connect confirmation might come back synchronously. + OutgoingPendingSend().Append(*newMsg); + TInt err = iBearerManager->Connect(newMsg->Addr()); + if ( err != KErrNone ) + { + OutgoingPendingSend().RemoveAndDestroy(*newMsg); + aSync = ETrue; + LEAVEIFERRORL(err); // will destroy newMsg + } + break; + } + case EConnecting: + case EDisconnecting: + { + // Just queue if connection is coming up or going down. + // + // If connection is coming up, the message will be + // sent when ConnectConfirm fires. + // + // If connection is going down: When CBearerManager gets + // the DisconnectConfirm it calls us back at RemoveConnection, + // whereat we will reconnect the bearer that was mid-disconnect; + // the message will be sent when that completes. + CLEANUPSTACK_POP1(newMsg); + OutgoingPendingSend().Append(*newMsg); + break; + } + default: + { + DEBUG_PANIC_LINENUM; + break; + } + } + + LOG1(_L("\taSync = %d"), aSync); + } + +void CRemConServer::DeliverCmdToClientL(const CRemConMessage& aMsg, CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + + ASSERT_DEBUG((aMsg.MsgType() == ERemConCommand) || (aMsg.MsgType() == ERemConNotifyCommand)); + // Take a copy of the message and set the right client ID (important to + // set the selected client's ID because this is how we match up the + // client's response, if aMsg is a command). + CRemConMessage* const newMsg = CRemConMessage::CopyL(aMsg); + newMsg->SessionId() = aClient.Id(); + LEAVEIFERRORL(DeliverMessageToClient(*newMsg, aClient)); + } + +TInt CRemConServer::DeliverMessageToClient(CRemConMessage& aMsg, CRemConControllerSession& aSess) + { + LOG_FUNC; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + // Controller clients only receive responses or rejects + ASSERT_DEBUG(aMsg.MsgType() == ERemConResponse || aMsg.MsgType() == ERemConReject); + + TInt err = KErrNone; + + // First off check if the client supports this + if(!aSess.SupportedMessage(aMsg)) + { + err = KErrArgument; + + // 'Take ownership' of it by destroying it- it's finished with. + delete &aMsg; + } + else if ( aSess.CurrentReceiveMessage().Handle() ) + { + err = aSess.WriteMessageToClient (aMsg); + + // 'Take ownership' of it by destroying it- it's finished with. + delete &aMsg; + } + else + { + IncomingPendingDelivery().Append(aMsg); + } + + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + return err; + } + +TInt CRemConServer::DeliverMessageToClient(CRemConMessage& aMsg, CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + TInt err = KErrNone; + + // Target clients only receive commands + ASSERT_DEBUG(aMsg.MsgType() == ERemConCommand || aMsg.MsgType() == ERemConNotifyCommand); + + // Pass message to client + err = aClient.ReceiveMessage(aMsg); + + if (err == KErrArgument) + { + // Message not supported. + // 'Take ownership' of it by destroying it- it's finished with. + delete &aMsg; + } + else if (err == KErrNotReady) + { + err = KErrNone; + // Client cannot receive this message at the moment. + IncomingPendingDelivery().Append(aMsg); + } + else + { + // If the message was delivered with no error, + // then put it in the 'incoming delivered' log. Otherwise, delete it + // because it's finished with. + if (err == KErrNone) + { + // We'll need to remember it for the response coming back. + IncomingDelivered().Append(aMsg); + } + else + { + // 'Take ownership' of it by destroying it- it's finished with. + delete &aMsg; + } + } + + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + return err; + } + +void CRemConServer::MrctspoDoIncomingCommandAddressed(TInt aError) + { + LOG(KNullDesC8()); + LOG_FUNC; + LOG1(_L("\taError = %d"), aError); + LOGINCOMINGCMDPENDINGADDRESS; + LOGINCOMINGDELIVERED; + + // Send the command to the requested target client(s), and remove the + // addressed message from the iIncomingPendingAddress queue. Any error at + // any point should be ignored- just complete as much as we can. + // Then check iIncomingPendingAddress for more commands to give to the + // TSP. + + ASSERT_DEBUG(iTspAddressingIncomingCommand); + + iTspAddressingIncomingCommand = EFalse; + + if (!iTspDropIncomingCommand) + { + // If we did not call CommandExpired on the first item in the queue, + // we know that the queue is not empty because we put something on the + // queue to call AddressIncomingCommand, which results in one call to this + // function, which is the only place where messages are removed from the + // queue. + CRemConMessage& msg = IncomingCmdPendingAddress().First(); + TBool cmdDelivered = EFalse; + + // If the TSP errored, can't complete to any clients. + if ( aError == KErrNone ) + { + TSglQueIter iter(iTspIncomingCmdClients); + + TClientInfo* procId; + CMessageRecipients* messageRecipients = NULL; + TBool canDeliver = ETrue; + TRAPD(err, messageRecipients = CMessageRecipients::NewL()); + if (err != KErrNone) + { + // If we didn't manage to create the list of clients we're delivering to, + // we shouldn't deliver the message to the clients + canDeliver = EFalse; + } + else + { + messageRecipients->TransactionId() = msg.TransactionId(); + } + if (canDeliver) + { + while ( ( procId = iter++ ) != NULL ) + { + LOG1(_L("\t\tprocess ID %d"), static_cast(procId->ProcessId())); + // Get the corresponding client. + CRemConTargetClientProcess* const client = TargetClient(procId->ProcessId()); + // NB The set of open clients may have changed while the request + // was out on the TSP. If the TSP indicates a client that has + // gone away, then ignore that client. + if ( client ) + { + TInt err = KErrNone; + TClientInfo* clientInfo = NULL; + TRAP(err, clientInfo = new (ELeave) TClientInfo); + if (err == KErrNone) + { + // If we didn't manage to create the TClientInfo, we shouldn't deliver to the client + TRAP(err, DeliverCmdToClientL(msg, *client)); + } + if (err == KErrNone) + { + cmdDelivered = ETrue; + // Add to the delivered information queue + clientInfo->ProcessId() = procId->ProcessId(); + clientInfo->SecureId() = procId->SecureId(); + messageRecipients->Clients().AddLast(*clientInfo); + } + else + { + delete clientInfo; + } + // If we couldn't deliver an instance of the command to a + // target, there's not much we can do. + } + } + if ( messageRecipients->Clients().IsEmpty()) + { + delete messageRecipients; + } + else + { + ASSERT_DEBUG(iMessageRecipientsList); + iMessageRecipientsList->Messages().AddLast (*messageRecipients); + } + } + } + + if ( !cmdDelivered) + { + // The command wasn't delivered to any clients + // Tell bearer it won't be getting a response + + SendReject(msg.Addr(), msg.InterfaceUid(), msg.OperationId(), msg.TransactionId()); + } + // Destroy the message we've just dealt with. + IncomingCmdPendingAddress().RemoveAndDestroy(msg); + + } + + iTspDropIncomingCommand = EFalse; + + if ( !IncomingCmdPendingAddress().IsEmpty() ) + { + LOG(_L("\tmore incoming commands awaiting addressing...")); + AddressIncomingCommand(); + } + + LOGINCOMINGCMDPENDINGADDRESS; + LOGINCOMINGDELIVERED; + } + +TInt CRemConServer::MrctspoDoGetConnections(TSglQue& aConnections) + { + LOG_FUNC; + + ASSERT_DEBUG(aConnections.IsEmpty()); + + // Get an owned copy of the current set of connections in the system. + CConnections* conns = NULL; + TRAPD(err, conns = CConnections::CopyL(Connections())); + LOG1(_L("\terr = %d"), err); + if ( err == KErrNone ) + { + // Pass these into aConnections, taking ownership of them. + TSglQueIter& iter = conns->SetToFirst(); + TRemConAddress* addr; + while ( ( addr = iter++ ) != NULL ) + { + conns->Remove(*addr); + aConnections.AddLast(*addr); + }; + delete conns; + } + + return err; + } + +TInt CRemConServer::MrctspoSetLocalAddressedClient(const TUid& aBearerUid, const TClientInfo& aClientInfo) + { + LOG_FUNC; + + TRemConClientId id = KNullClientId; + iTargetClientsLock.Wait(); + const TUint count = iTargetClients.Count(); + for(TUint i=0; iClientInfo().ProcessId() == aClientInfo.ProcessId()) + { + id = client->Id(); + break; + } + } + iTargetClientsLock.Signal(); + + if(id != KNullClientId) + { + ASSERT_DEBUG(iBearerManager); + return iBearerManager->SetLocalAddressedClient(aBearerUid, id); + } + else + { + return KErrNotFound; + } + } + +void CRemConServer::LoadTspL() + { + LOG_FUNC; + + // Instantiate one-and-only implementation of the target selector plugin + // interface. + const TUid KUidTargetSelectorInterface = TUid::Uid(KRemConTargetSelectorInterfaceUid); + RImplInfoPtrArray implementations; + const TEComResolverParams noResolverParams; + REComSession::ListImplementationsL(KUidTargetSelectorInterface, + noResolverParams, + KRomOnlyResolverUid, + implementations); + CleanupResetAndDestroyPushL(implementations); + LOG1(_L("\tnumber of implementations of target selector plugin interface: %d"), implementations.Count()); + // We use ASSERT_ALWAYS here because this assumption is a crucial + // licensee-facing one- it's not a case of simply 'run the tests on a + // debug build and fix it if it's broken'. + ASSERT_ALWAYS( implementations.Count() == 1 ); + CImplementationInformation* impl = implementations[0]; + ASSERT_DEBUG(impl); + LOG(_L("\tloading TSP with:")); + LOG1(_L("\t\timplementation uid 0x%08x"), impl->ImplementationUid()); + LOG1(_L("\t\tversion number %d"), impl->Version()); + TBuf8 buf8; + buf8.Copy(impl->DisplayName()); + LOG1(_L8("\t\tdisplay name \"%S\""), &buf8); + LOG1(_L("\t\tROM only %d"), impl->RomOnly()); + LOG1(_L("\t\tROM based %d"), impl->RomBased()); + iTsp = CRemConTargetSelectorPlugin::NewL(impl->ImplementationUid(), *this); + CleanupStack::PopAndDestroy(&implementations); + + iTspIf = reinterpret_cast( + iTsp->GetInterface(TUid::Uid(KRemConTargetSelectorInterface1)) + ); + + iTspIf2 = reinterpret_cast( + iTsp->GetInterface(TUid::Uid(KRemConTargetSelectorInterface2)) + ); + + iTspIf3 = reinterpret_cast( + iTsp->GetInterface(TUid::Uid(KRemConTargetSelectorInterface3)) + ); + + iTspIf4 = reinterpret_cast( + iTsp->GetInterface(TUid::Uid(KRemConTargetSelectorInterface4)) + ); + + iTspIf5 = reinterpret_cast( + iTsp->GetInterface(TUid::Uid(KRemConTargetSelectorInterface5)) + ); + + // If the TSP doesn't implement the required interface, panic server + // startup. + ASSERT_ALWAYS(iTspIf); + + // We always need a V4 interface to allow simpler handling by the + // bearers of bearer addressing so if we don't have a V4 interface + // from the TSP itself use a stub object to implement default + // behaviour. + if(!iTspIf4) + { + iTspIf4 = &iTspIf4Stub; + } + } + +void CRemConServer::SendCommand(CRemConMessage& aMsg) + { + LOG_FUNC; + + if (aMsg.MsgType() == ERemConCommand) + { + LOGOUTGOINGCMDPENDINGTSP; + + OutgoingCmdPendingTsp().Append(aMsg); + if ( !iTspHandlingOutgoingCommand ) + { + TspOutgoingCommand(); + } + + LOGOUTGOINGCMDPENDINGTSP; + } + else + { + // Check the command is a notify command + ASSERT_DEBUG(aMsg.MsgType() == ERemConNotifyCommand); + + if(iTspIf3) + { + OutgoingNotifyCmdPendingTsp().Append(aMsg); + if ( !iTspHandlingOutgoingNotifyCommand ) + { + TspOutgoingNotifyCommand(); + } + } + else + { + CRemConControllerSession* const sess = ControllerSession(aMsg.SessionId()); + delete &aMsg; + ASSERT_DEBUG(sess); + sess->SendError() = KErrNotSupported; + sess->CompleteSend(); + } + } + + } + +void CRemConServer::SendResponse(CRemConMessage& aMsg, CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOGINCOMINGDELIVERED; + LOGOUTGOINGRSPPENDINGTSP; + LOGOUTGOINGPENDINGSEND; + + CRemConMessage* response = &aMsg; + CRemConMessage* newResponse = NULL; + + // Find the first command ('John') in the iIncomingDelivered queue with + // the same session ID, interface UID and operation ID as the response + // we're sending, and send the response to the same address that John came + // from. + TSglQueIter& iter = IncomingDelivered().SetToFirst(); + CRemConMessage* msg; + TBool found = EFalse; + + while ( ( msg = iter++ ) != NULL ) + { + if ( msg->SessionId() == aClient.Id() + && msg->InterfaceUid() == response->InterfaceUid() + && msg->OperationId() == response->OperationId() + && ( + (msg->MsgSubType() == ERemConMessageDefault && response->MsgSubType() == ERemConMessageDefault) + || (msg->MsgSubType() == ERemConNotifyCommandAwaitingInterim && response->MsgSubType() == ERemConNotifyResponseInterim) + || (msg->MsgSubType() == ERemConNotifyCommandAwaitingChanged && response->MsgSubType() == ERemConNotifyResponseChanged) + ) + ) + { + LOG1(_L("\tfound a matching item in the incoming delivered commands log: [0x%08x]"), msg); + found = ETrue; + + // Set the right address and transaction id in the outgoing message + response->Addr() = msg->Addr(); + response->TransactionId() = msg->TransactionId(); + + if(msg->MsgType() == ERemConCommand) + { + // Notify client (this shall go to one remote) + aClient.MrcmsoMessageSendOneOrMoreAttempt(*response, 1); + + // Check the normal command and response have the default subtype set + ASSERT_DEBUG(msg->MsgSubType() == ERemConMessageDefault && response->MsgSubType() == ERemConMessageDefault); + + // Remove the item from the 'incoming delivered' queue now we've + // addressed a response using it. + IncomingDelivered().RemoveAndDestroy(*msg); + + // At this point we need to ask the TSP if we can deliver the outgoing response, + // if there is a TSP supporting the V2 interface. If there isn't, we should just deliver + // the message to the bearer + OutgoingRspPendingTsp().Append(*response); + if (!iTspHandlingOutgoingResponse) + { + PermitOutgoingResponse(); + } + } + else + { + // Notify client (this may be a series of messages to remotes) + aClient.MrcmsoMessageSendOneOrMoreIncremental(*msg, 1); + + // Check the command is a notify command + ASSERT_DEBUG(msg->MsgType() == ERemConNotifyCommand); + + // Check the command has a valid subtype for a notify command + ASSERT_DEBUG(msg->MsgSubType() == ERemConNotifyCommandAwaitingInterim || msg->MsgSubType() == ERemConNotifyCommandAwaitingChanged); + + // Check the response has a valid subtype for a notify response + ASSERT_DEBUG(response->MsgSubType() == ERemConNotifyResponseInterim || response->MsgSubType() == ERemConNotifyResponseChanged); + + switch(msg->MsgSubType()) + { + case ERemConNotifyCommandAwaitingChanged: + IncomingDelivered().RemoveAndDestroy(*msg); + + // As this is a changed notification, there could be several notifications outstanding + // that should all be completed with this message. + // We therefore need to take a copy of the message, but the response should not be + // completed back to the client. We therefore set the Session ID of the new meessage to 0 + + TRAPD(err, newResponse = CRemConMessage::CopyL(*response)); + if (err != KErrNone) + { + newResponse = NULL; + } + break; + case ERemConNotifyCommandAwaitingInterim: + msg->MsgSubType() = ERemConNotifyCommandAwaitingChanged; + break; + } + OutgoingRspPendingSend().Append(*response); + response = newResponse; + } + if (!newResponse) + { + break; + } + } + } + + if (newResponse) + { + delete newResponse; + } + + + TSglQueIter& rspIter = OutgoingRspPendingSend().SetToFirst(); + while ((msg = rspIter++) != NULL) + { + OutgoingRspPendingSend().Remove(*msg); + CompleteSendResponse(*msg, aClient); + } + + + // If the command was not found, then the app has sent a response to a + // non-existant command. It may do this in good intention if the server + // has transparently died and been restarted between the app's reception + // of the command and it sending its response, so we can't panic it. + // Just drop the message. + if ( !found ) + { + // Inform client that message should be completed with KErrNone We + // have done all we can with it. Any other error may encourage + // retries from the application, which would be useless in this situation. + aClient.MrcmsoMessageSendOneOrMoreAttemptFailed(aMsg, KErrNone); + delete &aMsg; + } + + LOGOUTGOINGRSPPENDINGTSP; + LOGINCOMINGDELIVERED; + LOGOUTGOINGPENDINGSEND; + } + +void CRemConServer::CompleteSendResponse(CRemConMessage& aMsg, CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOGOUTGOINGPENDINGSEND; + // If the bearer-level connection exists, then send the message. + // Otherwise, queue the message and request the connection to come + // up. The message will be sent when ConnectConfirm or + // ConnectIndicate is called (assuming no error). + ASSERT_DEBUG(aMsg.MsgType() == ERemConResponse); + ASSERT_DEBUG(iBearerManager); + // If we're connected (and not mid-connect/disconnect) we can send immediately, + // otherwise we queue and try to bring up the connection. + switch ( ConnectionState(aMsg.Addr()) ) + { + case EConnected: + { + // We're already connected + // If the bearer couldn't send, we need to error the client. + TInt err = iBearerManager->Send(aMsg); + + // Inform client that message should be completed with err. + // Bearer-level error means the response got sent to zero remotes- + // bearer-level success means it got sent to precisely 1. + aClient.MrcmsoMessageSendOneOrMoreResult(aMsg, err); + + // We've now finished with the response. + delete &aMsg; + break; + } + case EDisconnected: + { + // We're not connected. + // Queue the response... + OutgoingPendingSend().Append(aMsg); + // ... and ask the bearer to establish a connection. If we + // couldn't then complete the client's message and clean up. + TInt err = iBearerManager->Connect(aMsg.Addr()); + if ( err != KErrNone ) + { + aClient.MrcmsoMessageSendOneOrMoreResult(aMsg, err); + OutgoingPendingSend().RemoveAndDestroy(aMsg); + } + break; + } + case EConnecting: + case EDisconnecting: + { + // Just queue if connection is coming up or going down. + // + // If connection is coming up, the message will be + // sent when ConnectConfirm fires. + // + // If connection is going down: When CBearerManager gets + // the DisconnectConfirm it calls us back at RemoveConnection, + // whereat we will reconnect the bearer that was mid-disconnect; + // the message will be sent when that completes. + OutgoingPendingSend().Append(aMsg); + break; + } + default: + { + DEBUG_PANIC_LINENUM; + break; + } + } + LOGOUTGOINGPENDINGSEND; + } + + +void CRemConServer::SendReject(TRemConAddress aAddr, TUid aInterfaceUid, TUint aOperationId, TUint aTransactionId) + { + LOG_FUNC; + LOGOUTGOINGPENDINGSEND; + // If the bearer-level connection exists, then send the message. + // Otherwise, queue the message and request the connection to come + // up. The message will be sent when ConnectConfirm or + // ConnectIndicate is called (assuming no error). + + CRemConMessage* rejectMsg = NULL; + RBuf8 data; + data = KNullDesC8; + TRAPD(err, rejectMsg = CRemConMessage::NewL(aAddr, KNullClientId, ERemConReject, ERemConMessageDefault, aInterfaceUid, aOperationId, data, 0, aTransactionId)); + + if ( err == KErrNone) + { + // SendReject will always take ownership of rejectMsg + + ASSERT_DEBUG(iBearerManager); + switch ( ConnectionState(rejectMsg->Addr()) ) + { + case EConnected: + { + // We're already connected + err = iBearerManager->Send(*rejectMsg); + // We've now finished with the response. + delete rejectMsg; + break; + } + case EDisconnected: + { + // We're not connected. + // Queue the response... + OutgoingPendingSend().Append(*rejectMsg); + // ... and ask the bearer to establish a connection. If we + // couldn't then clean up. + err = iBearerManager->Connect (rejectMsg->Addr ()); + if ( err != KErrNone) + { + OutgoingPendingSend().RemoveAndDestroy(*rejectMsg); + } + break; + } + case EConnecting: + case EDisconnecting: + { + // Just queue if connection is coming up or going down. + // + // If connection is coming up, the message will be + // sent when ConnectConfirm fires. + // + // If connection is going down: When CBearerManager gets + // the DisconnectConfirm it calls us back at RemoveConnection, + // whereat we will reconnect the bearer that was mid-disconnect; + // the message will be sent when that completes. + OutgoingPendingSend().Append(*rejectMsg); + break; + } + default: + { + DEBUG_PANIC_LINENUM; + break; + } + } + } + LOGOUTGOINGPENDINGSEND; + } + +void CRemConServer::SendCancel(CRemConControllerSession& aSess) + { + LOG_FUNC; + LOGOUTGOINGCMDPENDINGTSP; + + TSglQueIter& iter = OutgoingCmdPendingTsp().SetToFirst(); + CRemConMessage* msg; + TBool first = ETrue; + while ( ( msg = iter++ ) != NULL ) + { + // A session can only have one send outstanding at once, so there can + // only be one message on the queue belonging to it. + if ( msg->SessionId() == aSess.Id() ) + { + ASSERT_DEBUG(msg->MsgType() == ERemConCommand); + LOG1(_L("\tfound a command belonging to this client in the outgoing pending TSP queue: [0x%08x]"), msg); + + // If the TSP is currently handling this command, we must stop + // them! The one the TSP is handling, if any, is always the first + // in the queue. + if ( iTspHandlingOutgoingCommand && first ) + { + LOG(_L("\tTSP is processing this command- cancel it")); + ASSERT_DEBUG(iTspIf); + iTspIf->CancelOutgoingCommand(); + iTspHandlingOutgoingCommand = EFalse; + iTspConnections.Reset(); + } + // Can now destroy the message. + OutgoingCmdPendingTsp().RemoveAndDestroy(*msg); + } + first = EFalse; + } + + LOGOUTGOINGCMDPENDINGTSP; + } + +void CRemConServer::NewResponse(CRemConMessage& aMsg) + { + LOG_FUNC; + LOGOUTGOINGSENT; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + // Look through the 'outgoing sent' queue for items with the same address, + // interface UID, operation ID and transaction ID. We assume there will + // only be one such item. + TBool sentCommandFound = EFalse; + TSglQueIter& iter = OutgoingSent().SetToFirst(); + CRemConMessage* cmd; + while ( ( cmd = iter++ ) != NULL ) + { + if ( cmd->Addr() == aMsg.Addr() + && cmd->InterfaceUid() == aMsg.InterfaceUid() + && cmd->OperationId() == aMsg.OperationId() + && cmd->TransactionId() == aMsg.TransactionId() + ) + { + LOG1(_L("\tfound a matching item in the sent commands log: [0x%08x]"), cmd); + sentCommandFound = ETrue; + CRemConControllerSession* const session = ControllerSession(cmd->SessionId()); + // When sessions close, their messages are removed from the logs, + // so the session here _should_ exist. + ASSERT_DEBUG(session); + aMsg.SessionId() = cmd->SessionId(); + (void) DeliverMessageToClient(aMsg, *session); + // Remove the found item from the sent message log. + OutgoingSent().RemoveAndDestroy(*cmd); + break; + } + } + if ( !sentCommandFound ) + { + // Either: + // (a) the remote is buggy (sent a response when there wasn't an + // originating command), + // (b) the client closed their controller session before the response + // came back (this cleans up the 'sent commands' log) + LOG(_L("\tno matching item found in sent commands log- response dropped")); + delete &aMsg; + } + + LOGOUTGOINGSENT; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + } + +void CRemConServer::NewNotifyResponse(CRemConMessage& aMsg) + { + LOG_FUNC; + LOGOUTGOINGSENT; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + // Look through the 'outgoing sent' queue for items with the same address, + // interface UID, operation ID and transaction ID. We assume there will + // only be one such item. + TBool sentCommandFound = EFalse; + TSglQueIter& iter = OutgoingSent().SetToFirst(); + CRemConMessage* cmd; + TRemConMessageSubType submessagetype; + submessagetype = aMsg.MsgSubType(); + + while ( ( cmd = iter++ ) != NULL ) + { + if ( cmd->Addr() == aMsg.Addr() + && cmd->InterfaceUid() == aMsg.InterfaceUid() + && cmd->OperationId() == aMsg.OperationId() + && cmd->TransactionId() == aMsg.TransactionId() + ) + { + LOG1(_L("\tfound a matching item in the sent commands log: [0x%08x]"), cmd); + sentCommandFound = ETrue; + CRemConControllerSession* const session = ControllerSession(cmd->SessionId()); + // When sessions close, their messages are removed from the logs, + // so the session here _should_ exist. + ASSERT_DEBUG(session); + aMsg.SessionId() = cmd->SessionId(); + (void) DeliverMessageToClient(aMsg, *session); + + //Do not remove the command if it is an interim response because + //we need to wait for the changed response. + //If the changed response received or error occurs, then remove + //the command from the sent message log + if (submessagetype != ERemConNotifyResponseInterim) + { + OutgoingSent().RemoveAndDestroy(*cmd); + } + + break; + } + } + if ( !sentCommandFound ) + { + // Either: + // (a) the remote is buggy (sent a response when there wasn't an + // originating command), + // (b) the client closed their controller session before the response + // came back (this cleans up the 'sent commands' log) + LOG(_L("\tno matching item found in sent commands log- response dropped")); + delete &aMsg; + } + + LOGOUTGOINGSENT; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + } + +void CRemConServer::NewCommand(CRemConMessage& aMsg) + { + LOG_FUNC; + LOGINCOMINGCMDPENDINGADDRESS; + + IncomingCmdPendingAddress().Append(aMsg); + if ( !iTspAddressingIncomingCommand) + { + AddressIncomingCommand(); + } + + LOGINCOMINGCMDPENDINGADDRESS; + } + +void CRemConServer::NewNotifyCommand(CRemConMessage& aMsg) + { + LOG_FUNC; + LOGINCOMINGNOTIFYCMDPENDINGADDRESS; + if (!iTspIf2) + { + SendReject(aMsg.Addr(), aMsg.InterfaceUid(), aMsg.OperationId(), aMsg.TransactionId()); + delete &aMsg; + } + else + { + IncomingNotifyCmdPendingAddress().Append(aMsg); + if ( !iTspAddressingIncomingNotifyCommand && !iTspReAddressingIncomingNotifyCommands) + { + AddressIncomingNotifyCommand(); + } + } + + LOGINCOMINGNOTIFYCMDPENDINGADDRESS; + } + +#ifdef __FLOG_ACTIVE +void CRemConServer::LogOutgoingNotifyCmdPendingTsp() const + { + LOG(_L("Logging outgoing notify pending TSP commands")); + FTRACE(const_cast(this)->OutgoingNotifyCmdPendingTsp().LogQueue();) + } + +void CRemConServer::LogOutgoingCmdPendingTsp() const + { + LOG(_L("Logging outgoing pending TSP commands")); + FTRACE(const_cast(this)->OutgoingCmdPendingTsp().LogQueue();) + } + +void CRemConServer::LogOutgoingRspPendingTsp() const + { + LOG(_L("Logging outgoing pending TSP responses")); + FTRACE(const_cast(this)->OutgoingRspPendingTsp().LogQueue();) + } + + +void CRemConServer::LogOutgoingPendingSend() const + { + LOG(_L("Logging outgoing pending send")); + FTRACE(const_cast(this)->OutgoingPendingSend().LogQueue();) + } + +void CRemConServer::LogOutgoingSent() const + { + LOG(_L("Logging outgoing sent")); + FTRACE(const_cast(this)->OutgoingSent().LogQueue();) + } + +void CRemConServer::LogIncomingCmdPendingAddress() const + { + LOG(_L("Logging incoming pending address commands")); + FTRACE(const_cast(this)->IncomingCmdPendingAddress().LogQueue();) + } + +void CRemConServer::LogIncomingNotifyCmdPendingAddress() const + { + LOG(_L("Logging incoming pending address notify commands")); + FTRACE(const_cast(this)->IncomingNotifyCmdPendingAddress().LogQueue();) + } + +void CRemConServer::LogIncomingNotifyCmdPendingReAddress() const + { + LOG(_L("Logging incoming pending readdress notify commands")); + FTRACE(const_cast(this)->IncomingNotifyCmdPendingReAddress().LogQueue();) + } + +void CRemConServer::LogIncomingPendingDelivery() const + { + LOG(_L("Logging incoming pending delivery")); + FTRACE(const_cast(this)->IncomingPendingDelivery().LogQueue();) + } + +void CRemConServer::LogIncomingDelivered() const + { + LOG(_L("Logging incoming delivered")); + FTRACE(const_cast(this)->IncomingDelivered().LogQueue();) + } +#endif // __FLOG_ACTIVE + +CRemConControllerSession* CRemConServer::ControllerSession(TUint aSessionId) const + { + LOG_FUNC; + + CRemConControllerSession* sess = NULL; + + TInt index = iControllerSessions.Find(aSessionId, ControllerSessionCompare); + + if(index >= 0) + { + sess = iControllerSessions[index]; + } + + return sess; + } + +CRemConTargetClientProcess* CRemConServer::TargetClient(TUint aClientId) const + { + LOG_FUNC; + + CRemConTargetClientProcess* client = NULL; + + iTargetClientsLock.Wait(); + + TInt index = iTargetClients.Find(aClientId, TargetClientCompareUsingSessionId); + + if(index >= 0) + { + client = iTargetClients[index]; + } + + iTargetClientsLock.Signal(); + + return client; + } + +CRemConTargetClientProcess* CRemConServer::TargetClient(TProcessId aProcessId) const + { + LOG_FUNC; + + CRemConTargetClientProcess* client = NULL; + + iTargetClientsLock.Wait(); + + TInt index = iTargetClients.Find(aProcessId, TargetClientCompareUsingProcessId); + + if(index >= 0) + { + client = iTargetClients[index]; + } + + iTargetClientsLock.Signal(); + + return client; + } + +MRemConConverterInterface* CRemConServer::Converter(TUid aInterfaceUid, + TUid aBearerUid) const + { + ASSERT_DEBUG(iConverterManager); + return iConverterManager->Converter(aInterfaceUid, aBearerUid); + } + +MRemConConverterInterface* CRemConServer::Converter(const TDesC8& aInterfaceData, + TUid aBearerUid) const + { + ASSERT_DEBUG(iConverterManager); + return iConverterManager->Converter(aInterfaceData, aBearerUid); + } + +void CRemConServer::ReceiveRequest(CRemConControllerSession& aSession) + { + LOG_FUNC; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + // Find the first message in IncomingPendingDelivery for this session. + TSglQueIter& iter = IncomingPendingDelivery().SetToFirst(); + CRemConMessage* msg; + while ( ( msg = iter++ ) != NULL ) + { + if ( msg->SessionId() == aSession.Id() ) + { + // Controllers receive responses or rejects only. + ASSERT_DEBUG(msg->MsgType() == ERemConResponse || msg->MsgType() == ERemConReject); + + TInt err = aSession.WriteMessageToClient(*msg); + IncomingPendingDelivery().Remove(*msg); + + // 'Take ownership' of it by destroying it- it's finished with. + delete msg; + + break; + } + } + + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + } + +void CRemConServer::ReceiveRequest(CRemConTargetClientProcess& aClient) + { + LOG_FUNC; + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + + // Messages are addressed to the client. Ask client to deliver any pending + // messages. For each delivered message, update ourselves accordingly. + + // Find the first message in IncomingPendingDelivery for this session. + TSglQueIter& iter = IncomingPendingDelivery().SetToFirst(); + CRemConMessage* msg; + while ( ( msg = iter++ ) != NULL ) + { + if (msg->SessionId() == aClient.Id()) + { + // Targets receive commands only. + ASSERT_DEBUG(msg->MsgType() == ERemConCommand || msg->MsgType() == ERemConNotifyCommand); + + TInt err = aClient.ReceiveMessage(*msg); + + if (err == KErrArgument) + { + // Message not supported by this client. + // 'Take ownership' of it by destroying it- it's finished with. + IncomingPendingDelivery().Remove(*msg); + delete msg; + } + else if (err == KErrNotReady) + { + // Client cannot receive this message at the moment, skip for now + // (message is already on the pemding queue). + } + else + { + // Message delivered, remove from pending queue. + IncomingPendingDelivery().Remove(*msg); + + if (err == KErrNone ) + { + // We'll need to remember it for the response coming back. + IncomingDelivered().Append(*msg); + } + else + { + // Tell bearer it won't be getting a response + CMessageRecipients* messageRecipients = iMessageRecipientsList->Message (msg->TransactionId ()); + + // If we aren't returned a client list, this means that the message has been delivered elsewhere + if (messageRecipients) + { + messageRecipients->RemoveAndDestroyClient (aClient.ClientInfo ()); + + if ( messageRecipients->Clients().IsEmpty ()) + { + iMessageRecipientsList->Messages().Remove (*messageRecipients); + delete messageRecipients; + + SendReject(msg->Addr(), msg->InterfaceUid(), msg->OperationId(), msg->TransactionId()); + } + } + + // 'Take ownership' of it by destroying it- it's finished with. + delete msg; + } + } + } + } + + LOGINCOMINGPENDINGDELIVERY; + LOGINCOMINGDELIVERED; + } + +TBool CRemConServer::FindDuplicateNotify(CRemConMessage& aMsg) + { + ASSERT_DEBUG(aMsg.MsgType() == ERemConNotifyCommand); + TSglQueIter& deliveredIter = IncomingDelivered().SetToFirst(); + CRemConMessage* msg; + while ((msg = deliveredIter++) != NULL) + { + if (msg->MsgType() == ERemConNotifyCommand + && msg->Addr() == aMsg.Addr() + && msg->InterfaceUid() == aMsg.InterfaceUid() + && msg->OperationId() == aMsg.OperationId() + && msg->Client() == aMsg.Client()) + { + return ETrue; + } + } + TSglQueIter& pendingDeliveryIter = IncomingPendingDelivery().SetToFirst(); + while ((msg = pendingDeliveryIter++) != NULL) + { + if (msg->MsgType() == ERemConNotifyCommand + && msg->Addr() == aMsg.Addr() + && msg->InterfaceUid() == aMsg.InterfaceUid() + && msg->OperationId() == aMsg.OperationId() + && msg->Client() == aMsg.Client()) + { + return ETrue; + } + } + return EFalse; + } + +TInt UidCompare(const TUid& aFirst, const TUid& aSecond) + { + if(aFirst.iUid < aSecond.iUid) + { + return -1; + } + else if(aFirst.iUid == aSecond.iUid) + { + return 0; + } + else + { + return 1; + } + } + +/** +Collect all supported interfaces of controller clients. + +@param aSupportedInterfaces An empty RArray which will be populated with the current + supported interfaces. Ownership is retained by the caller. +@return KErrNone if any interfaces were able to be retrieved. This does not + imply that every session's interfaces were able to be retrieved. + KErrNoMemory if no interfaces could be retrived. +*/ +TInt CRemConServer::ControllerSupportedInterfaces(RArray& aSupportedInterfaces) + { + LOG_FUNC; + ASSERT_DEBUG(aSupportedInterfaces.Count() == 0); + + TLinearOrder uidCompare(&UidCompare); + RArray sessionFeatures; + TInt err = KErrNone; + for(TInt i=0; iSupportedInterfaces(sessionFeatures); + ASSERT_DEBUG(err == KErrNone || err == KErrNoMemory); + + if(!err) + { + for(TInt j=0; j 0) + { + return KErrNone; + } + else + { + return KErrNoMemory; + } + } + +CMessageQueue& CRemConServer::OutgoingCmdPendingTsp() + { + ASSERT_DEBUG(iOutgoingCmdPendingTsp); + return *iOutgoingCmdPendingTsp; + } + +CMessageQueue& CRemConServer::OutgoingNotifyCmdPendingTsp() + { + ASSERT_DEBUG(iOutgoingNotifyCmdPendingTsp); + return *iOutgoingNotifyCmdPendingTsp; + } + +CMessageQueue& CRemConServer::OutgoingRspPendingTsp() + { + ASSERT_DEBUG(iOutgoingRspPendingTsp); + return *iOutgoingRspPendingTsp; + } + +CMessageQueue& CRemConServer::OutgoingRspPendingSend() + { + ASSERT_DEBUG(iOutgoingRspPendingSend); + return *iOutgoingRspPendingSend; + } + +CMessageQueue& CRemConServer::OutgoingPendingSend() + { + ASSERT_DEBUG(iOutgoingPendingSend); + return *iOutgoingPendingSend; + } + +CMessageQueue& CRemConServer::OutgoingSent() + { + ASSERT_DEBUG(iOutgoingSent); + return *iOutgoingSent; + } + +CMessageQueue& CRemConServer::IncomingCmdPendingAddress() + { + ASSERT_DEBUG(iIncomingCmdPendingAddress); + return *iIncomingCmdPendingAddress; + } + +CMessageQueue& CRemConServer::IncomingNotifyCmdPendingAddress() + { + ASSERT_DEBUG(iIncomingNotifyCmdPendingAddress); + return *iIncomingNotifyCmdPendingAddress; + } + +CMessageQueue& CRemConServer::IncomingPendingDelivery() + { + ASSERT_DEBUG(iIncomingPendingDelivery); + return *iIncomingPendingDelivery; + } + +CMessageQueue& CRemConServer::IncomingDelivered() + { + ASSERT_DEBUG(iIncomingDelivered); + return *iIncomingDelivered; + } + + +CMessageQueue& CRemConServer::IncomingNotifyCmdPendingReAddress() + { + ASSERT_DEBUG(iIncomingNotifyCmdPendingReAddress); + return *iIncomingNotifyCmdPendingReAddress; + } + +TBool CRemConServer::ConnectionHistoryPointerAtLatest(TUint aSessionId) const + { + LOG_FUNC; + LOG1(_L("\taSessionId = %d"), aSessionId); + +#ifdef _DEBUG + TBool found = EFalse; +#endif + TUint index = 0; + const TUint count = iSession2ConnHistory.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + if ( iSession2ConnHistory[ii].iSessionId == aSessionId ) + { + index = iSession2ConnHistory[ii].iIndex; +#ifdef _DEBUG + found = ETrue; +#endif + break; + } + } + ASSERT_DEBUG(found); + + TBool ret = EFalse; + ASSERT_DEBUG(iConnectionHistory); + if ( index == iConnectionHistory->Count() - 1 ) + { + ret = ETrue; + } + + LOG1(_L("\tret = %d"), ret); + return ret; + } + +CConnections& CRemConServer::Connections() + { + ASSERT_DEBUG(iConnectionHistory); + return iConnectionHistory->Last(); + } + +TInt CRemConServer::HandleConnection(const TRemConAddress& aAddr, TInt aError) + { + LOG_FUNC; + LOG2(_L("\taError = %d, aAddr.BearerUid = 0x%08x"), aError, aAddr.BearerUid()); + LOGREMOTES; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + + // Try to update the connection history. If this fails (it involves memory + // allocation) we need to return the error so the connection will be + // dropped. + if ( aError == KErrNone ) + { + ASSERT_DEBUG(iConnectionHistory); + aError = iConnectionHistory->NewConnection(aAddr); + } + + // If we have a real new connection and we could handle it, aError is + // now KErrNone. In this case, sessions' notifications need completing. + if ( aError == KErrNone ) + { + TUint count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iControllerSessions[ii]); + iControllerSessions[ii]->ConnectionsChanged(); + } + + iTargetClientsLock.Wait(); + count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iTargetClients[ii]); + iTargetClients[ii]->ConnectionsChanged(); + } + iTargetClientsLock.Signal(); + } + + // Complete the specific client request(s) that caused a ConnectRequest on + // the bearer. Tell all sessions- they remember the address they wanted to + // connect to, and will filter on the address we give them. NB This + // function is called by ConnectIndicate as well as by ConnectConfirm, but + // the client doesn't care which end brought the connection up. + const TUint count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iControllerSessions[ii]); + iControllerSessions[ii]->CompleteConnect(aAddr, aError); + } + + // Any messages waiting on OutgoingPendingSend for this connection need to + // be handled. + TSglQueIter& iter = OutgoingPendingSend().SetToFirst(); + CRemConMessage* msg; + TBool moveToSent = EFalse; + while ( ( msg = iter++ ) != NULL ) + { + if ( msg->Addr() == aAddr ) + { + MRemConMessageSendObserver* observer = TargetClient(msg->SessionId()); + if(!observer) + { + observer = ControllerSession(msg->SessionId()); + } +#ifdef __DEBUG + else + { + // Message has matched to a target session, so it should not also match + // a controller session (we know the vice-versa is already true). + ASSERT_DEBUG(!ControllerSession(msg->SessionId())); + } +#endif + + // The session or client should exist- if it doesn't then this message + // wasn't cleaned from OutgoingPendingSend correctly when the + // session closed. The exceptions are Reject, which can be put + // on the queue without a session, and notify changed responses when they are being + // delivered to multiple controllers + ASSERT_DEBUG(observer || msg->MsgType() == ERemConReject || msg->MsgSubType() == ERemConNotifyResponseChanged); + + if ( aError == KErrNone) + { + // We have a connection! + ASSERT_DEBUG(iBearerManager); + TInt err = iBearerManager->Send(*msg); + if ( err == KErrNone + && ((msg->MsgType() == ERemConCommand)||(msg->MsgType() == ERemConNotifyCommand))) + { + // If the send succeeded and it was a command, we move the + // message to the 'sent' log. Otherwise, it's simply + // deleted because we've finished with it. + moveToSent = ETrue; + } + if ( observer && msg->MsgType() != ERemConReject ) + { + observer->MrcmsoMessageSendOneOrMoreResult(*msg, err); + } + } + else + { + // No connection, remember the error. + if ( observer ) + { + if (msg->MsgType() != ERemConReject) + { + observer->MrcmsoMessageSendOneOrMoreResult(*msg, aError); + } + } + } + + if ( moveToSent) + { + OutgoingPendingSend().Remove (*msg); + OutgoingSent().Append (*msg); + } + else + { + OutgoingPendingSend().RemoveAndDestroy (*msg); + } + } + } + + LOGREMOTES; + LOGOUTGOINGPENDINGSEND; + LOGOUTGOINGSENT; + LOG1(_L("\taError = %d"), aError); + return aError; + } + +void CRemConServer::RemoveConnection(const TRemConAddress& aAddr, TInt aError) + { + LOG_FUNC; + LOG1(_L("\taAddr.BearerUid = 0x%08x"), aAddr.BearerUid()); + LOGREMOTES; + + if(aError == KErrNone) + { + // The connection has gone away + + // We make a new item in the connection history and inform the sessions so + // they can complete outstanding connection status notifications. + + ASSERT_DEBUG(iConnectionHistory); + iConnectionHistory->Disconnection(aAddr); + + TUint count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iControllerSessions[ii]); + iControllerSessions[ii]->ConnectionsChanged(); + } + + iTargetClientsLock.Wait(); + count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iTargetClients[ii]); + iTargetClients[ii]->ConnectionsChanged(); + } + iTargetClientsLock.Signal(); + + // If there are any messages waiting on OutgoingPendingSend for this connection, + // we re-connect it - they'll be picked up in HandleConnection above. + + TSglQueIter iter = OutgoingPendingSend().SetToFirst(); + CRemConMessage* msg; + TBool needToReconnect = false; + while ( ( msg = iter++ ) != NULL ) + { + if (msg->Addr() == aAddr) + { + needToReconnect = true; + break; + } + } + + if (needToReconnect) + { + ASSERT_DEBUG(iBearerManager); + TInt err = iBearerManager->Connect(aAddr); + if ( err != KErrNone ) + { + // This fails if: + // 1. we're already connecting (in which case, we don't care) + // 2. we can't add aAddr to the connecting list + // The semantics of this observer don't let us return an error or leave, so + // we can't do much about it here. Log it, and the next command will + // invoke Connect from a better situation. + LOG1(_L("\tFailed to re-connect bearer after connection removed: %d"), err); + } + } + } + + // Complete the specific request(s) that caused a DisconnectRequest on the + // bearer. Tell all sessions- they remember the address they wanted to + // connect to, and will filter on the address we give them. + TInt count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + ASSERT_DEBUG(iControllerSessions[ii]); + iControllerSessions[ii]->CompleteDisconnect(aAddr, aError); + } + + LOGREMOTES; + } + +void CRemConServer::SetConnectionHistoryPointer(TUint aSessionId) + { + LOG_FUNC; + LOG1(_L("\taSessionId = %d"), aSessionId); + LOGCONTROLLERSESSIONS; + LOGTARGETSESSIONS; + LOGCONNECTIONHISTORYANDINTEREST; + + // Update the record for this session. + const TUint count = iSession2ConnHistory.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + if ( iSession2ConnHistory[ii].iSessionId == aSessionId ) + { + ASSERT_DEBUG(iConnectionHistory); + iSession2ConnHistory[ii].iIndex = iConnectionHistory->Count() - 1; + break; + } + } + + // If the calling session was the last session pointing to that item in + // the history, and if it was the earliest item in the history, then we'll + // be able to clean up the history a bit. + UpdateConnectionHistoryAndPointers(); + + LOGCONNECTIONHISTORYANDINTEREST; + } + +const CConnections& CRemConServer::Connections(TUint aSessionId) const + { + LOG_FUNC; + LOG1(_L("\taSessionId = %d"), aSessionId); + + // Get the connection history record for this session. + const CConnections* conns = NULL; + const TUint count = iSession2ConnHistory.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + if ( iSession2ConnHistory[ii].iSessionId == aSessionId ) + { + ASSERT_DEBUG(iConnectionHistory); + conns = &(*iConnectionHistory)[iSession2ConnHistory[ii].iIndex]; + break; + } + } + + ASSERT_DEBUG(conns); + + return *conns; + } + +void CRemConServer::UpdateConnectionHistoryAndPointers() + { + LOG_FUNC; + LOGCONNECTIONHISTORYANDINTEREST; + + // This function is called whenever a session finishes its interest in a + // connection history record, either by closing or by completing + // GetConnections. We remove uninteresting records in the history by + // removing the lowest-indexed item in the history until the + // lowest-indexed item has a session interested in it. As we do so, adjust + // the other sessions' pointers so they're still pointing at the right + // records. + TUint lowestInterestingRecord = KMaxTUint; + const TUint count = iSession2ConnHistory.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + if ( iSession2ConnHistory[ii].iIndex < lowestInterestingRecord ) + { + lowestInterestingRecord = iSession2ConnHistory[ii].iIndex; + } + } + + // In theory, lowestInterestingRecord is now the number of connection + // history records we have to delete, starting with the 0th. This will not + // be the case (lowestInterestingRecord will still be KMaxTUint) if there + // are no sessions left. So adjust lowestInterestingRecord down to + // iConnectionHistory->Count() - 1 so that we remove all but the 'current' + // connection history record. This cleans up as much as possible in case + // server termination is interrupted. + ASSERT_DEBUG(iConnectionHistory); + if ( lowestInterestingRecord >= iConnectionHistory->Count() ) + { + lowestInterestingRecord = iConnectionHistory->Count() - 1; + } + ASSERT_DEBUG(iConnectionHistory); + for ( TUint ii = 0 ; ii < lowestInterestingRecord ; ++ii ) + { + iConnectionHistory->DestroyFirst(); + } + + // We now have to go through iSession2ConnHistory and decrement each + // iIndex by lowestInterestingRecord, to keep _those_ records pointing + // at the right history records. + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + iSession2ConnHistory[ii].iIndex -= lowestInterestingRecord; + } + + LOGCONNECTIONHISTORYANDINTEREST; + } + +TConnectionState CRemConServer::ConnectionState(const TRemConAddress& aAddr) + { + LOG_FUNC; + LOG1(_L("\taaAddr.BearerUid = 0x%08x"), aAddr.BearerUid()); + + TConnectionState ret; + + // Because 'connection state' knowledge is spread across the system + // we have asserts in following if statement to ensure that our state + // is consistent + + // Check if connecting + ASSERT_DEBUG(iBearerManager); + if ( iBearerManager->IsConnecting(aAddr) ) + { + ASSERT_DEBUG(!iBearerManager->IsDisconnecting(aAddr)); + ASSERT_DEBUG(!Connections().Find(aAddr)); + ret = EConnecting; + } + // Check if disconnecting + else if ( iBearerManager->IsDisconnecting(aAddr) ) + { + ASSERT_DEBUG(!iBearerManager->IsConnecting(aAddr)); + // NB Connection remains in connections list until we get DisconnectConfirm + ASSERT_DEBUG(Connections().Find(aAddr)); + ret = EDisconnecting; + } + // Check if connected + else if ( Connections().Find(aAddr) ) + { + ASSERT_DEBUG(!iBearerManager->IsConnecting(aAddr)); + ASSERT_DEBUG(!iBearerManager->IsDisconnecting(aAddr)); + ret = EConnected; + } + // otherwise it's disconnected + else + { + ret = EDisconnected; + } + + LOG1(_L("\tret(connection state) = %d"), ret); + return ret; + } + +void CRemConServer::CommandExpired(TUint aTransactionId) + { + LOG_FUNC; + + CRemConMessage* msg; + + TBool first = ETrue; + + TSglQueIter addressCommandIter = IncomingCmdPendingAddress().SetToFirst(); + + while ((msg = addressCommandIter++) != NULL) + { + if (msg->TransactionId() == aTransactionId) + { + IncomingCmdPendingAddress().RemoveAndDestroy(*msg); + if (first && iTspAddressingIncomingCommand) + { + iTspDropIncomingCommand = ETrue; + } + } + first = EFalse; + } + + TSglQueIter addressNotifyIter = IncomingNotifyCmdPendingAddress().SetToFirst(); + + first = ETrue; + + while ((msg = addressNotifyIter++) != NULL) + { + if (msg->TransactionId() == aTransactionId) + { + IncomingNotifyCmdPendingAddress().RemoveAndDestroy(*msg); + if (first && iTspAddressingIncomingNotifyCommand) + { + iTspDropIncomingNotifyCommand = ETrue; + } + } + first = EFalse; + } + + TSglQueIter reAddressNotifyIter = IncomingNotifyCmdPendingReAddress().SetToFirst(); + + + while ((msg = reAddressNotifyIter++) != NULL) + { + if (msg->TransactionId() == aTransactionId) + { + IncomingNotifyCmdPendingReAddress().RemoveAndDestroy(*msg); + if (first && iTspReAddressingIncomingNotifyCommands) + { + iTspDropIncomingNotifyCommand = ETrue; + } + + } + first = EFalse; + } + + TSglQueIter pendingDeliveryIter = IncomingPendingDelivery().SetToFirst(); + + while ((msg = pendingDeliveryIter++) != NULL) + { + if (msg->TransactionId() == aTransactionId) + { + IncomingPendingDelivery().RemoveAndDestroy(*msg); + } + } + + TSglQueIter deliveredIter = IncomingDelivered().SetToFirst(); + + while ((msg = deliveredIter++) != NULL) + { + if (msg->TransactionId() == aTransactionId) + { + IncomingDelivered().RemoveAndDestroy(*msg); + } + } + ASSERT_DEBUG(iMessageRecipientsList); + iMessageRecipientsList->RemoveAndDestroyMessage(aTransactionId); + } + +TClientInfo* CRemConServer::TargetClientIdToClientInfo(TRemConClientId aId) + { + TClientInfo* clientInfo = NULL; + + iTargetClientsLock.Wait(); + const TUint count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess * const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + if (client->Id() == aId) + { + clientInfo = &client->ClientInfo(); + break; + } + } + iTargetClientsLock.Signal(); + + return clientInfo; + } + +TInt CRemConServer::SupportedInterfaces(const TRemConClientId& aId, RArray& aUids) + { + TUint count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConSession* const sess = iControllerSessions[ii]; + ASSERT_DEBUG(sess); + if (sess->Id() == aId) + { + return sess->SupportedInterfaces(aUids); + } + } + + iTargetClientsLock.Wait(); + count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + if (client->Id() == aId) + { + iTargetClientsLock.Signal(); + return client->SupportedInterfaces(aUids); + } + } + iTargetClientsLock.Signal(); + + return KErrNotFound; + } + +TInt CRemConServer::SupportedOperations(const TRemConClientId& aId, TUid aInterfaceUid, RArray& aOperations) + { + iTargetClientsLock.Wait(); + TUint count = iTargetClients.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConTargetClientProcess* const client = iTargetClients[ii]; + ASSERT_DEBUG(client); + if (client->Id() == aId) + { + iTargetClientsLock.Signal(); + return client->SupportedOperations(aInterfaceUid, aOperations); + } + } + iTargetClientsLock.Signal(); + + count = iControllerSessions.Count(); + for ( TUint ii = 0 ; ii < count ; ++ii ) + { + CRemConControllerSession* const sess = iControllerSessions[ii]; + ASSERT_DEBUG(sess); + if (sess->Id() == aId) + { + return sess->SupportedOperations(aInterfaceUid, aOperations); + } + } + + return KErrNotFound; + } + +void CRemConServer::SetRemoteAddressedClient(const TUid& aBearerUid, const TRemConClientId& aId) + { + LOG_FUNC; + + TClientInfo* clientInfo = TargetClientIdToClientInfo(aId); + // Bearer must supply valid client id + ASSERT_DEBUG(clientInfo); + + ASSERT_DEBUG(iTspIf4); + iTspIf4->SetRemoteAddressedClient(aBearerUid, *clientInfo); + } + +TInt CRemConServer::RegisterLocalAddressedClientObserver(const TUid& aBearerUid) + { + return iTspIf5 ? iTspIf5->RegisterLocalAddressedClientObserver(aBearerUid) : KErrNotSupported; + } + +TInt CRemConServer::UnregisterLocalAddressedClientObserver(const TUid& aBearerUid) + { + return iTspIf5 ? iTspIf5->UnregisterLocalAddressedClientObserver(aBearerUid) : KErrNotSupported; + } + +TRemConClientId CRemConServer::ClientIdByProcessId(TProcessId aProcessId) + { + LOG_FUNC; + TRemConClientId ret = KNullClientId; + iTargetClientsLock.Wait(); + CRemConTargetClientProcess* client = TargetClient(aProcessId); + if(client) + { + ret = client->Id(); + } + iTargetClientsLock.Signal(); + return ret; + } + +void CRemConServer::BulkInterfacesForClientL(TRemConClientId aId, RArray& aUids) + { + LOG_FUNC; + iTargetClientsLock.Wait(); + CleanupSignalPushL(iTargetClientsLock); + CRemConTargetClientProcess* client = TargetClient(aId); + if(!client) + { + LEAVEL(KErrNotFound); + } + LEAVEIFERRORL(client->SupportedBulkInterfaces(aUids)); + CleanupStack::PopAndDestroy(&iTargetClientsLock); + } + + +// Helper Active Objects + +CBulkThreadWatcher::CBulkThreadWatcher(CRemConServer& aServer) + : CActive(CActive::EPriorityStandard) + , iServer(aServer) + { + LOG_FUNC; + } + +CBulkThreadWatcher::~CBulkThreadWatcher() + { + LOG_FUNC; + Cancel(); + } + +void CBulkThreadWatcher::StartL() + { + LOG_FUNC; + // Add to scheduler jit + CActiveScheduler::Add(this); + iServer.iBulkServerThread.Logon(iStatus); + if(iStatus.Int() == KErrNoMemory) + { + User::WaitForRequest(iStatus); // swallow the signal... + // if no memory then we have to fail now otherwise + // we are in an odd state where we don't know if the + // bulk thread is running or not. + LEAVEL(KErrNoMemory); + } + else + { + // Otherwise the request is handled by the active scheduler. + SetActive(); + } + } + +void CBulkThreadWatcher::RunL() + { + LOG_FUNC; + LOG1(_L("\tiStatus.Int() = %d"), iStatus.Int()); + // Thread is dead so kill handle. + iServer.iBulkServerThread.Close(); + iServer.iBulkThreadOpen = EFalse; + iServer.StartShutdownTimerIfNoClientsOrBulkThread(); + iServer.iBulkThreadWatcher = NULL; + delete this; // end... + } + +void CBulkThreadWatcher::DoCancel() + { + LOG_FUNC; + iServer.iBulkServerThread.LogonCancel(iStatus); + } +