diff -r 22de2e391156 -r 20ac952a623c remotecontrol/remotecontrolfw/server/src/session.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotecontrol/remotecontrolfw/server/src/session.cpp Wed Oct 13 16:20:29 2010 +0300 @@ -0,0 +1,898 @@ +// 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 session implementation. +// +// + +/** + @file + @internalComponent +*/ +#include "e32base.h" + +#include +#include +#include +#include +#include "utils.h" +#include "server.h" +#include "bearermanager.h" +#include "remconmessage.h" +#include "connections.h" +#include "session.h" +#include "messagequeue.h" +#include "remconserver.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_REMCON_SERVER); +#endif + +#ifdef _DEBUG +PANICCATEGORY("session"); +#endif + + +CRemConSession::CRemConSession(CRemConServer& aServer, + CBearerManager& aBearerManager, + TUint aId) + : iServer(aServer), + iBearerManager(aBearerManager), + iId(aId) + { + LOG_FUNC; + } + +void CRemConSession::BaseConstructL(const TClientInfo& aClientInfo) + { + LOG_FUNC; + + iClientInfo = aClientInfo; + + iSendQueue = CMessageQueue::NewL(); + + // The send callback is used by the base class to handle queued sends. + iSendNextCallBack = new(ELeave) CAsyncCallBack(CActive::EPriorityStandard); + TCallBack cb(SendNextCb, this); + iSendNextCallBack->Set(cb); + + } + +CRemConSession::~CRemConSession() + { + LOG(KNullDesC8); + LOG_FUNC; + + delete iSendNextCallBack; + delete iSendQueue; + delete iInterestedAPIs; + } + +void CRemConSession::ServiceL(const RMessage2& aMessage) + { + LOG(KNullDesC8); + LOG_FUNC; + LOG1(_L("\taMessage.Function() = %d"), aMessage.Function()); + // Switch on the IPC number and call a 'message handler'. Message handlers + // complete aMessage (either with Complete or Panic), or make a note of + // the message for later asynchronous completion. + // Message handlers should not leave- the server does not have an Error + // function. + + switch ( aMessage.Function() ) + { + // Heap failure testing APIs. + case ERemConDbgMarkHeap: +#ifdef _DEBUG + LOG(_L("\tmark heap")); + __UHEAP_MARK; +#endif // _DEBUG + CompleteClient(aMessage, KErrNone); + break; + + case ERemConDbgCheckHeap: +#ifdef _DEBUG + LOG1(_L("\tcheck heap (expecting %d cells)"), aMessage.Int0()); + __UHEAP_CHECK(aMessage.Int0()); +#endif // _DEBUG + CompleteClient(aMessage, KErrNone); + break; + + case ERemConDbgMarkEnd: +#ifdef _DEBUG + LOG1(_L("\tmark end (expecting %d cells)"), aMessage.Int0()); + __UHEAP_MARKENDC(aMessage.Int0()); +#endif // _DEBUG + CompleteClient(aMessage, KErrNone); + break; + + case ERemConDbgFailNext: +#ifdef _DEBUG + { + LOG1(_L("\tfail next (simulating failure after %d allocation(s))"), aMessage.Int0()); + if ( aMessage.Int0() == 0 ) + { + __UHEAP_RESET; + } + else + { + __UHEAP_FAILNEXT(aMessage.Int0()); + } + } +#endif // _DEBUG + CompleteClient(aMessage, KErrNone); + break; + + case ERemConSetPlayerType: + SetPlayerType(aMessage); + // This is a sync API- check that the message has been completed. + // (NB We don't check the converse for async APIs because the message + // may have been panicked synchronously.) + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConSend: + Send(aMessage); + break; + + case ERemConSendUnreliable: + SendUnreliable(aMessage); + break; + + case ERemConSendCancel: + SendCancel(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConReceive: + Receive(aMessage); + break; + + case ERemConReceiveCancel: + ReceiveCancel(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConGetConnectionCount: + GetConnectionCount(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConGetConnections: + GetConnections(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConNotifyConnectionsChange: + NotifyConnectionsChange(aMessage); + break; + + case ERemConNotifyConnectionsChangeCancel: + NotifyConnectionsChangeCancel(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConRegisterInterestedAPIs: + RegisterInterestedAPIs(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConGoConnectionOriented: + GoConnectionOriented(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConGoConnectionless: + GoConnectionless(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConConnectBearer: + ConnectBearer(aMessage); + break; + + case ERemConConnectBearerCancel: + ConnectBearerCancel(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConDisconnectBearer: + DisconnectBearer(aMessage); + break; + + case ERemConDisconnectBearerCancel: + DisconnectBearerCancel(aMessage); + ASSERT_DEBUG(aMessage.IsNull()); + break; + + case ERemConSendNotify: + SendNotify(aMessage); + break; + + default: + // Unknown message + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicIllegalIpc); + break; + } + } + +void CRemConSession::CompleteClient(const RMessage2& aMessage, TInt aError) + { + LOG1(_L("\tcompleting client message with %d"), aError); + TBool cleanClientInfoMessage = (iClientInfo.Message().Handle() == aMessage.Handle()); + aMessage.Complete(aError); + if(cleanClientInfoMessage) + { + iClientInfo.Message() = RMessage2(); + } + } + +void CRemConSession::GetPlayerTypeAndNameL(const RMessage2& aMessage, TPlayerTypeInformation& aPlayerType, RBuf8& aPlayerName) + { + // check validity of descriptors + if (aMessage.GetDesLength(1) < 0 || aMessage.GetDesLength(2) < 0) + { + LEAVEL(KErrBadDescriptor); + } + + // Retrieve and validate the client type information + TPckg pckg(aPlayerType); + aMessage.ReadL(1, pckg); + switch (aPlayerType.iPlayerType) + { + case ERemConAudioPlayer: + // Valid + case ERemConVideoPlayer: + // Valid + case ERemConBroadcastingAudioPlayer: + // Valid + case ERemConBroadcastingVideoPlayer: + // Valid + break; + default: + // Invalid + LEAVEL(KErrArgument); + } + switch (aPlayerType.iPlayerSubType) + { + case ERemConNoSubType: + // Valid + case ERemConAudioBook: + // Valid + case ERemConPodcast: + // Valid + break; + default: + // Invalid + LEAVEL(KErrArgument); + } + + // Retrieve the client player name inforamtion + aPlayerName.CreateL(aMessage.GetDesLengthL(2)); + CleanupClosePushL(aPlayerName); + aMessage.ReadL(2, aPlayerName); + CleanupStack::Pop(&aPlayerName); + } + +void CRemConSession::Send(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we're not already sending... + if ( iSendMsg.Handle()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicSendAlreadyOutstanding); + return; + } + + iSendMsg = aMessage; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + // Before we ask the server to send, we must set our ClientInfo + // correctly so the TSP can get information about the client. + iClientInfo.Message() = aMessage; + + // Prepare the message for send. If DoPrepareSendMessageL() returns + // NULL, it panicked the client. + CRemConMessage* msg = NULL; + TRAPD(err, msg = DoPrepareSendMessageL(aMessage)); + + if ( err != KErrNone ) + { + CompleteClient(aMessage, err); + } + else if (msg) + { + ASSERT_DEBUG(iSendQueue); + + if (iSending != ENotSending || !iSendQueue->IsEmpty()) + { + iSendQueue->Append(*msg); + } + else + { + // we know msg cannot be null here as said above. + SendToServer(*msg); + } + } + } + +void CRemConSession::SendCancel(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + // See comments in ConnectBearerCancel. + if ( iSendMsg.Handle() ) + { + DoSendCancel(); + } + + if (iSendMsg.Handle()) + { + CRemConMessage* msg; + TBool first = ETrue; + ASSERT_DEBUG(iSendQueue); + TSglQueIter& iter = iSendQueue->SetToFirst(); + while ((msg = iter++) != NULL) + { + if (msg->IsReliableSend()) + { + CompleteClient(iSendMsg, KErrCancel); + iSendQueue->RemoveAndDestroy(*msg); + if (first) + { + ASSERT_DEBUG(iSendNextCallBack); + iSendNextCallBack->Cancel(); + } + break; + } + first = EFalse; + } + } + + CompleteClient(aMessage, KErrNone); + } + +void CRemConSession::Receive(const RMessage2& aMessage) + { + LOG_FUNC; + + // Messages are pushed from bearers, so we + // (a) do some sanity checking, + // (b) check the queue of incoming messages in case there's anything + // already waiting to be given to the client. + + if ( iReceiveMsg.Handle() ) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicReceiveAlreadyOutstanding); + return; + } + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + iReceiveMsg = aMessage; + + DoReceive(); + } + +void CRemConSession::ReceiveCancel(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + // See comments in ConnectBearerCancel. + if ( iReceiveMsg.Handle() ) + { + CompleteClient(iReceiveMsg, KErrCancel); + } + CompleteClient(aMessage, KErrNone); + } + +void CRemConSession::GetConnectionCount(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + // Get the answer to the question- the number of connections at the + // current point in time (i.e. the latest entry in the connection + // history). + const TUint connCount = iServer.Connections().Count(); + LOG1(_L("\tconnCount = %d"), connCount); + TPckg count(connCount); + TInt err = aMessage.Write(0, count); + + // If the client was told the answer with no error, then remember the + // current point in the connection history, so that when the client asks + // for the connections themselves, we give them a consistent answer. + if ( err == KErrNone ) + { + iServer.SetConnectionHistoryPointer(Id()); + iInGetConnectionsProcedure = ETrue; + } + CompleteClient(aMessage, err); + } + +void CRemConSession::GetConnections(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + iInGetConnectionsProcedure = EFalse; + + // Get the array of connections at the point in the history we're + // interested in and write it back to the client. NB This is not + // necessarily the Last item in the history but the item that we were + // pointing at when GetConnectionCount was called. + const CConnections& conns = iServer.Connections(iId); + const TUint count = conns.Count(); + LOG1(_L("\tcount = %d"), count); + RBuf8 buf; + TInt err = buf.Create(count * sizeof(TRemConAddress)); + if ( err == KErrNone ) + { + TSglQueIter& iter = conns.SetToFirst(); + TRemConAddress* addr; + while ( ( addr = iter++ ) != NULL ) + { + buf.Append((TUint8*)addr, sizeof(TRemConAddress)); + } + + // Write back to the client... + err = aMessage.Write(0, buf); + buf.Close(); + if ( err != KErrNone ) + { + // We don't need to call SetConnectionHistoryPointer here because + // the server will do it when it cleans up the panicked client. + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor); + return; + } + } + + // Whether or not there was an error, we're no longer interested in the + // history item we're currently registered as being interested in, so tell + // the server to bump up our pointer to the current (latest) one. NB This + // may in fact be the same record, if no connection changes have occurred + // since GetConnectionCount was called, but it's still important to give + // the server a chance to remove obsolete history records. + iServer.SetConnectionHistoryPointer(Id()); + CompleteClient(aMessage, err); + } + +void CRemConSession::NotifyConnectionsChange(const RMessage2& aMessage) + { + LOG_FUNC; + + // Messages are pushed to us from bearers, so we don't need anything more + // than some sanity checking here. + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + if ( iNotifyConnectionsChangeMsg.Handle() ) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicConnectionsNotificationAlreadyOutstanding); + } + else + { + iNotifyConnectionsChangeMsg = aMessage; + // Check the connection history for any more recent items than that we + // currently know about. If our pointer into the connection history + // isn't pointing at the 'current' item, we can complete the + // notification immediately and move the pointer up. + if ( !iServer.ConnectionHistoryPointerAtLatest(Id()) ) + { + CompleteClient(iNotifyConnectionsChangeMsg, KErrNone); + iServer.SetConnectionHistoryPointer(Id()); + } + } + } + +void CRemConSession::NotifyConnectionsChangeCancel(const RMessage2& aMessage) + { + LOG_FUNC; + + // Check we've had our features set... + if (!ClientAvailable()) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet); + return; + } + + // See comments in ConnectBearerCancel. + if ( iNotifyConnectionsChangeMsg.Handle() ) + { + CompleteClient(iNotifyConnectionsChangeMsg, KErrCancel); + } + CompleteClient(aMessage, KErrNone); + } + +CRemConInterfaceDetailsArray* CRemConSession::ExtractInterestedAPIsL(const RMessage2& aMessage) + { + LOG_FUNC; + + CRemConInterfaceDetailsArray* result; + + RBuf8 buf; + buf.CreateL(aMessage.GetDesLengthL(0)); + CleanupClosePushL(buf); + + aMessage.ReadL(0, buf); + RDesReadStream ipcStream(buf); + + result = CRemConInterfaceDetailsArray::InternalizeL(ipcStream); + + ipcStream.Close(); + CleanupStack::PopAndDestroy(&buf); + + return result; + } + +TBool CRemConSession::DoGetSendInfoLC(const RMessage2& aMessage, + TUid& aInterfaceUid, + TUint& aOperationId, + TRemConMessageSubType& aMessageSubType, + RBuf8& aSendDes) + { + // Get the data the client wants to send. + aInterfaceUid = TUid::Uid(aMessage.Int0()); + LOG1(_L("\taInterfaceUid = 0x%08x"), aInterfaceUid); + + if (aMessage.GetDesLengthL(1) != sizeof(TOperationInformation)) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor); + return EFalse; + } + + TPckgBuf opInfoPckg; + + TInt err= aMessage.Read( + 1, // location of the descriptor in the client's message (as we expect them to have set it up) + opInfoPckg, // descriptor to write to from client memory space + 0 // offset into our descriptor to put the client's data + ); + + if ( err != KErrNone ) + { + LOG1(_L("\taMessage.Read = %d"), err); + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor); + return EFalse; + } + + aOperationId = opInfoPckg().iOperationId; + LOG1(_L("\taOperationId = 0x%02x"), aOperationId); + + aMessageSubType = opInfoPckg().iMessageSubType; + LOG1(_L("\taMessageSubType = 0x%02x"), aMessageSubType); + + const TUint dataLength = (TUint)aMessage.GetDesLengthL(3); + LOG1(_L("\tdataLength = %d"), dataLength); + + // If the client wanted to send some operation-associated data, read it + // from them. + if ( dataLength != 0 ) + { + aSendDes.CreateL(dataLength); + TInt err = aMessage.Read( + 3, // location of the descriptor in the client's message (as we expect them to have set it up) + aSendDes, // descriptor to write to from client memory space + 0 // offset into our descriptor to put the client's data + ); + // NB We don't do LEAVEIFERRORL(aMessage.Read) because a bad client + // descriptor is a panicking offence for them, not an 'error the + // request' offence. + if ( err != KErrNone ) + { + LOG1(_L("\taMessage.Read = %d"), err); + aSendDes.Close(); + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor); + return EFalse; + } + } + CleanupClosePushL(aSendDes); + return ETrue; + } + +void CRemConSession::GoConnectionOriented(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::GoConnectionless(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::ConnectBearer(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::ConnectBearerCancel(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::DisconnectBearer(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::DisconnectBearerCancel(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::SendNotify(const RMessage2& aMessage) + { + PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType); + } + +void CRemConSession::ConnectionsChanged() + { + LOG_FUNC; + + LOG1(_L("\tiInGetConnectionsProcedure = %d"), iInGetConnectionsProcedure); + // Only update the connections history pointer if we're not in the middle + // of a 'GetConnections' procedure. + if ( !iInGetConnectionsProcedure ) + { + iServer.SetConnectionHistoryPointer(Id()); + } + LOG1(_L("\tiNotifyConnectionsChangeMsg.Handle = %d"), iNotifyConnectionsChangeMsg.Handle()); + if ( iNotifyConnectionsChangeMsg.Handle() ) + { + CompleteClient(iNotifyConnectionsChangeMsg, KErrNone); + } + } + +void CRemConSession::CompleteSend() + { + LOG_FUNC; + LOG2(_L("\tiNumRemotes = %d, iSendError = %d"), iNumRemotes, iSendError); + + ASSERT_DEBUG(NumRemotesToTry() == 0); + + if (iSending == ESendingReliable) + { + if ( iSendError == KErrNone ) + { + TPckg count(iNumRemotes); + // 2 is the slot in the client's message for the number of remotes the + // message got sent to. + iSendError = iSendMsg.Write(2, count); + } + CompleteClient(iSendMsg, iSendError); + } + + ASSERT_DEBUG(iSendQueue); + if (!iSendQueue->IsEmpty()) + { + ASSERT_DEBUG(iSendNextCallBack); + iSendNextCallBack->CallBack(); + } + iSending = ENotSending; + } + +void CRemConSession::CompleteSendNotify() + { + LOG_FUNC; + LOG1(_L("\tiSendError = %d"), iSendError); + + if (iSending == ESendingReliable) + { + CompleteClient(iSendMsg, iSendError); + } + + ASSERT_DEBUG(iSendQueue); + if (!iSendQueue->IsEmpty()) + { + ASSERT_DEBUG(iSendNextCallBack); + iSendNextCallBack->CallBack(); + } + iSending = ENotSending; + } + +void CRemConSession::PanicSend(TRemConClientPanic aCode) + { + LOG_FUNC; + LOG1(_L("\taCode = %d"), aCode); + + PANIC_MSG(iSendMsg, KRemConClientPanicCat, aCode); + } + +TInt CRemConSession::WriteMessageToClient(const CRemConMessage& aMsg) + { + LOG_FUNC; + + ASSERT_DEBUG(SupportedMessage(aMsg)); + ASSERT_DEBUG(iReceiveMsg.Handle()); + TRAPD(err, WriteMessageToClientL(aMsg)); + CompleteClient(iReceiveMsg, err); + + LOG1(_L("\terr = %d"), err); + return err; + } + +void CRemConSession::WriteMessageToClientL(const CRemConMessage& aMsg) + { + LOG_FUNC; + + //check if our client is interested in this API + //Only need to check commands because it is safe to assume that we are interested + //in the response if we have sent out a command. + if(aMsg.MsgType() == ERemConCommand && !FindInterfaceByUid(aMsg.InterfaceUid())) + { + //The server will clean up the resource allocated for this msg + LEAVEL(KErrArgument); + } + + // This logging code left in for maintenance. + //LOG1(_L("\t\tOperationData = \"%S\""), &aMsg.OperationData()); + + TRemConClientReceivePackage receivePackage; + receivePackage.iInterfaceUid = aMsg.InterfaceUid(); + receivePackage.iOperationId = aMsg.OperationId(); + receivePackage.iMessageSubType = aMsg.MsgSubType(); + receivePackage.iRemoteAddress = aMsg.Addr(); + + TPckgC recPckg(receivePackage); + LEAVEIFERRORL(iReceiveMsg.Write(0, recPckg)); + + // Note that we do not panic the client if their descriptor is not + // big enough to hold the operation-specific data. If we did, then + // a buggy remote could take down a client of RemCon. Just error + // the client instead. + LEAVEIFERRORL(iReceiveMsg.Write(1, aMsg.OperationData())); + } + +TInt CRemConSession::SupportedInterfaces(RArray& aUids) + { + LOG_FUNC; + + aUids.Reset(); + return AppendSupportedInterfaces(aUids); + } + +TInt CRemConSession::AppendSupportedInterfaces(RArray& aUids) + { + LOG_FUNC; + ASSERT_DEBUG(iInterestedAPIs); + TInt err = KErrNone; + + TInt count = iInterestedAPIs->Array().Count(); + for(TInt i=0; (iArray()[i]; + ASSERT_DEBUG(details); + err = aUids.Append(details->Uid()); + } + + return err; + } + +TInt CRemConSession::SupportedBulkInterfaces(RArray& aUids) + { + LOG_FUNC; + + aUids.Reset(); + return AppendSupportedBulkInterfaces(aUids); + } + +TInt CRemConSession::AppendSupportedBulkInterfaces(RArray& aUids) + { + LOG_FUNC; + ASSERT_DEBUG(iInterestedAPIs); + TInt err = KErrNone; + + TInt count = iInterestedAPIs->Array().Count(); + for(TInt i=0; (iArray()[i]; + ASSERT_DEBUG(details); + if(details->IsBulk()) + { + err = aUids.Append(details->Uid()); + } + } + + return err; + } + +TInt CRemConSession::SupportedOperations(TUid aInterfaceUid, RArray& aOperations) + { + LOG_FUNC; + TInt err = KErrNotSupported; + CRemConInterfaceDetails* details = FindInterfaceByUid(aInterfaceUid); + + if(details) + { + TRAP(err, details->GetRemConInterfaceFeaturesL(aOperations)); + } + return err; + } + +CRemConInterfaceDetails* CRemConSession::FindInterfaceByUid(TUid aUid) const + { + LOG_FUNC; + ASSERT_DEBUG(iInterestedAPIs); + TInt count = iInterestedAPIs->Array().Count(); + for(TInt ix=0; ixArray()[ix]; + ASSERT_DEBUG(details); + if(details->Uid() == aUid) + { + return details; + } + } + return NULL; + } + +void CRemConSession::DoSendNext() + { + LOG_FUNC; + + ASSERT_DEBUG(iSendQueue); + CRemConMessage& msg = iSendQueue->First(); + iSendQueue->Remove(msg); + SendToServer(msg); + } + +TInt CRemConSession::SendNextCb(TAny *aThis) + { + LOG_STATIC_FUNC; + + static_cast(aThis)->DoSendNext(); + return KErrNone; + } +