sl@0: // Copyright (c) 2001-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 "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: //
sl@0: 
sl@0: #include "MmfAudioPolicy.h"
sl@0: #include "MmfAudioPolicySession.h"	
sl@0: #include "MmfAudioPolicyServer.h"
sl@0: #include "MdaHwInfo.h"	
sl@0: #include "MmfAudioPolicyRequest.h"
sl@0: 
sl@0: /**
sl@0: *@internalTechnology
sl@0: *@return if a client owns or wish to own audio resource
sl@0: */
sl@0: inline TBool IsActiveState(TMMFAudioPolicyState aState)
sl@0: 	{
sl@0: 	return (aState < EMMFStateStopped || aState==EMMFStateNotified || aState==EMMFStatePlayDualTone);
sl@0: 	}
sl@0: 
sl@0: CAudioPolicy::~CAudioPolicy()
sl@0: 	{
sl@0: 	delete iMdaHwInfo;
sl@0: 	delete iAudioPolicyRequestArray;
sl@0: 	}
sl@0: 
sl@0: void CAudioPolicy::ConstructL()
sl@0: 	{
sl@0: 	// Create dynamic array for sessions list
sl@0: 	iAudioPolicyRequestArray = new(ELeave) CPolicyReqPtrArray(CAudioPolicy::EGranularity);
sl@0: 	iMdaHwInfo = CMdaHwInfo::NewL();
sl@0: 	}
sl@0: 
sl@0: EXPORT_C CAudioPolicy* CAudioPolicy::NewL(CMMFAudioPolicyServer* aAudioPolicyServer)
sl@0: 	{	
sl@0: 
sl@0: 	CAudioPolicy* self = new(ELeave)CAudioPolicy(aAudioPolicyServer);
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL();
sl@0: 	CleanupStack::Pop();
sl@0: 	return(self);
sl@0: 	}
sl@0: 
sl@0: CAudioPolicy::CAudioPolicy(CMMFAudioPolicyServer* aAudioPolicyServer) :
sl@0: 	iAudioPolicyServer(aAudioPolicyServer),
sl@0: 	iNotifiedSessionId(KErrNotFound),
sl@0: 	iSessionIdAwaitingForDevsound(KErrNotFound),
sl@0: 	iStopHandledFromSessId(KErrNotFound)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: void CAudioPolicy::MakeRequest(CMMFAudioPolicyRequest* aAudioPolicyRequest)
sl@0: 	{
sl@0: // since we have a FIFO q, then remove request and re-add it.
sl@0: 	RemoveFromList(aAudioPolicyRequest->PolicySessionId(), EFalse); 
sl@0: 	if (iStopHandledFromSessId==aAudioPolicyRequest->PolicySessionId())
sl@0: 		{
sl@0: 		iStopHandledFromSessId=KErrNotFound;
sl@0: 		}
sl@0: // Process Request by looking at priorities, preferences, special states...
sl@0: 	TPolicyResponse responseValue = ProcessRequest(aAudioPolicyRequest);
sl@0: #if defined(ALLOW_POLICY_DEBUG)
sl@0: 	RDebug::Print(_L("Sess ID=%d, Priority=%d"),aAudioPolicyRequest->PolicySessionId(),aAudioPolicyRequest->Priority());
sl@0: #endif	
sl@0: 	switch (responseValue)
sl@0: 		{
sl@0: 		case EDenied:
sl@0: 			{
sl@0: 			TMMFAudioPolicyEvent responseEvent(TMMFAudioPolicyEvent::EMMFAudioPolicyPriorityTooLow, KErrInUse,EMMFStateWaitingForResource);
sl@0: 			// the client won't be notified until he has request so, so we can set its state anyway
sl@0: 			aAudioPolicyRequest->SetState( EMMFStateWaitingForResource ); 
sl@0: 			iAudioPolicyServer->SendEventToClient(aAudioPolicyRequest->PolicySessionId(), responseEvent);
sl@0: 			}
sl@0: 			break;
sl@0: 		case EProceed:
sl@0: 			{
sl@0: 			iAudioPolicyServer->StopNotificationTimer();
sl@0: 			TMMFAudioPolicyEvent responseEvent(TMMFAudioPolicyEvent::EMMFAudioPolicyNoEvent, KErrNone, aAudioPolicyRequest->State());
sl@0: 			iAudioPolicyServer->SendEventToClient(aAudioPolicyRequest->PolicySessionId(), responseEvent);
sl@0: 			}
sl@0: 			break;
sl@0: 		case EStopThenProceed:
sl@0: 			{
sl@0: 			iAudioPolicyServer->StopNotificationTimer(); 
sl@0: 			iSessionIdAwaitingForDevsound=aAudioPolicyRequest->PolicySessionId();
sl@0: 			// we have to wait for devsound to stop client(s), then notify that one
sl@0: 			}
sl@0: 			break;
sl@0: 		case EResume:
sl@0: 		case EMix:
sl@0: 		default:
sl@0: 			ASSERT(EFalse);
sl@0: 		}
sl@0: 	TRAPD(err, iAudioPolicyRequestArray->AppendL(aAudioPolicyRequest) );
sl@0: 	__ASSERT_ALWAYS(err==KErrNone, Panic(EMMFAudioPolicyRequestArrayOverflow) ); // we reserved space, so shouldn't hit this
sl@0: 	}
sl@0: 
sl@0: TPolicyResponse CAudioPolicy::ProcessRequest(CMMFAudioPolicyRequest* aAudioPolicyRequest)
sl@0: 	{
sl@0: 	// If there is no other item on list, return with proceed
sl@0: 	if (iAudioPolicyRequestArray->Count()==0)
sl@0: 		{
sl@0: 		return EProceed;
sl@0: 		}
sl@0: 		
sl@0: 	TPolicyResponse responseValue(EProceed);
sl@0: 	TInt requestPriority = aAudioPolicyRequest->Priority();
sl@0: 	
sl@0: 	TBool requestCaps = aAudioPolicyRequest->Capabilities();
sl@0: 
sl@0: 	// Iterate through list and compare priorities:
sl@0: 	// QUEST: state checking shall be done as well?
sl@0: 	for (TInt index = 0; index < iAudioPolicyRequestArray->Count(); index++)
sl@0: 		{
sl@0: 		CMMFAudioPolicyRequest& currentReq=*(*iAudioPolicyRequestArray)[index];
sl@0: 		if (!IsActiveState(currentReq.State()) ) // this request is inactive
sl@0: 			{
sl@0: 			continue;
sl@0: 			}
sl@0: 			// If there's even one on the list w/ a higher priority deny request and leave:
sl@0: 
sl@0: 		if (currentReq.Capabilities() > requestCaps)
sl@0: 			{
sl@0: 			responseValue = EDenied;
sl@0: 			break;
sl@0: 			}
sl@0: 		else if(currentReq.Capabilities() == requestCaps) 
sl@0: 			{
sl@0: 			if(currentReq.Priority() >= requestPriority)
sl@0: 				{
sl@0: 				responseValue = EDenied;
sl@0: 				break;
sl@0: 				}
sl@0: 			}
sl@0: 		if (currentReq.State()==EMMFStateWaitingForResource || currentReq.State()==EMMFStatePreempted)
sl@0: 			{
sl@0: 			continue;
sl@0: 			}
sl@0: 		// we need to stop active client since new request is of higher priority
sl@0: 		TMMFAudioPolicyEvent freezeEvent(TMMFAudioPolicyEvent::EMMFAudioPolicyPriorityTooLow, KErrInUse, EMMFStatePaused);
sl@0: #if defined(ALLOW_POLICY_DEBUG)	
sl@0: 		RDebug::Print(_L("Sess ID=%d, State=%d Has been preempted"),currentReq.PolicySessionId(),currentReq.State());
sl@0: #endif			
sl@0: 		currentReq.SetState( EMMFStatePreempted );
sl@0: 		currentReq.SetEventFlag(EFalse);
sl@0: 		iAudioPolicyServer->SendEventToClient(currentReq.PolicySessionId(), freezeEvent);
sl@0: 		responseValue = EStopThenProceed;
sl@0: 		}
sl@0: 
sl@0: 	return responseValue;
sl@0: 	}
sl@0: 
sl@0: void CAudioPolicy::ModifyEntry(TInt aPolicySessionId,const TMMFAudioPolicyState aNewState)
sl@0: 	{
sl@0: 	CMMFAudioPolicyRequest* sessionEntry=FindPolicyRequestById(aPolicySessionId);
sl@0: #if defined(ALLOW_POLICY_DEBUG)	
sl@0: 	RDebug::Print(_L("Sess ID=%d, Old State=%d New State=%d"),aPolicySessionId,sessionEntry?sessionEntry->State():-1, aNewState);
sl@0: #endif	
sl@0: 	// some client took over resource, so update its state and cancel timer
sl@0: 	if (IsActiveState(aNewState))
sl@0: 		{
sl@0: 		if (sessionEntry)
sl@0: 			{
sl@0: 			sessionEntry->SetState(aNewState);	
sl@0: 			sessionEntry->SetEventFlag(EFalse);
sl@0: 			}
sl@0: 		iAudioPolicyServer->StopNotificationTimer();
sl@0: 		iNotifiedSessionId	=KErrNotFound;
sl@0: 		ASSERT(iSessionIdAwaitingForDevsound==KErrNotFound); // we shouldn't have a client waiting to be notified
sl@0: 		if (iStopHandledFromSessId==aPolicySessionId)
sl@0: 			{
sl@0: 			iStopHandledFromSessId=KErrNotFound;
sl@0: 			}
sl@0: 		return;
sl@0: 		}
sl@0: 	if (iNotifiedSessionId==aPolicySessionId) // if client that was notified, then stop timer.
sl@0: 		{
sl@0: 		iAudioPolicyServer->StopNotificationTimer();
sl@0: 		}
sl@0: 	if (sessionEntry==NULL) // to cope with erroneous behaviour of devsound
sl@0: 		{
sl@0: 		return;
sl@0: 		}
sl@0: 	// we have update from the client, if we have other clients waiting we should notify them
sl@0: 	if ( (aNewState == EMMFStatePaused || (aNewState == EMMFStateStopped && iStopHandledFromSessId!=aPolicySessionId) )
sl@0: 			&& sessionEntry->State()!=EMMFStateStopped && sessionEntry->State()!=EMMFStateWaitingForResource)
sl@0: 		{
sl@0: 		if (aNewState == EMMFStateStopped) // to eliminate duplicate stop events
sl@0: 			{
sl@0: 			iStopHandledFromSessId=aPolicySessionId;
sl@0: 			}
sl@0: 		if (sessionEntry->State()==EMMFStatePreempted)
sl@0: 			{
sl@0: 			sessionEntry->SetState(EMMFStateWaitingForResource);
sl@0: 			}
sl@0: 			
sl@0: 		if (iSessionIdAwaitingForDevsound==aPolicySessionId)
sl@0: 			{
sl@0: 			iSessionIdAwaitingForDevsound=KErrNotFound;
sl@0: 			}
sl@0: 			
sl@0: 		if (aNewState == EMMFStatePaused || aNewState == EMMFStateStopped) // devsound should free, so notify waiting client
sl@0: 			{
sl@0: 			if (iSessionIdAwaitingForDevsound!=KErrNotFound) // we have a client waiting for Devsound, so notify it
sl@0: 				{
sl@0: 				NotifySessionWithTimeout(iSessionIdAwaitingForDevsound, TMMFAudioPolicyEvent::EMMFAudioPolicyNoEvent);
sl@0: 				iSessionIdAwaitingForDevsound=KErrNotFound;
sl@0: 				}
sl@0: 			else if (!iAudioPolicyServer->IsTimerActive()) // do not try to notify if we're still waiting for response
sl@0: 				{
sl@0: 				const TInt sessionIdToNotify = CheckSessionToNotify();
sl@0: 				if(sessionIdToNotify != KErrNotFound)
sl@0: 					{
sl@0: 					// set the state as notified
sl@0: 					NotifySessionWithTimeout(sessionIdToNotify, TMMFAudioPolicyEvent::EMMFAudioPolicyResourceNotification);
sl@0: 					}				
sl@0: 				}
sl@0: 			}
sl@0: 		}
sl@0: 	// we update state to passive only if the client hasn't been stopped by us, so as not loose its waiting indication
sl@0: 	if (sessionEntry->State()!=EMMFStateWaitingForResource)
sl@0: 		{
sl@0: 		sessionEntry->SetState(aNewState);
sl@0: 		}
sl@0: 	}
sl@0: 	
sl@0: void CAudioPolicy::NotifySessionWithTimeout(TInt aPolicySessionId, TMMFAudioPolicyEvent::TAudioPolicyEventType aEvent)
sl@0: 	{
sl@0: 	CMMFAudioPolicyRequest* sessionEntry=FindPolicyRequestById(aPolicySessionId);
sl@0: 	ASSERT(sessionEntry);
sl@0: #if defined(ALLOW_POLICY_DEBUG)	
sl@0: 	RDebug::Print(_L("Sending timed not. ID=%d, State=%d Event=%d"),aPolicySessionId,sessionEntry->State(), aEvent);
sl@0: #endif
sl@0: 	const TMMFAudioPolicyEvent eventToSend(aEvent, KErrNone, sessionEntry->State());
sl@0: 	sessionEntry->SetEventFlag(ETrue);
sl@0: 	iAudioPolicyServer->StartNotificationTimer();
sl@0: 	iNotifiedSessionId 	= aPolicySessionId;
sl@0: 	iAudioPolicyServer->SendEventToClient(aPolicySessionId, eventToSend);
sl@0: 	}
sl@0: 
sl@0: void CAudioPolicy::RemoveFromList(TInt aPolicySessionId, TBool aAllowTimerRestart)
sl@0: 	{
sl@0: 	if (aPolicySessionId==KErrNotFound)
sl@0: 		{
sl@0: 		return;
sl@0: 		}
sl@0: 	for (TInt index = iAudioPolicyRequestArray->Count(); index-- ;)
sl@0: 		{
sl@0: 		// Find correct entry to remove	
sl@0: 		if ( (*iAudioPolicyRequestArray)[index]->PolicySessionId() == aPolicySessionId)
sl@0: 			{
sl@0: 			if (iSessionIdAwaitingForDevsound==aPolicySessionId)
sl@0: 				{
sl@0: 				iSessionIdAwaitingForDevsound=KErrNotFound;
sl@0: 				}			
sl@0: 			if (iNotifiedSessionId==aPolicySessionId && iAudioPolicyServer->IsTimerActive()) 
sl@0: 				{
sl@0: 				iNotifiedSessionId=KErrNotFound;
sl@0: 				// the session we were waiting for disconnected so try to immediately notify another one
sl@0: 				iAudioPolicyServer->StopNotificationTimer();
sl@0: 				if (iAudioPolicyRequestArray->Count() > 1 && aAllowTimerRestart)
sl@0: 					{
sl@0: 					iAudioPolicyServer->StartNotificationTimer(ETrue);
sl@0: 					}
sl@0: 				}			
sl@0: 			else if(!iAudioPolicyServer->IsTimerActive())
sl@0: 				{
sl@0: 				if (iAudioPolicyRequestArray->Count() > 1 && aAllowTimerRestart)
sl@0: 					{
sl@0: 					iAudioPolicyServer->StartNotificationTimer(ETrue);
sl@0: 					}
sl@0: 				}
sl@0: 			iAudioPolicyRequestArray->Delete(index);
sl@0: 			return;
sl@0: 			}
sl@0: 		}
sl@0: 	}
sl@0: 	
sl@0: void CAudioPolicy::HandlePreferences(CMMFAudioPolicyRequest* /*aAudioPolicyRequest*/, TInt /*aPref*/, TPolicyResponse& /*aResponse*/)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: // this is weird, but devsound 
sl@0: // does Stop() if a client is denied access to resource
sl@0: // then calls this routine to indicate that resource became available
sl@0: void CAudioPolicy::LaunchRequest(TInt aSessionId)
sl@0: 	{
sl@0: 	ASSERT(iSessionIdAwaitingForDevsound!=aSessionId);
sl@0: 	ModifyEntry(aSessionId, EMMFStateStopped);
sl@0: 	}
sl@0: 
sl@0: void CAudioPolicy::ReserveClientNumL(TInt aNum)
sl@0: 	{
sl@0: 	iAudioPolicyRequestArray->SetReserveL(aNum);
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @internalTechnology
sl@0: 
sl@0: This function raises a panic
sl@0: 
sl@0: @param	aError
sl@0: 		one of the several panics codes that may be raised by this dll
sl@0: 
sl@0: @panic	EMMFAudioPolicyRequestArrayOverflow is raised when policyrequest array is full
sl@0: */
sl@0: GLDEF_C void Panic(TMMFAudioPolicyPanicCodes aPanicCode)
sl@0: 	{
sl@0: 	User::Panic(KMMFAudioPolicyPanicCategory, aPanicCode);
sl@0: 	}
sl@0: 
sl@0: // checks based on the session ,Is the session is registered for Notification 
sl@0: TBool CAudioPolicy::IsRegisteredNotification(TInt aSessionId) const
sl@0: 	{
sl@0: 	TUid event;
sl@0:  	for (TInt index = 0; index < iAudioPolicyRequestArray->Count(); index++)
sl@0:  		{
sl@0:  		if((*iAudioPolicyRequestArray)[index]->PolicySessionId() == aSessionId)
sl@0:  			{
sl@0:  			event = (*iAudioPolicyRequestArray)[index]->NotificationEvent();
sl@0:  			if (event  == KMMFEventCategoryAudioResourceAvailable)
sl@0:  				{
sl@0:  				// only when the client is registered for KMMFEventCategoryAudioResourceAvailable event
sl@0:  				return ETrue; 
sl@0:  				}
sl@0:  			break;	
sl@0:  			} 
sl@0:  		} 
sl@0:  	return EFalse;
sl@0: 	}
sl@0: 
sl@0: // get the next highest priority of the client to notify 	
sl@0: TInt CAudioPolicy::CheckSessionToNotify()
sl@0: 	{
sl@0: 	TInt nextHighestPriority= -100;
sl@0: 	TInt sessionToNotify = KErrNotFound;
sl@0: 
sl@0: 	// get the max priority and set the Index
sl@0: 	for (TInt attempt=2; attempt-- && sessionToNotify==KErrNotFound;)
sl@0: 		{
sl@0: 		
sl@0: 		for (TInt index = 0; index < iAudioPolicyRequestArray->Count(); ++index)
sl@0: 	 		{
sl@0: 	 		CMMFAudioPolicyRequest& currentReq=*(*iAudioPolicyRequestArray)[index];
sl@0: 	 		if((nextHighestPriority <= currentReq.Priority())
sl@0: 	 					&& (currentReq.NotificationEvent() == KMMFEventCategoryAudioResourceAvailable) 
sl@0: 	 					&& (!currentReq.IsEventNotified())
sl@0: 	 					&& currentReq.State()==EMMFStateWaitingForResource)
sl@0: 	 			{
sl@0: 	 			nextHighestPriority = currentReq.Priority();
sl@0: 	 			sessionToNotify = currentReq.PolicySessionId();
sl@0: 	 			}
sl@0: 	 		}
sl@0: 	 	// we tried to notify every session once, so reset flag and try again.
sl@0: 	 	if (sessionToNotify==KErrNotFound)
sl@0: 	 		{
sl@0: 	 		for (TInt i=iAudioPolicyRequestArray->Count(); i--;)
sl@0: 	 			{
sl@0: 	 			(*iAudioPolicyRequestArray)[i]->SetEventFlag(EFalse);
sl@0: 	 			}
sl@0: 	 		}
sl@0:  		}
sl@0:   	return sessionToNotify;	
sl@0: 	}
sl@0: 
sl@0: // send the message to the server 
sl@0: void CAudioPolicy::NotifyNextClient()
sl@0: 	{
sl@0: 	const TInt sessionIdToNotify = CheckSessionToNotify();
sl@0: #if defined(ALLOW_POLICY_DEBUG)	
sl@0: 	RDebug::Print(_L("Sess ID %d didn't continue within timeout, Next ID=%d"), iNotifiedSessionId, sessionIdToNotify);
sl@0: #endif	
sl@0: 	iNotifiedSessionId = KErrNotFound;
sl@0: 	if(sessionIdToNotify != KErrNotFound)
sl@0: 		{
sl@0: 		NotifySessionWithTimeout(sessionIdToNotify, TMMFAudioPolicyEvent::EMMFAudioPolicyResourceNotification);
sl@0: 		}	
sl@0: 	}
sl@0: 
sl@0: // Set in the AudiopolicyRequestArray the uid registered
sl@0: TInt CAudioPolicy::SetNotification(TInt aSessionId, TUid aEventType)
sl@0: 	{
sl@0: 	if (KMMFEventCategoryAudioResourceAvailable!=aEventType)
sl@0: 		{
sl@0: 		return EFalse;
sl@0: 		}
sl@0: 	for (TInt index = iAudioPolicyRequestArray->Count(); index-- ; )
sl@0:  		{
sl@0:  		CMMFAudioPolicyRequest& currentReq=*(*iAudioPolicyRequestArray)[index];
sl@0:  		if(currentReq.PolicySessionId() == aSessionId)
sl@0:  			{
sl@0:  			currentReq.SetNotificationEvent(aEventType);
sl@0: #if defined(ALLOW_POLICY_DEBUG)
sl@0: 			RDebug::Print(_L("Sess ID %d state=%d requested resource notification"), aSessionId, currentReq.State());
sl@0: #endif			 			 			
sl@0:  			if (!IsActiveState(currentReq.State()))
sl@0:  				{
sl@0:  				currentReq.SetState(EMMFStateWaitingForResource);
sl@0:  				}
sl@0:  			return ETrue;	
sl@0:  			}
sl@0:  		}
sl@0:  	return EFalse;
sl@0: 	}
sl@0: 
sl@0: CMMFAudioPolicyRequest* CAudioPolicy::FindPolicyRequestById(TInt aSessionId) const
sl@0: 	{
sl@0: 	for (TInt index = iAudioPolicyRequestArray->Count(); index-- ; )
sl@0:  		{
sl@0:  		if((*iAudioPolicyRequestArray)[index]->PolicySessionId() == aSessionId)
sl@0:  			{
sl@0:  			return (*iAudioPolicyRequestArray)[index];
sl@0:  			}
sl@0:  		}
sl@0: 	return NULL;
sl@0: 	}
sl@0: 
sl@0: 
sl@0: