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: