--- /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 <obex.h>
+#include <obex/internal/obexinternalheader.h>
+#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<TObexResponse>(static_cast<TInt> (aAppResponse) & 0x7F);
+ iAppResponse = aAppResponse;
+ }
+