diff -r 000000000000 -r 3e07fef1e154 testexecfw/statsrv/device/source/statapi/src/stat_bt.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testexecfw/statsrv/device/source/statapi/src/stat_bt.cpp Mon Mar 08 15:03:44 2010 +0800 @@ -0,0 +1,695 @@ +/* +* Copyright (c) 2005-2009 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: +* +*/ + + + +/******************************************************************************** + * + * System Includes + * + *******************************************************************************/ +#include +#include + +/******************************************************************************** + * + * Local Includes + * + *******************************************************************************/ +#include "assert.h" +#include "ntoh.h" +#include "Stat_bt.h" +#include "../../../../common/inc/SerialPacketSize.h" + +/******************************************************************************** + * + * Macro functions + * + ********************************************************************************/ +#define NOTISDISCONNECTING(s) (((s) != EDisconnectingData) && ((s) != EDisconnectingListen) && ((s) != EDisconnected) && ((s) != EDisconnectingUnregister)) +#define STANDARDBUFVALUE 4 + +/******************************************************************************** + * + * CStatTransportBT -- Constructor + * + *******************************************************************************/ +CStatTransportBT *CStatTransportBT::NewL( void ) +{ + CStatTransportBT *self = new (ELeave) CStatTransportBT(); + CleanupStack::PushL(self); + self->ConstructL( ); + CleanupStack::Pop(); + return self; +} + +CStatTransportBT::CStatTransportBT() : CActive(EPriorityStandard) +{ +} + +void CStatTransportBT::ConstructL( void ) +{ + // add this to active scheduler + CActiveScheduler::Add(this); + + // initialise all params + + iWrCommandData = NULL; + iRecvBuffer = NULL; + iRecvBufferPtr = NULL; + iTransport = NULL; + iBTTransportStatus = EIdle; + iBTTransportDisconnectStatusBeforeUnregister = EIdle; + iRWStatus = ENoRW; + + iMaxPacketSize = KMaxBluetoothPacketSize; +} + +CStatTransportBT::~CStatTransportBT() +{ + // this will call cancel and remove the active object -- this will call cancel + Deque(); + + // cleanup the sockets + switch( iBTTransportStatus ) { + + case EIdle: + case EInitialised: + case EDisconnected: + case EError: + break; + + case EConnected: + iDataSocket.Shutdown( RSocket::EImmediate, iStatus ); + /* fall through */ + + case EConnecting: + iDataSocket.Close(); + iListenSocket.Shutdown( RSocket::EImmediate, iStatus ); + iListenSocket.Close(); + iSocketServ.Close(); + break; + + case EDisconnectingData: + case EDisconnectingListen: + ; + break; + default: + break; + } + + if( iWrCommandData ) + { + delete iWrCommandData; + iWrCommandData = NULL; + } + + if( iRecvBuffer ) + { + delete iRecvBuffer; + iRecvBuffer = NULL; + } + + if( iRecvBufferPtr ) + { + delete iRecvBufferPtr; + iRecvBufferPtr = NULL; + } +} + +/******************************************************************************** + * + * CStatTransportBT -- MStatApiTransport + * + *******************************************************************************/ +TInt CStatTransportBT::InitialiseL( MNotifyStatTransport *aTransport ) +{ + // save the transport interface + iTransport = aTransport; + + // everything here is done in connect + iBTTransportStatus = EInitialised; + iRecHandle = NULL; + return KSTErrSuccess; +} + +TInt CStatTransportBT::Release( void ) +{ + // release has nothing to do (disconnecting unregister is in here in case the original connection failed) + asserte( (iBTTransportStatus == EDisconnected) || (iBTTransportStatus == EInitialised) || (iBTTransportStatus == EDisconnectingUnregister) ); + iBTTransportStatus = EIdle; + return KSTErrSuccess; +} + +//Register with the Security Manager then goes on and does other stuff in the RunL +TInt CStatTransportBT::ConnectL( TDesC* /*aRemoteHost*/ ) +{ + // make sure we are in the correct state + asserte( iBTTransportStatus == EInitialised ); + + + // update the state + iBTTransportStatus = EConnectingRegisterMgr; + + // Force a call to RunL instead of waiting for a call back (CAcitve) after registering + // with the security manager as in V1 + SetActive(); + TRequestStatus* status=&iStatus; + User::RequestComplete(status,KErrNone); + // tell the client to wait for an asynchronous response + return KSTErrAsynchronous; +} + +TInt CStatTransportBT::StartSocketL() +{ + TUint error; + + //make sure we are in the correct state + asserte( iBTTransportStatus == EConnectingRegisterMgr ); + + // connect to the socket server (as we are the receiver and not the initiator), create a socket, bind, listen, accept + User::LeaveIfError( iSocketServ.Connect() ); + + //now select the protocol to use (RFCOMM (serial emulation - boo. Problems with demultiplexing if 1 generic serial port is used for multiple BT connections) or L2CAP) + TProtocolDesc pInfo; + User::LeaveIfError( iSocketServ.FindProtocol(_L("RFCOMM"),pInfo ) ); + + //open the listener socket + User::LeaveIfError( iListenSocket.Open(iSocketServ, pInfo.iAddrFamily, pInfo.iSockType, pInfo.iProtocol) ); + + + // RFComm Socket + TRfcommSockAddr addr; + + // Get First available server channel + addr.SetPort(KRfcommPassiveAutoBind); + + // Set the service security + //Set user defined EPOC TUid to internally represent the service + iServiceSecurity.SetUid( TUid::Uid( 0x1234 ) ); + + //Define security requirements + iServiceSecurity.SetAuthentication( EFalse ); + iServiceSecurity.SetEncryption( EFalse ); + iServiceSecurity.SetAuthorisation( EFalse ); + addr.SetSecurity(iServiceSecurity); + + //bind + User::LeaveIfError( iListenSocket.Bind( addr ) ); + + // Get the assigned port + iPort=iListenSocket.LocalPort(); + + // register with the SDP database + error = RegWithSDPDatabaseL(); + + if( error != KSTErrSuccess ) + { + iTransport->HandleError( error, (void*)iStatus.Int() ); + return KSTErrGeneralFailure; + } + + //listen + User::LeaveIfError( iListenSocket.Listen( KLittleStatBTListenQueue ) ); + + // create a blank socket which is used as the data socket + User::LeaveIfError( iDataSocket.Open(iSocketServ) ); + + // everything should now be set up, we just wait for a stat connection + asserte( !IsActive() ); + iListenSocket.Accept( iDataSocket, iStatus ); + SetActive(); + return KSTErrSuccess; +} + +//------------------------------------------------------------------------------ + +TInt CStatTransportBT::Disconnect( void ) +{ + // must be connected + asserte( (iBTTransportStatus == EInitialised) || + (iBTTransportStatus == EConnected) || + (iBTTransportStatus == EConnectingSockets) || + (iBTTransportStatus == EDisconnectingData) || + (iBTTransportStatus == EDisconnectingListen) || + (iBTTransportStatus == EConnectingRegisterMgr) ); + + // cancel any pending ops + Cancel(); + + iBTTransportDisconnectStatusBeforeUnregister = iBTTransportStatus; + iBTTransportStatus = EDisconnectingUnregister; + + // Force call to RunL instead of waiting for a return from unregistring security service as in bluetooth V1 + SetActive(); + TRequestStatus* iDisconnectStatus=&iStatus; + User::RequestComplete(iDisconnectStatus,KErrNone); + + return KSTErrAsynchronous; +} + +TInt CStatTransportBT::RequestSend( TDesC8 *aData, const TUint aDataLength ) +{ + // make sure the state is correct + asserte( iBTTransportStatus == EConnected ); + asserte( iRWStatus == ENoRW ); + iRWStatus = EWritePending; + + // copy the data to members to local members + if( aDataLength > 0 ) + { + if( iWrCommandData && ( aDataLength != static_cast(iWrCommandData->Length( )) ) ) + { + delete iWrCommandData; + iWrCommandData = NULL; + } + + if( ! iWrCommandData ) + { + iWrCommandData = HBufC8::New( aDataLength ); + + if( ! iWrCommandData ) + { + User::Leave(KErrNoMemory); + } + } + + TPtr8 dataPointer( iWrCommandData->Des() ); + dataPointer.Copy( aData->Ptr(), aDataLength ); + } + + // do the send + asserte( !IsActive() ); + asserte( (unsigned)aData->Length() == aDataLength ); + iDataSocket.Write( *iWrCommandData, iStatus ); + SetActive(); + + // tell the caller to wait for an asynchronous response + return KSTErrAsynchronous; +} + +TInt CStatTransportBT::RequestReceive( TUint aByteCount ) +{ + // ensure that there are no reads in progress + asserte( iBTTransportStatus == EConnected ); + asserte( !IsActive() ); + asserte( iRWStatus == ENoRW ); + iRWStatus = EReadPending; + + asserte( aByteCount <= static_cast(iMaxPacketSize) ); + asserte( !IsActive() ); + + if( iRecvBuffer && ( aByteCount != static_cast(iRecvBuffer->Length( )) ) ) + { + delete iRecvBuffer; + iRecvBuffer = NULL; + delete iRecvBufferPtr; + iRecvBufferPtr = NULL; + } + + if( ! iRecvBuffer ) + { + iRecvBuffer = HBufC8::New( aByteCount ); + + if( ! iRecvBuffer ) + { + User::Leave(KErrNoMemory); + } + + iRecvBufferPtr = new TPtr8( + const_cast(iRecvBuffer->Ptr( )), + aByteCount, aByteCount ); + + if( ! iRecvBufferPtr ) + { + User::Leave(KErrNoMemory); + } + +#ifdef _DEBUG + // Check the size of the pointer is the same + // as the size of the buffer. + TInt length = iRecvBufferPtr->Length( ); + length = 0; // Extra likne to remove compiler warning. +#endif // def _DEBUG + } + + iDataSocket.Read( *iRecvBufferPtr, iStatus ); + SetActive(); + + // return to the caller + return KSTErrAsynchronous; +} + +TText8 *CStatTransportBT::Error( void ) +{ + return NULL; +} + +TInt CStatTransportBT::GetPacketSize() +{ + // The packet size is configured when we initialise the port. + return iMaxPacketSize; +} + + +//Register it in the SDP database - determine attribs, construct the record and then add attribs to new record +TInt CStatTransportBT::RegWithSDPDatabaseL( void ) +{ + asserte( iBTTransportStatus == EConnectingRegisterMgr ); + + //Connect and open to the session and the DB + //User::LeaveIfError( iSdpSession.Connect() ); + + TInt ret2; + ret2 = iSdpSession.Connect(); + + if(ret2!=KErrNone) + { + User::Leave(ret2); + } + + User::LeaveIfError( iSdpDatabaseSession.Open( iSdpSession ) ); + TBuf8 value1; + + TBuf8 value2; + CSdpAttrValue* attrVal = 0; + CSdpAttrValueDES* attrValDES = 0; + + //initialise + value1.FillZ(STANDARDBUFVALUE); + value2.FillZ(STANDARDBUFVALUE); + + // Set Attr 1 (service class list) to list with UUID = 0x1101 (serial port) + iSdpDatabaseSession.CreateServiceRecordL(TUUID(0x1101), iRecHandle); + + // Set Service name + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdBasePrimaryLanguage + + KSdpAttrIdOffsetServiceName, + _L("STATAPI") ); + + // Set Service description + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdBasePrimaryLanguage + + KSdpAttrIdOffsetServiceDescription, + _L("Symbian Test Automation Tool using Serial BT") ); + + attrVal = CSdpAttrValueString::NewStringL( _L8( "Test Solutions Dept Symbian Ltd." ) ); + CleanupStack::PushL(attrVal); + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdBasePrimaryLanguage + + KSdpAttrIdOffsetProviderName, *attrVal); + CleanupStack::PopAndDestroy(); //attrVal + attrVal = 0; + + // Set Attr 2 (service record state) to 0 + attrVal = CSdpAttrValueUint::NewUintL(value1); + CleanupStack::PushL(attrVal); + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdServiceRecordState, *attrVal); + CleanupStack::PopAndDestroy(); //attrVal + attrVal = 0; + + // Set attr 4 (protocol list) to RFCOMM + //initialise + TBuf8<1> serverChannel; + serverChannel.FillZ(1); + serverChannel[0] = (unsigned char)iPort; + + attrValDES = CSdpAttrValueDES::NewDESL(0); + CleanupStack::PushL(attrValDES); + + attrValDES->StartListL() + ->BuildDESL()->StartListL() + ->BuildUUIDL( TUUID( TUint16( 0x0003 ) ) ) // RFCOMM + ->BuildUintL( serverChannel ) //Channel ID = 3 (listening port) + ->EndListL() + ->EndListL(); + + //update attr 4 + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdProtocolDescriptorList, *attrValDES); + CleanupStack::PopAndDestroy(); //attrValDES + attrValDES = 0; + + // Set Attr 5 (browse group list) to list with one UUID + // 0x1101 (serial port class) + // this should be updated with other service classes when other services are added. + attrValDES = CSdpAttrValueDES::NewDESL(0); + CleanupStack::PushL(attrValDES); + + attrValDES->StartListL() + ->BuildUUIDL( TUUID( 0x1002 ) ) + ->EndListL(); + + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdBrowseGroupList, *attrValDES); + CleanupStack::PopAndDestroy(); + attrValDES = 0; + + // Set Attr 0x006 (language base) + value1.FillZ(4); + value1[2] = 0x65; + value1[3] = 0x6e; + TBuf8 val2; + TBuf8 val3; + val2.FillZ(STANDARDBUFVALUE); + val3.FillZ(STANDARDBUFVALUE); + val2[3] = 0x6a; + val3[2] = 0x01; + + attrValDES = CSdpAttrValueDES::NewDESL(0); + CleanupStack::PushL(attrValDES); + + attrValDES->StartListL() + ->BuildUintL( value1 ) // speka de english + ->BuildUintL( val2 ) // UTF-8 + ->BuildUintL( val3 ) // language base + ->EndListL(); + + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdLanguageBaseAttributeIDList, *attrValDES); + CleanupStack::PopAndDestroy(); + attrValDES = 0; + + // Set Attr 0x007 (time to live) to 600 (0x258) seconds (10 minutes) + //initialise buffer + value1.FillZ(4); + value1[2]=2; + value1[3]=0x58; + + attrVal = CSdpAttrValueUint::NewUintL( value1 ); + CleanupStack::PushL( attrVal ); + iSdpDatabaseSession.UpdateAttributeL( iRecHandle, KSdpAttrIdServiceInfoTimeToLive, *attrVal ); + CleanupStack::PopAndDestroy(); //attrVal + attrVal = 0; + + //Set Attr 0x08 (availability) to 0xff - fully available - not in use + //initialise + TBuf8<1> val4; + val4.FillZ(1); + val4[0]=0xff; + + attrVal = CSdpAttrValueUint::NewUintL(val4); + CleanupStack::PushL(attrVal); + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdServiceAvailability, *attrVal); + CleanupStack::PopAndDestroy(); //attrVal + attrVal = 0; + + //Set Attr 0x201 (service database state) to 0 + //initialise + value1.FillZ(4); + + attrVal = CSdpAttrValueUint::NewUintL(value1); + CleanupStack::PushL(attrVal); + iSdpDatabaseSession.UpdateAttributeL(iRecHandle, KSdpAttrIdSdpServerServiceDatabaseState, *attrVal); + CleanupStack::PopAndDestroy(); //attrVal + attrVal = 0; + + return KSTErrSuccess; +} + +/******************************************************************************** + * + * CStatTransportBT -- Active Object + * + *******************************************************************************/ +void CStatTransportBT::RunL( void ) +{ + TInt error = KErrNone; + + // if there was an error during connecting then tell the engine this + if( (iBTTransportStatus == EConnectingSockets) && (iStatus != KErrNone) ) { + + iTransport->HandleError( KSTErrConnectFailure, (void*)iStatus.Int() ); + return; + } + + // the other end have disconnected. just cleanup the resource by calling Disconnect function. + if(iStatus == KErrDisconnected && iBTTransportStatus == EConnected) { + + Disconnect(); + return; + } + + // if there was any other error then also tell the engine about it + if( (iStatus != KErrNone) && NOTISDISCONNECTING(iBTTransportStatus) ) { + + iTransport->HandleError( KSTErrGeneralFailure, (void*)iStatus.Int() ); + return; + } + + // Now we are registered with the security manager, reg with the SDP + if( iBTTransportStatus == EConnectingRegisterMgr ) + { + // start the socket and make it listen (async call) + error = StartSocketL(); + if( error != KSTErrSuccess ) + { + iTransport->HandleError( error, (void*)iStatus.Int() ); + return; + } + + iBTTransportStatus = EConnectingSockets; + return; + } + + // We are now connected + if( iBTTransportStatus == EConnectingSockets ) + { + iBTTransportStatus = EConnected; + iTransport->HandleConnect( KErrNone ); + return; + } + + // handle unregister service + if( iBTTransportStatus == EDisconnectingUnregister ) { + HandleAsyncDisconnect(); + return; + } + + // handle shutdown data socket + if( iBTTransportStatus == EDisconnectingData ) { + iDataSocket.Close(); + iListenSocket.Shutdown( RSocket::ENormal, iStatus ); + SetActive(); + iBTTransportStatus = EDisconnectingListen; + return; + } + + // handle shutdown listen + if( iBTTransportStatus == EDisconnectingListen ) { + iListenSocket.Close(); + iBTTransportStatus = EDisconnected; + iSocketServ.Close(); + iTransport->HandleDisconnect( KErrNone ); + return; + } + + // if we are writing then notify of the write + if( iRWStatus == EWritePending ) { + iRWStatus = ENoRW; + asserte( iWrCommandData != NULL ); + iTransport->HandleSend( KErrNone ); + return; + } + + // if we are reading then notify of the read + if( iRWStatus == EReadPending ) { + iRWStatus = ENoRW; + TInt length = iRecvBufferPtr->Length( ); + iTransport->HandleReceive( KErrNone, iRecvBufferPtr, length ); + return; + } +} + +//------------------------------------------------------------------------------ + +void CStatTransportBT::DoCancel( void ) +{ + // if we are connecting then cancel the connect() call + if( iBTTransportStatus == EConnectingSockets ) + { + iListenSocket.CancelAccept(); + } + + if( iRWStatus == EReadPending ) + { + iDataSocket.CancelRead(); + } + + if( iRWStatus == EWritePending ) + { + iDataSocket.CancelWrite(); + } + + iRWStatus = ENoRW; +} + + +//------------------------------------------------------------------------------ +// +// Handle async disconnect is called by the RunL when we get a successful response +// to the unregister service request. We can now shutdown the transport normally, +// but we have to know which state it had reached. +// +//------------------------------------------------------------------------------ +void CStatTransportBT::HandleAsyncDisconnect( void ) +{ + TInt tv = KErrNone; + + if(iRecHandle != NULL) + { + //Close the SDP database objects and delete the record + TRAP( tv, iSdpDatabaseSession.DeleteRecordL( iRecHandle) ); + if( tv != KErrNone ) + { + _LIT(KErrDelRecord,"An error occured while deleting the SDP record."); + + iTransport->HandleInfo(&KErrDelRecord); + } + } + + iSdpDatabaseSession.Close(); + iSdpSession.Close(); + iRecHandle = NULL; + + // clean up the sockets depending on the state + switch( iBTTransportDisconnectStatusBeforeUnregister ) { + + case EConnected: + iDataSocket.Shutdown( RSocket::ENormal, iStatus ); + SetActive(); + iBTTransportStatus = EDisconnectingData; + break; + + case EConnecting: + case EDisconnectingData: + iDataSocket.Close(); + iListenSocket.Shutdown( RSocket::ENormal, iStatus ); + SetActive(); + iBTTransportStatus = EDisconnectingListen; + break; + + case EInitialised: + case EDisconnectingListen: + case EConnectingRegisterMgr: + // initialised may mean that ConnectL threw an error -- so close the resources + iDataSocket.Close(); + iListenSocket.Close(); + iSocketServ.Close(); + iTransport->HandleDisconnect( KErrNone ); + break; + + default: + ; + } +} +