bluetoothappprofiles/avrcp/absolutevolumeapi/src/absolutevolumeapicontroller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 20:28:57 +0200
changeset 0 f63038272f30
permissions -rw-r--r--
Revision: 201001 Kit: 201003

// Copyright (c) 2008-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:
//



/**
 @file
 @publishedAll
 @released
*/

#include <e32def.h>
#include <absolutevolumeapicontroller.h>
#include <absolutevolumeapicontrollerobserver.h>
#include <remconinterfaceselector.h>
#include "absolutevolumesender.h"
#include <absolutevolumeutils.h>
#include <bluetooth/logger.h>

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_REMCONABSOLUTEVOLUME);
#endif

#ifdef _DEBUG
_LIT(KAbsoluteVolumeControllerFaultName, "AbsVolFault");
// The panic codes associated with category KAbsoluteVolumeControllerFaultName are line numbers in this file.
#endif

/**
Allocates and constructs a new CRemConAbsoluteVolumeController object

@param aInterfaceSelector The interface selector. The client must have 
       created one of these first.
@param aObserver The observer. The client must have implemented the observer,
       owned by the client.
@param aMaxVolume The client maximum volume.
@return A new CRemConAbsoluteVolumeController, owned by the interface selector
@panic AbsoluteVolumeController 0 if aMaxVolume is zero.
*/
EXPORT_C 
CRemConAbsoluteVolumeController* CRemConAbsoluteVolumeController::NewL(
		CRemConInterfaceSelector& aInterfaceSelector, 
		MRemConAbsoluteVolumeControllerObserver& aObserver,
		TUint32 aMaxVolume)
	{
	LOG_STATIC_FUNC
	
	__ASSERT_ALWAYS(aMaxVolume > 0, 
				User::Panic(KAbsoluteVolumeControllerPanicName, 
						EControllerInvalidMaxVolume)
				);
				
	CRemConAbsoluteVolumeController* self = 
	new(ELeave) CRemConAbsoluteVolumeController(aInterfaceSelector, 
			aObserver, aMaxVolume);
	
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/** 
Constructor.

@param aInterfaceSelector The interface selector.
@param aObserver The observer.
*/
CRemConAbsoluteVolumeController::CRemConAbsoluteVolumeController(
		CRemConInterfaceSelector& aInterfaceSelector, 
		MRemConAbsoluteVolumeControllerObserver& aObserver,
		TUint32 aMaxVolume)
:	CRemConInterfaceBase(TUid::Uid(KRemConAbsoluteVolumeControllerApiUid), 
		KAbsoluteVolumeResponseDataSize,
		aInterfaceSelector,
		ERemConClientTypeController),
    iObserver(aObserver),
    iClientMaxVolume(aMaxVolume),
	iNotificationRequested(EFalse),
	iAbsVolRequest(ENotInUse),
	iNotifyRequest(ENotInUse)
	{
	LOG_FUNC
	}

EXPORT_C CRemConAbsoluteVolumeController::~CRemConAbsoluteVolumeController()
    {
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	delete iAbsVolSender;
    }

void CRemConAbsoluteVolumeController::ConstructL()
	{
	LOG_FUNC
	
	iAbsVolSender = CAbsVolSender::NewL(InterfaceSelector(), *this);
	BaseConstructL();
	}

/**
Sets an absolute volume on the target device,
Any responses will be returned via the observer interface.

@pre The send of any previous SetAbsoluteVolume command has completed.
@param aStatus Indicates the completion of the send request.  The client must
			   not block execution by using User::WaitForRequest to await 
			   completion of this send.
@param aVolume The relative volume against the client max volume.
@param aNumRemotes The number of remotes to which the command was sent.
@panic AbsoluteVolumeController 1, if aVolume greater than the client 
       max volume.
*/
EXPORT_C void CRemConAbsoluteVolumeController::SetAbsoluteVolume(
		TRequestStatus& aStatus, 
		TUint32 aVolume, 
		TUint& aNumRemotes)
	{
	LOG_FUNC
	LOG1(_L8("\taVolume = %d"), aVolume);
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	__ASSERT_ALWAYS(aVolume <= iClientMaxVolume, 
			User::Panic(KAbsoluteVolumeControllerPanicName, 
					EControllerVolumeBeyondMaxVolume)
			);
	__ASSERT_DEBUG(iAbsVolRequest == ENotInUse, User::Panic(KAbsoluteVolumeControllerPanicName, 
				EMultipleSetAbsoluteVolumes));
	
	RRemConAbsoluteVolumeRequest setAbsVol;
	setAbsVol.iVolume = aVolume;
	setAbsVol.iMaxVolume = iClientMaxVolume;
	TRAPD(err, setAbsVol.WriteL(iSetData));
	if (err == KErrNone)
		{
		// Store the client's info so we can complete their request later
		aStatus = KRequestPending;
		iAbsVolRequest = EPending;
		__ASSERT_DEBUG(iClientStatus == NULL, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
		iClientStatus = &aStatus;
		__ASSERT_DEBUG(iClientNumRemotes == NULL, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
		iClientNumRemotes = &aNumRemotes;
		KickOffSendIfNeeded();
		}
	else
		{
		iObserver.MrcavcoSetAbsoluteVolumeResponse(0, 0, err);
		}
	}

EXPORT_C void CRemConAbsoluteVolumeController::CancelSetAbsoluteVolume()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	switch ( iAbsVolRequest )
		{
	case ENotInUse:
		// Nothing to do.
		break;
	case ESending:
		iAbsVolSender->Cancel();
		SetAbsoluteVolumeSendComplete(KErrCancel);
		break;
	case EPending:
		SetAbsoluteVolumeSendComplete(KErrCancel);
		break;
	default:
		__ASSERT_DEBUG(EFalse, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
		break;
		}
	}

/**
Requests notification when the volume on the target device changes,
Any responses will be returned via the observer interface.

Volume changes will continue to be provided until either the 
CancelAbsoluteVolumeNotification function is called, or MrcavcoCurrentVolume
is called on the client with an error.

@see MRemConAbsoluteVolumeControllerObserver::MrcavcoCurrentVolume
@see CRemConAbsoluteVolumeController::CancelAbsoluteVolumeNotification
@pre The client is not currently registered to receive absolute volume notifications.
*/
EXPORT_C 
void CRemConAbsoluteVolumeController::RegisterAbsoluteVolumeNotification()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	// The request is outstanding, so not allowed to register again.
	__ASSERT_DEBUG(!iNotificationRequested, User::Panic(KAbsoluteVolumeControllerPanicName, EAbsoluteVolumeNotificationAlreadyRegistered));
	__ASSERT_DEBUG(iNotifyRequest == ENotInUse, User::Panic(KAbsoluteVolumeControllerPanicName, EMultipleNotifies));
	
	RRemConAbsoluteVolumeRequest absVol;
	TRAPD(err, absVol.WriteL(iNotifyData));
	if (err == KErrNone)
		{
		iNotifyRequest = EPending;
		iNotificationRequested = ETrue;
		KickOffSendIfNeeded();
		}
	else
		{
		iObserver.MrcavcoAbsoluteVolumeNotificationError();
		}
	}

/**
Called by the client to tell the controller that the client doesn't wish to 
receicve the volume change notification
until the client re-register again.
*/
EXPORT_C 
void CRemConAbsoluteVolumeController::CancelAbsoluteVolumeNotification()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	if ( iNotifyRequest == ESending )
		{
		iAbsVolSender->Cancel();
		}
	
	iNotificationRequested = EFalse;
	RegisterNotifySendComplete();
	}

TAny* CRemConAbsoluteVolumeController::GetInterfaceIf(TUid aUid)
    {
	LOG_FUNC
	
	TAny* ret = NULL;
	if ( aUid == TUid::Uid(KRemConInterfaceIf2) )
		{
		ret = reinterpret_cast<TAny*>(
			static_cast<MRemConInterfaceIf2*>(this)
			);
		}
	
	return ret;
    }

void CRemConAbsoluteVolumeController::MrcibNewMessage(TUint aOperationId, 
		const TDesC8& aData, 
		TRemConMessageSubType aMessageSubType)
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	switch (aOperationId)
	    {
	case KRemConAbsoluteVolumeNotification:
		HandleNotify(aData, aMessageSubType);
	    break;
	case KRemConSetAbsoluteVolume:
    	HandleSetAbsoluteVolumeResponse(aData);
	    break;
	default:	   
	    break;
	    }
	}

/** 
Process the 'volume changed notification response'

@param aData The response data.
@param aMessageSubType The RemCon submessage type.
*/
void CRemConAbsoluteVolumeController::HandleNotify(const TDesC8& aData,
		TRemConMessageSubType aMessageSubType)
    {
	LOG_FUNC
	
    TInt err = KErrNone;
    
    if(iNotificationRequested)
    	{
	    RRemConAbsoluteVolumeResponse absVol;
	    TRAP(err, absVol.ReadL(aData))
	    if (err == KErrNone)
	    	{
	    	if (absVol.iError == KErrNone)
	    		{
	    		switch ( aMessageSubType )
	    	        {
	    	    case ERemConNotifyResponseInterim:
	    	    	{
	    	    	VolumeUpdate(absVol.iVolume, absVol.iMaxVolume);
		    	    break;
	    	    	}	    	    
	    	    case ERemConNotifyResponseChanged:
		    		{
	    	    	VolumeUpdate(absVol.iVolume, absVol.iMaxVolume);

		    		// Register notification again.
		    		iNotificationRequested = EFalse;
		    		RegisterAbsoluteVolumeNotification();
		    	    break;
		    		}	    			
	    	    default:    	    
	    	        break;
	    	        }//switch
	    		}
	    	else //Error response
	    		{
	    		iNotificationRequested = EFalse;
	    		iObserver.MrcavcoAbsoluteVolumeNotificationError();
	    		}
	    	}
	    else
	    	{
    		iNotificationRequested = EFalse;
    		iObserver.MrcavcoAbsoluteVolumeNotificationError();
	    	}
    	}
    }

/** 
Process the 'set absolute volume response'
@param aData The response data.
*/
void CRemConAbsoluteVolumeController::HandleSetAbsoluteVolumeResponse( 
		const TDesC8& aData )
    {
	LOG_FUNC
	
    TInt err = KErrNone;
    RRemConAbsoluteVolumeResponse absVol;
    TRAP(err, absVol.ReadL(aData))
    if (err == KErrNone)
    	{
    	iCurrentVolume = absVol.iVolume;
    	iCurrentMaxVolume = absVol.iMaxVolume;
    	iObserver.MrcavcoSetAbsoluteVolumeResponse(absVol.iVolume, 
    			absVol.iMaxVolume,
    			absVol.iError);
    	}
    else
    	{
    	iObserver.MrcavcoSetAbsoluteVolumeResponse(0, 
    			0,
    			err);
    	}
    }

void CRemConAbsoluteVolumeController::MavsoSendComplete(TInt aResult)
	{
	LOG_FUNC
	LOG1(_L8("\taResult = %d"), aResult);
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	if ( iAbsVolRequest == ESending )
		{
		__ASSERT_DEBUG(iNotifyRequest != ESending, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
		SetAbsoluteVolumeSendComplete(aResult);
		}
	else if ( iNotifyRequest == ESending )
		{
		// This updates our own state and kicks off any pending send. The 
		// later client upcall (if there was an error) gives them a chance 
		// to make further calls on us.
		RegisterNotifySendComplete();

		if(aResult != KErrNone)
			{
			iNotificationRequested = EFalse;
			iObserver.MrcavcoAbsoluteVolumeNotificationError();
			}
		}
	else
		{
		// Send complete with no send outstanding.
		__ASSERT_DEBUG(EFalse, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
		}
	}

void CRemConAbsoluteVolumeController::KickOffSendIfNeeded()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	if ( iAbsVolRequest == ESending || iNotifyRequest == ESending )
		{
		// Any pending send will be kicked off when current send completes and this function is called again.
		return;
		}
		
	if ( iAbsVolRequest == EPending )
		{
		SendSetAbsoluteVolume();
		}
	else if ( iNotifyRequest == EPending )
		{
		SendNotify();
		}
	}

void CRemConAbsoluteVolumeController::SetAbsoluteVolumeSendComplete(TInt aResult)
	{
	LOG_FUNC
	LOG1(_L8("\taResult = %d"), aResult);
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	__ASSERT_DEBUG(iClientStatus, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
	User::RequestComplete(iClientStatus, aResult);
	iClientStatus = NULL;
	iClientNumRemotes = NULL;
	iSetData.SetLength(0);
	__ASSERT_DEBUG(iAbsVolRequest != ENotInUse, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
	iAbsVolRequest = ENotInUse;
	
	KickOffSendIfNeeded();
	}

void CRemConAbsoluteVolumeController::RegisterNotifySendComplete()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);

	iNotifyData.SetLength(0);
	// This method doesn't actually complete a client request so we don't bother asserting state.
	iNotifyRequest = ENotInUse;
	
	KickOffSendIfNeeded();
	}

void CRemConAbsoluteVolumeController::VolumeUpdate(TUint32 aVolume, TUint32 aMaxVolume)
	{
	LOG_FUNC
	LOG2(_L8("\taVolume = %d, aMaxVolume = %d"), aVolume, aMaxVolume);
	
	// Only update the client if the volume has changed
    if (aVolume != iCurrentVolume || aMaxVolume != iCurrentMaxVolume)
	    {
	    iCurrentVolume = aVolume; //store the new value
	    iCurrentMaxVolume = aMaxVolume;
	    
	    iObserver.MrcavcoCurrentVolume(aVolume,
	    		aMaxVolume,
	    		KErrNone);
	    }
	}

void CRemConAbsoluteVolumeController::SendSetAbsoluteVolume()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	__ASSERT_DEBUG(iClientNumRemotes, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
	iAbsVolSender->SendSetAbsoluteVolume(*iClientNumRemotes, iSetData);
	__ASSERT_DEBUG(iAbsVolRequest == EPending, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
	iAbsVolRequest = ESending;
	}

void CRemConAbsoluteVolumeController::SendNotify()
	{
	LOG_FUNC
	LOG2(_L8("\tiAbsVolRequest = %d, iNotifyRequest = %d"), iAbsVolRequest, iNotifyRequest);
	
	iAbsVolSender->SendNotify(iNotifyData);
	__ASSERT_DEBUG(iNotifyRequest == EPending, User::Panic(KAbsoluteVolumeControllerFaultName, __LINE__));
	iNotifyRequest = ESending;
	}