sl@0: /* sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * Implements CScsServer functionality. See class and functions definitions sl@0: * for more detail. sl@0: * sl@0: */ sl@0: sl@0: sl@0: /** sl@0: @file sl@0: */ sl@0: sl@0: #include sl@0: #include "scsserverconstants.h" sl@0: sl@0: static const TInt defaultPolicyRangeCount = 1; sl@0: static const TInt defaultPolicyRanges[defaultPolicyRangeCount] = sl@0: { sl@0: 0 // All requests sl@0: }; sl@0: sl@0: static const TUint8 defaultPolicyElementsIndex[defaultPolicyRangeCount] = sl@0: { sl@0: CPolicyServer::EAlwaysPass sl@0: }; sl@0: sl@0: static const CPolicyServer::TPolicyElement defaultPolicyPolicyElements[1] = {}; sl@0: sl@0: sl@0: static const CPolicyServer::TPolicy defaultPolicy = sl@0: { sl@0: CPolicyServer::EAlwaysPass, // Allow all connects sl@0: defaultPolicyRangeCount, sl@0: defaultPolicyRanges, sl@0: defaultPolicyElementsIndex, sl@0: defaultPolicyPolicyElements, sl@0: }; sl@0: sl@0: EXPORT_C CScsServer::CScsServer(const TVersion& aVersion, CActive::TPriority aPriority) sl@0: /** sl@0: Record this server's version so it can be compared against the requested sl@0: version in NewSessionL. sl@0: sl@0: @param aVersion This server's version. This is compared against each sl@0: client's requested version when the client tries to connect. sl@0: @param aPriority This server's active object priority. sl@0: */ sl@0: : CPolicyServer(aPriority, defaultPolicy, ESharableSessions) sl@0: ,iVersion(aVersion) sl@0: // ,iSessionCount(0) sl@0: { sl@0: // empty. sl@0: } sl@0: sl@0: EXPORT_C CScsServer::CScsServer(const TVersion& aVersion, const CPolicyServer::TPolicy& aPolicy, CActive::TPriority aPriority) sl@0: /** sl@0: Record this server's version so it can be compared against the requested sl@0: version in NewSessionL. sl@0: sl@0: @param aVersion This server's version. This is compared against each sl@0: client's requested version when the client tries to connect. sl@0: @param aPolicy Can be used to configure security for server connect and session functions sl@0: @param aPriority This server's active object priority. sl@0: sl@0: Note that aPolicy must be configured to allow all functions sl@0: covered by the KScsFunctionMask mask. sl@0: */ sl@0: : CPolicyServer(aPriority, aPolicy, ESharableSessions) sl@0: ,iVersion(aVersion) sl@0: // ,iSessionCount(0) sl@0: { sl@0: // empty. sl@0: } sl@0: sl@0: EXPORT_C void CScsServer::ConstructL(TInt aShutdownPeriodUs) sl@0: /** sl@0: Second-phase constructor allocates the shutdown timer for this server object. sl@0: sl@0: If aShutdownPeriodUs is not 0, this function starts the shutdown sl@0: timer because the server starts up with no current sessions. sl@0: sl@0: If aShutdownPeriodUs is 0, the timer is not started and the server sl@0: will not auto-exit. sl@0: sl@0: nb. It must be called, even if you do not want a shutdown timer. sl@0: sl@0: This function does not start the server, i.e. it does not call StartL. The sl@0: calling function must do this after this function returns. sl@0: sl@0: @param aShutdownPeriodUs Shutdown period in microseconds. sl@0: */ sl@0: { sl@0: if(aShutdownPeriodUs > 0) sl@0: { sl@0: iShutdownTimer = CShutdownTimer::NewL(aShutdownPeriodUs); sl@0: iShutdownTimer->Restart(); sl@0: } sl@0: sl@0: iContainerIndex = CObjectConIx::NewL(); sl@0: sl@0: TCallBack cb(RemoveCompletedRequests, this); sl@0: // EPriorityHigh to encourage the active scheduler to action the sl@0: // delete ASAP, in particular ahead of pending or new requests. sl@0: iAsyncCleanup = new(ELeave) CAsyncCallBack(cb, CActive::EPriorityHigh); sl@0: } sl@0: sl@0: sl@0: EXPORT_C void CScsServer::DisableShutdownTimer() sl@0: /** sl@0: The server will no longer shutdown after the last client session closes. sl@0: Client calls to RScsClientBase::ShutdownServer will fail with KErrNotSupported. sl@0: */ sl@0: { sl@0: if(iShutdownTimer) sl@0: { sl@0: iShutdownTimer->Cancel(); sl@0: } sl@0: delete iShutdownTimer; sl@0: iShutdownTimer = 0; sl@0: } sl@0: sl@0: EXPORT_C void CScsServer::EnableShutdownTimerL(TInt aShutdownPeriodUs) sl@0: /** sl@0: Enable shutdown timer support in the server. sl@0: If there are currently no client sessions the timer will be immediately started, otherwise sl@0: it will be started when the last client session closes. sl@0: If the timer expires, before another client creates a session, the server will shutdown. sl@0: The RScsClientBase::ShutdownServer api will now be supported, if called the server timeout sl@0: will effectively be reduced to 0. sl@0: */ sl@0: { sl@0: if(aShutdownPeriodUs <= 0) sl@0: { sl@0: return; sl@0: } sl@0: DisableShutdownTimer(); sl@0: iShutdownTimer = CShutdownTimer::NewL(aShutdownPeriodUs); sl@0: if(iSessionCount == 0) sl@0: { sl@0: iShutdownTimer->Restart(); sl@0: } sl@0: } sl@0: sl@0: sl@0: EXPORT_C CScsServer::~CScsServer() sl@0: /** sl@0: Frees resources used at this level. Specifically, frees the sl@0: container index, which is used to generate subsession containers, sl@0: the shutdown timer, and the async request cleanup object. sl@0: */ sl@0: { sl@0: __ASSERT_DEBUG(iSessionCount == 0, PanicServer(ESvrRemSessions)); sl@0: __ASSERT_DEBUG(iAsyncCleanup == 0 || !iAsyncCleanup->IsActive(), PanicServer(ESvrRemCleanup)); sl@0: __ASSERT_DEBUG(iAsyncRequests.Count() == 0, PanicServer(ESvrRemRequests)); sl@0: sl@0: delete iContainerIndex; sl@0: delete iShutdownTimer; sl@0: sl@0: delete iAsyncCleanup; sl@0: iAsyncRequests.Reset(); sl@0: } sl@0: sl@0: // -------- sessions -------- sl@0: sl@0: EXPORT_C CSession2* CScsServer::NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const sl@0: /** sl@0: Implement CServer2 by allocating a new session object. This function sl@0: delegates the actual allocation to the subclass. Before creating the sl@0: session object, it compares the requested version with its own version. sl@0: After creating the session object, it increments the session count, which sl@0: reset the shutdown timer if it is the only session. sl@0: sl@0: @param aVersion Version of server which client requires. sl@0: @param aMessage Connect message. sl@0: @return New initialized instance of CSession2 subclass. sl@0: */ sl@0: { sl@0: TBool versionOk = User::QueryVersionSupported(/* aCurrent */ iVersion, /* aRequested */ aVersion); sl@0: if (! versionOk) sl@0: User::Leave(KErrNotSupported); sl@0: sl@0: CScsSession* s = const_cast(this)->DoNewSessionL(aMessage); sl@0: sl@0: return s; sl@0: } sl@0: sl@0: EXPORT_C void CScsServer::DoPreHeapMarkOrCheckL() sl@0: { sl@0: } sl@0: sl@0: EXPORT_C void CScsServer::DoPostHeapMarkOrCheckL() sl@0: { sl@0: } sl@0: sl@0: void CScsServer::IncrementSessionCount() sl@0: /** sl@0: Record the fact that another session has been created. If this new sl@0: session is the only session then cancel the shutdown timer. sl@0: */ sl@0: { sl@0: ++iSessionCount; sl@0: if (iSessionCount == 1) sl@0: { sl@0: if(iShutdownTimer) sl@0: { sl@0: iShutdownTimer->Cancel(); sl@0: } sl@0: } sl@0: } sl@0: sl@0: void CScsServer::DecrementSessionCount() sl@0: /** sl@0: Record the fact that a session has been deleted. If this was the sl@0: only remaining session then start the shutdown timer. sl@0: sl@0: Note the shutdown timer may be deferred if a request cleanup is sl@0: pending. sl@0: */ sl@0: { sl@0: --iSessionCount; sl@0: if (iShutdownTimer && (iSessionCount == 0) && (! iAsyncCleanup->IsActive())) sl@0: { sl@0: iShutdownTimer->Restart(); sl@0: } sl@0: } sl@0: sl@0: EXPORT_C void CScsServer::ShutdownWhenIdleL() sl@0: /** sl@0: Shutdown immediately when server is next idle. If the server sl@0: is not transient (ie. has no shutdown timer), this call is sl@0: prohibited and will leave with KErrNotSupported. sl@0: */ sl@0: { sl@0: if(! iShutdownTimer) sl@0: { sl@0: // We do not allow shutdown of non-transient servers. sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: // Next time we are idle, shutdown immediately. sl@0: iShutdownTimer->ImmediateTimeoutNextRestart(); sl@0: } sl@0: sl@0: sl@0: sl@0: // -------- asynchronous requests -------- sl@0: sl@0: CAsyncRequest* CScsServer::FindAsyncRequest( sl@0: CScsSession* aSession, CScsSubsession* aSubsession, TInt aFunction) sl@0: /** sl@0: Find the outstanding request which matches the supplied criteria. sl@0: sl@0: @param aSession Session which hosts the request. sl@0: @param aSubsession Subsession which hosts the request, NULL if sl@0: the request is relative to a session. sl@0: @param aFunction The function identifier, without any SCS code. sl@0: @return The matching asynchronous request, NULL if not sl@0: found. sl@0: */ sl@0: { sl@0: TInt reqCount = iAsyncRequests.Count(); sl@0: for (TInt i = reqCount - 1; i >= 0; --i) sl@0: { sl@0: CAsyncRequest* req = iAsyncRequests[i]; sl@0: sl@0: if (req->iSession != aSession) sl@0: continue; sl@0: sl@0: if (req->iSubsession != aSubsession) sl@0: continue; sl@0: sl@0: if (req->iFunction != aFunction) sl@0: continue; sl@0: sl@0: return req; sl@0: } sl@0: sl@0: return 0; // request not found sl@0: } sl@0: sl@0: void CScsServer::AddAsyncRequestL(CAsyncRequest* aAsyncRequest) sl@0: /** sl@0: Add the supplied request to the server's collection. sl@0: sl@0: @param aAsyncRequest Request to add. If this function succeeds sl@0: then ownership has been transferred to the sl@0: collection. sl@0: */ sl@0: { sl@0: // ensure this session does not already have an outstanding request sl@0: // for the same function sl@0: CAsyncRequest* existReq = FindAsyncRequest( sl@0: aAsyncRequest->iSession, aAsyncRequest->iSubsession, sl@0: aAsyncRequest->iFunction); sl@0: sl@0: if (existReq != 0) sl@0: User::Leave(KErrScsAsyncAlreadyQueued); sl@0: sl@0: iAsyncRequests.AppendL(aAsyncRequest); sl@0: } sl@0: sl@0: void CScsServer::CancelAsyncRequest( sl@0: CScsSession* aSession, CScsSubsession* aSubsession, TInt aFunction) sl@0: /** sl@0: Cancels a specific (sub)session request in response to a client sl@0: command. Also completes the client request. sl@0: sl@0: CancelOutstandingRequest should be called when a (sub)session is closed. sl@0: sl@0: @param aSession Session which hosts the request. sl@0: @param aSubsession Subsession which hosts the request, NULL if sl@0: the request is relative to a session. sl@0: @param aFunction The function identifier, without any SCS code. sl@0: */ sl@0: { sl@0: CAsyncRequest* req = FindAsyncRequest(aSession, aSubsession, aFunction); sl@0: sl@0: // not an error if the request is not queued; could have been sl@0: // completed before the cancel function was processed. sl@0: if (req != 0) sl@0: req->CancelCompleteAndMarkForDeletion(); sl@0: } sl@0: sl@0: void CScsServer::CancelOutstandingRequests(CScsSession* aSession, TBool aCompleteClientRequests) sl@0: /** sl@0: Cancels and deletes all outstanding asynchronous requests associated sl@0: with the supplied session or any of its subsessions. Does not complete sl@0: the associated client requests. sl@0: sl@0: This function should be called when a session is closed. sl@0: CancelAsyncRequest should be called when a specific request is cancelled. sl@0: sl@0: @param aSession Session which is being closed. sl@0: @param aCompleteClientRequests Whether to complete the client-side requests sl@0: with KErrCancel. sl@0: */ sl@0: { sl@0: CancelOutstandingRequests(aSession, KWildSubsession, aCompleteClientRequests); sl@0: } sl@0: sl@0: void CScsServer::CancelOutstandingRequests(CScsSession* aSession, CScsSubsession* aSubsession, TBool aCompleteClientRequests) sl@0: /** sl@0: Cancels and deletes all outstanding asynchronous requests associated sl@0: with the supplied session and subsession. This should be called when sl@0: a session or subsession is closed. sl@0: sl@0: As an exception, the user-side request is completed when a subsession is sl@0: closed, else the request would not be completed until the session itself sl@0: was destroyed. sl@0: sl@0: CancelAsyncRequest should be called when a specific request is cancelled. sl@0: sl@0: @param aSession Session which is being closed. sl@0: @param aSubsession Subsession which is being closed. If this is sl@0: KWildSubsession then a session is being closed so all of sl@0: its subsession requests should be destroyed as well. sl@0: @param aCompleteClientRequests Whether to complete the client-side requests sl@0: with KErrCancel. sl@0: */ sl@0: { sl@0: TBool wildSubsession = (aSubsession == KWildSubsession); sl@0: sl@0: TInt reqCount = iAsyncRequests.Count(); sl@0: for (TInt i = reqCount - 1; i >= 0; --i) sl@0: { sl@0: CAsyncRequest* req = iAsyncRequests[i]; sl@0: sl@0: TBool sessionMatch = (req->iSession == aSession); sl@0: if (! sessionMatch) sl@0: continue; sl@0: sl@0: TBool subsessionMatch = wildSubsession || (req->iSubsession == aSubsession); sl@0: if (! subsessionMatch) sl@0: continue; sl@0: sl@0: if (aCompleteClientRequests) sl@0: req->CancelCompleteAndMarkForDeletion(); sl@0: else sl@0: { sl@0: req->DoCleanup(); sl@0: req->MarkForDeletion(); sl@0: } sl@0: } sl@0: } sl@0: sl@0: TInt CScsServer::RemoveCompletedRequests(TAny* aPtr) sl@0: /** sl@0: This static function is called when iAsyncCleanup sl@0: runs. It interprets its argument as a pointer to sl@0: an instance of CScsServer and removes any asynchronous sl@0: requests which have been completed. sl@0: sl@0: @param aPtr Required callback argument. Interpreted sl@0: as a pointer to an instance of CScsServer. sl@0: @return KErrNone. Required to satisfy the TCallBack sl@0: function signature. sl@0: @see RemoveCompletedRequests() sl@0: */ sl@0: { sl@0: CScsServer* svr = static_cast(aPtr); sl@0: svr->RemoveCompletedRequests(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void CScsServer::RemoveCompletedRequests() sl@0: /** sl@0: Delete any asynchronous requests which are marked for deletion. sl@0: */ sl@0: { sl@0: // The requests have already been completed; they just need to be removed. sl@0: TInt reqCount = iAsyncRequests.Count(); sl@0: for (TInt i = reqCount - 1; i >= 0; --i) sl@0: { sl@0: CAsyncRequest* req = iAsyncRequests[i]; sl@0: if (req->iSession != 0) // still outstanding if iSession != 0 sl@0: continue; sl@0: sl@0: delete req; sl@0: iAsyncRequests.Remove(i); sl@0: } sl@0: sl@0: // if no more outstanding requests then reset array so heap balances sl@0: if (iAsyncRequests.Count() == 0) sl@0: iAsyncRequests.Compress(); sl@0: sl@0: // if the shutdown timer was deferred because of an impending sl@0: // cleanup then launch it now. sl@0: if (iShutdownTimer && (iSessionCount == 0)) sl@0: { sl@0: iShutdownTimer->Restart(); sl@0: } sl@0: } sl@0: // End of file sl@0: