diff -r 000000000000 -r d0791faffa3f obex/obexprotocol/obex/src/obexserverstatemachine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/obex/obexprotocol/obex/src/obexserverstatemachine.cpp Tue Feb 02 01:11:40 2010 +0200 @@ -0,0 +1,477 @@ +// 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: +// + + +#include +#include +#include "obexserverstatemachine.h" + +#if ( defined __FLOG_ACTIVE && defined __LOG_FUNCTIONS__ ) +_LIT8(KLogComponent, "OBEX"); +#endif + +/** +@file +@internalComponent +*/ + +/** +Constructs the state machine and the state classes + +@param aOwner Server object that owns the state machine +@param aTransport Transport Controller associated with the Server + +@return Contructed CObexServerStateMachine object +*/ +CObexServerStateMachine* CObexServerStateMachine::NewL(CObexServer& aOwner, CObexTransportControllerBase& aTransport) + { + CObexServerStateMachine* self = new(ELeave) CObexServerStateMachine(aOwner, aTransport); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +/** +First phase constructor + +@param aOwner Server object that owns the state machine +@param aTransport Transport Controller associated with the Server +*/ +CObexServerStateMachine::CObexServerStateMachine(CObexServer& aOwner, CObexTransportControllerBase& aTransport) + : iCurrentState(&iStates[EDisconnected]), iTransport(aTransport), iOwner(aOwner), iServerStarted(EFalse) + { + // Use placement new here to create the state objects and put them into a pre-defined array + // This is done as the number of states is known at compile time and this avoids heap fragmentation + new(&iStates[EDisconnected]) TObexServerStateDisconnected(); + new(&iStates[ETransportConnected]) TObexServerStateTransportConnected(); + new(&iStates[EObexConnecting]) TObexServerStateObexConnecting(); + new(&iStates[EWaitForUserPassword]) TObexServerStateWaitForUserPassword(); + new(&iStates[EReady]) TObexServerStateReady(); + new(&iStates[EPutOpWaitForUser]) TObexServerStatePutOpWaitForUser(); + new(&iStates[EPutOpReceiveObject]) TObexServerStatePutOpReceiveObject(); + new(&iStates[EGetOpReceiveSpecification]) TObexServerStateGetOpReceiveSpecification(); + new(&iStates[EGetOpWaitForUser]) TObexServerStateGetOpWaitForUser(); + new(&iStates[EGetOpSendObject]) TObexServerStateGetOpSendObject(); + new(&iStates[ESetPathOp]) TObexServerStateSetPathOp(); + new(&iStates[EPutOpFinal]) TObexServerStatePutOpFinal(); + new(&iStates[EGetOpFinal]) TObexServerStateGetOpFinal(); + new(&iStates[EDisconnecting]) TObexServerStateDisconnecting(); + } + +/** +Second phase constructor +*/ +void CObexServerStateMachine::ConstructL() + { + iSpecObject = CObexNullObject::NewL(); + iHeader = CObexHeader::NewL(); + iCallBack = new(ELeave) CAsyncCallBack(CActive::EPriorityStandard); + } + +/** +Destructor +*/ +CObexServerStateMachine::~CObexServerStateMachine() + { + // No need to delete state array + delete iHeader; + delete iSpecObject; + delete iPutFinalResponseHeaderSet; + delete iCallBack; + } + +/** +Move machine to a new state +Note that this function is synchronous, so any code after calling this function +will be executed with the machine in a different state. +Usually the action after changing state should be to return from the current function. + +@param aState Index of new state +@return Result of state change +*/ +void CObexServerStateMachine::ChangeState(TObexServerOperationStateEnum aState) + { + STATE_LOG_2(_L8("Changing from state %S to %S"), &iCurrentState->iName, &iStates[aState].iName); + iCurrentState = &(iStates[aState]); + iCurrentStateEnum = aState; + iCurrentState->Entry(*this); + } + +void CObexServerStateMachine::ControlledTransportDown() + { + LOG_FUNC + + iOwner.ControlledTransportDown(); + } + +/** +Process a received packet according to the packet's op-code and the current state + +@param aPacket Packet to process +@return Result of any state changes +*/ +void CObexServerStateMachine::OnPacketReceive(CObexPacket& aPacket) + { + if (!iServerStarted) + { + __ASSERT_DEBUG(iServerStarted, IrOBEXUtil::Fault(EPacketReceivedWhenServerNotStarted)); + return; + } + + iLastReceivedPacket = &aPacket; + + if(Transport().IsWriteActive()) + { + FLOG(_L("OnPacketReceive received request whilst writing... dropping connection\r\n")); + Notification().ObexDisconnectIndication(KNullDesC8); + // change state before issuing the transport down, as it may cause notifications + ChangeState(ETransportConnected); + ControlledTransportDown(); + return; + } + + // Initialise the send packet to ensure that we do not + // accidentally send the same packet as last time! + iTransport.SendPacket().Init(0); + + switch (aPacket.Opcode()) + { + case CObex::EOpConnect: + STATE_LOG(_L8("Calling connect")); + iCurrentState->Connect(*this, aPacket); + break; + case CObex::EOpDisconnect: + STATE_LOG(_L8("Calling disconnect")); + iCurrentState->Disconnect(*this, aPacket); + break; + case CObex::EOpPut: + STATE_LOG(_L8("Calling put")); + iCurrentState->Put(*this, aPacket); + break; + case CObex::EOpGet: + STATE_LOG(_L8("Calling get")); + iCurrentState->Get(*this, aPacket); + break; + case CObex::EOpSetPath: + STATE_LOG(_L8("Calling SetPath")); + iCurrentState->SetPath(*this, aPacket); + break; + case CObex::EOpAbortNoFBit: + // Abort does not check target headers (hence no need to send the packet to the event) + STATE_LOG(_L8("Calling abort")); + iCurrentState->Abort(*this); + break; + + default: + // Unknown packet type + STATE_LOG(_L8("Unknown packet type")); + iTransport.Send(ERespNotImplemented); + break; + } + } + +/** +Indicates a transport-level connection has been made to the Server +*/ +void CObexServerStateMachine::TransportUp() + { + // This event could happen while the Server is stopped (as the transport state + // can be independent of whether the Server has requested a read) + iCurrentState->TransportUp(*this); + } + +/** +Indicates the transport-level connection to the Server has disappeared +*/ +void CObexServerStateMachine::TransportDown() + { + // This event could happen while the Server is stopped (as the transport state + // can be independent of whether the Server has requested a read) + iCurrentState->Reset(*this); + } + +/** +Indicates a Protocol Error has occurred +*/ +void CObexServerStateMachine::Error() + { + // This event could happen while the Server is stopped (as the transport state + // can be independent of whether the Server has requested a read) + iCurrentState->Reset(*this); + } + +/** +Process an OBEX object received as part of an asynchronous Put/Get indication +This object pointer can be NULL to indicate an error condition +@see MObexServerNotifyAsync::PutRequestInidication +@see MObexServerNotifyAsync::GetRequestInidication +@see CObexServer::RequestIndicationCallback + +@param aObject OBEX object to use (NULL to indication an error condition) +@return Result of any state changes +*/ +TInt CObexServerStateMachine::RequestNotificationCompleted(CObexBaseObject* aObject) + { + __ASSERT_ALWAYS(iCurrentStateEnum == EPutOpWaitForUser || + iCurrentStateEnum == EGetOpWaitForUser, IrOBEXUtil::Panic(EInvalidResponseCallback)); + if (!iServerStarted) + { + return KErrIrObexServerStopped; + } + + iCurrentState->RequestNotificationCompleted(*this, aObject); + return KErrNone; + } + +/** +Process the response received as part of an asynchronous PutComplete/GetComplete/SetPath indication + +@param aAppResponse Application's response to the indication +@return result of state changes +*/ +TInt CObexServerStateMachine::RequestNotificationCompleted(TObexResponse aAppResponse) + { + __ASSERT_ALWAYS(iCurrentStateEnum == EPutOpWaitForUser || + iCurrentStateEnum == EGetOpWaitForUser, IrOBEXUtil::Panic(EInvalidResponseCallback)); + __ASSERT_ALWAYS(iCurrentState->ValidResponse(aAppResponse), IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp)); + if (!iServerStarted) + { + return KErrIrObexServerStopped; + } + + iCurrentState->RequestNotificationCompleted(*this, aAppResponse); + return KErrNone; + } + +/** +Process the response received as part of an asynchronous PutComplete/GetComplete/SetPath indication + +@param aAppResponse Application's response to the indication +@return result of state changes +*/ +TInt CObexServerStateMachine::RequestCompleteNotificationCompleted(TObexResponse aAppResponse) + { + __ASSERT_ALWAYS(iCurrentStateEnum == ESetPathOp || iCurrentStateEnum == EGetOpFinal || + iCurrentStateEnum == EPutOpFinal, IrOBEXUtil::Panic(EInvalidResponseCallback)); + __ASSERT_ALWAYS(iCurrentState->ValidResponse(aAppResponse), IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp)); + if (!iServerStarted) + { + return KErrIrObexServerStopped; + } + + iCurrentState->RequestCompleteNotificationCompleted(*this, aAppResponse); + return KErrNone; + } + +/** +Indicates an OBEX level connection has been established + +@return Result of any state changes +*/ +void CObexServerStateMachine::ConnectionComplete() + { + __ASSERT_ALWAYS(iServerStarted, IrOBEXUtil::Fault(EConnectionCompleteWhenServerStopped)); + + iCurrentState->ConnectionComplete(*this); + } + +/** +Indicates Server has been started + +@param aNotify Notification interface to use +*/ +void CObexServerStateMachine::Start(MObexServerNotifyAsync& aNotify) + { + iServerStarted = ETrue; + iNotification = &aNotify; // state will panic if trying to change interface at an inappropriate point + iCurrentState->Start(*this); + } + +/** +Indicates Server has been stopped +*/ +void CObexServerStateMachine::Stop() + { + iCurrentState->Reset(*this); + iServerStarted = EFalse; + } + +/** +Indication that the Obex server application has chosen to override the +handling of the request packet that has been received. + +@param aResponse The response the server application has indicated that should + be sent to the Obex client. The actual sending of the response + is delegated to the individual states to handle as appropriate. +@return Result of any state changes +*/ +void CObexServerStateMachine::OverrideRequestHandling(TObexResponse aResponse) + { + iCurrentState->OverrideRequestHandling(*this, aResponse); + } + +/** +Indicates that a write of a packet has been completed successfully at the +transport level. +*/ +void CObexServerStateMachine::WriteComplete() + { + LOG_FUNC + + iCurrentState->WriteComplete(*this); + } + +/** +Indicates that a new obex packet is being read. +*/ +void CObexServerStateMachine::ReadActivityDetected() + { + LOG_FUNC + + iCurrentState->ReadActivityDetected(*this); + } + +/** +@return Last packet processed by the state machine +*/ +CObexPacket& CObexServerStateMachine::LastReceivedPacket() const + { + __ASSERT_DEBUG(iLastReceivedPacket, IrOBEXUtil::Fault(ENoReceivePacketAvailable)); + return *iLastReceivedPacket; + } + +/** +This sets pointer of the object received from the application +and so can be NULL to indicate an error + +@see CObexServerStateMachine::NotificationComplete +@return Specification Object - used to describe the OBEX object to Get +*/ +CObexBaseObject* CObexServerStateMachine::SpecObject() const + { + return iSpecObject; + } + +/** +This returns pointer to the object received from the application +and so can be NULL to indicate an error + +@see CObexServerStateMachine::NotificationComplete +@return Transfer object to exchange with the Client +*/ +CObexBaseObject* CObexServerStateMachine::TransObject() const + { + return iTransObject; + } + +/** +@param aTransObject New transfer object to exchange with the Client +*/ +void CObexServerStateMachine::SetTransObject(CObexBaseObject* aTransObject) + { + iTransObject = aTransObject; + } + +/** +@return Transport Controller associated with the Server +*/ +CObexTransportControllerBase& CObexServerStateMachine::Transport() const + { + return iTransport; + } + +/** +@return Server object associated with the State Machine +*/ +CObexServer& CObexServerStateMachine::Owner() const + { + return iOwner; + } + +/** +@return Notification interface associated with the Server +*/ +MObexServerNotifyAsync& CObexServerStateMachine::Notification() const + { + __ASSERT_DEBUG(iNotification, IrOBEXUtil::Fault(ENoNotifierAvailable)); + return *iNotification; + } + +/** +@return Final Header set to send at the end of a Put operation +*/ +CObexHeaderSet* CObexServerStateMachine::PutFinalResponseHeaderSet() + { + return iPutFinalResponseHeaderSet; + } + +/** +@param aHeaderSet Final Header set to send at the end of a Put operation +*/ +void CObexServerStateMachine::SetPutFinalResponseHeaderSet(CObexHeaderSet* aHeaderSet) + { + delete iPutFinalResponseHeaderSet; + iPutFinalResponseHeaderSet = aHeaderSet; + } + +/** +@return Internal header object (used for sizing the final response header set) +*/ +CObexHeader* CObexServerStateMachine::GetHeader() + { + return iHeader; + } + +/** +Activate one-shot call-back to run from the Active Scheduler +@param aFunction Pointer to the function to call +*/ +void CObexServerStateMachine::CallBack(TInt (*aFunction)(TAny* aPtr)) + { + iCallBack->Set(TCallBack(aFunction, this)); + iCallBack->CallBack(); + } + +/** +Cancel one-shot call-back +*/ +void CObexServerStateMachine::CancelCallBack() + { + iCallBack->Cancel(); + } + +/** +@return If one-shot call-back is active +*/ +TBool CObexServerStateMachine::IsCallBackActive() const + { + return iCallBack->IsActive(); + } + +TObexResponse CObexServerStateMachine::AppResponse() const + { + return iAppResponse; + } + +void CObexServerStateMachine::SetAppResponse(TObexResponse aAppResponse) + { + __ASSERT_DEBUG(aAppResponse>0 && aAppResponse<=255, IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp )); + + //remove the final bit, sorry about the horrible double casting. + aAppResponse = static_cast(static_cast (aAppResponse) & 0x7F); + iAppResponse = aAppResponse; + } +