bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcputils.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) 2004-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 <bt_sock.h>
#include <e32base.h>
#include <e32msgqueue.h>
#include <remconaddress.h>
#include <remconbeareravrcp.h>
#include "avrcplog.h"
#include "avrcputils.h"

/**
@file
@internalComponent
@released
*/

/** Utility AVRCP panic function.

@param aPanic The panic number.
*/
void AvrcpUtils::Panic(TAvrcpPanic aPanic)
	{
	User::Panic(KAvrcpPanicName, aPanic);
	}
	

/** Set the command data.  This overwrites the current
contents of the data buffer.

@param aCommandData The buffer in which to set the data.
@param aOffset The offset within aCommandData to set the data.
@param aLength The length of data to replace.
@param aValue The new value for the replaced data.
*/
void AvrcpUtils::SetCommandDataFromInt(RBuf8& aCommandData, 
	TInt aOffset, TInt aLength, TInt aValue)
	{
	LOG_STATIC_FUNC
	__ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong));
	
	for(TInt i = 0; i < aLength; i++)
		{
		aCommandData[aOffset+i] = aValue >> (8*i);
		}
	}

/** Reads command data from the buffer to an int.

@param aCommandData The buffer from which to read the data.
@param aOffset The offset within aCommandData read from.
@param aLength The length of data to read.  This must not be
				more than 4.
@param aValue On return, the value of the specified data section.
*/	
void AvrcpUtils::ReadCommandDataToInt(const RBuf8& aCommandData, 
	TInt aOffset, TInt aLength, TInt& aValue)
	{
	LOG_STATIC_FUNC
	__ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong));
	
	aValue = 0;
	
	for(TInt i = 0 ; i < aLength; i++)
		{
		aValue |= aCommandData[aOffset+i]<<(8*i);
		}
	}

/** Convert from a RemCon address to a bluetooth device address.

@param aRemoteAddress The RemCon format address to convert.
@param aBTAddr On return, the bluetooth device address.
@return Whether the conversion could be performed successfully.
*/	
TInt AvrcpUtils::RemConToBTAddr(const TRemConAddress& aRemoteAddress, TBTDevAddr& aBTAddr)
	{
	LOG_STATIC_FUNC
	TInt err = KErrArgument;
	
	// Check client has provided us a valid address
	if(aRemoteAddress.Addr().Length() == KBTDevAddrSize)
		{
		aBTAddr = TBTDevAddr(aRemoteAddress.Addr());
		err = KErrNone;
		}
	else
		{
		__ASSERT_DEBUG(EFalse, AvrcpUtils::Panic(EAvrcpBadBTAddr));
		}
		
	return err;
	}

/** Convert from a bluetooth device address to a RemCon address.

We assume this cannot fail, as bluetooth addresses are generated
internally rather than by a client, so they should always be
valid, and so convertible.

@param aBTAddr The bluetooth device address to convert.
@param aRemoteAddress On return, the RemCon format address.
*/	
void AvrcpUtils::BTToRemConAddr(const TBTDevAddr& aBTAddr, TRemConAddress& aRemConAddress)
	{
	LOG_STATIC_FUNC
	aRemConAddress.Addr() = aBTAddr.Des();
	aRemConAddress.BearerUid() = TUid::Uid(KRemConBearerAvrcpImplementationUid);
	}

NONSHARABLE_CLASS(CSpecificThreadCallBackBody)
	: public CActive
	{
public:
	static CSpecificThreadCallBackBody* NewL(const TCallBack& aCallBack, TInt aPriority);
	~CSpecificThreadCallBackBody();
	
	TInt Start();
	TInt CallBack();
	void HandleCancel();
	
private:
	CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority);
	void ConstructL();
	
	TInt AsyncMessage(TInt aParam);
	
private: // from CActive
	void RunL();
	void DoCancel();
	
private:
	TCallBack		iCallBack;
	
	RThread			iLocalThread;
	
	RMsgQueue<TInt>	iInbound;
	RMsgQueue<TInt>	iOutbound;
	};

RSpecificThreadCallBack::RSpecificThreadCallBack()
	: iBody(NULL)
	{
	LOG_FUNC
	}

TInt RSpecificThreadCallBack::Create(const TCallBack& aCallBack, TInt aPriority)
	{
	TRAPD(err, iBody = CSpecificThreadCallBackBody::NewL(aCallBack, aPriority));
	return err;
	}

void RSpecificThreadCallBack::Close()
	{
	LOG_FUNC
	delete iBody;
	iBody = NULL;
	}

TInt RSpecificThreadCallBack::Start()
	{
	return iBody->Start();
	}

TInt RSpecificThreadCallBack::CallBack()
	{
	return iBody->CallBack();
	}

void RSpecificThreadCallBack::Cancel()
	{
	return iBody->HandleCancel();
	}

CSpecificThreadCallBackBody* CSpecificThreadCallBackBody::NewL(const TCallBack& aCallBack, TInt aPriority)
	{
	CSpecificThreadCallBackBody* self = new(ELeave) CSpecificThreadCallBackBody(aCallBack, aPriority);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CSpecificThreadCallBackBody::CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority)
	: CActive(aPriority)
	, iCallBack(aCallBack)
	{
	LOG_FUNC
	}

void CSpecificThreadCallBackBody::ConstructL()
	{
	User::LeaveIfError(iInbound.CreateLocal(1));
	User::LeaveIfError(iOutbound.CreateLocal(1));
	}

CSpecificThreadCallBackBody::~CSpecificThreadCallBackBody()
	{
	LOG_FUNC
	HandleCancel();
	iInbound.Close();
	iOutbound.Close();
	iLocalThread.Close();
	}

TInt CSpecificThreadCallBackBody::Start()
	{
	TInt err = KErrNone;
	if(!IsAdded())
		{
		err = iLocalThread.Duplicate(RThread());
		if(err == KErrNone)
			{
			CActiveScheduler::Add(this);
			iInbound.NotifyDataAvailable(iStatus);
			SetActive();
			}
		}
	return err;
	}

TInt CSpecificThreadCallBackBody::CallBack()
	{
	TInt err = KErrUnknown;
	if(iLocalThread.Id() == RThread().Id())
		{
		// Simple synchronous case.
		err = iCallBack.CallBack();
		}
	else
		{
		RThread thisThread;
		err = thisThread.Duplicate(RThread());
		if(err == KErrNone)
			{
			err = AsyncMessage(thisThread.Handle());
			}
		}
	return err;
	}
	
TInt CSpecificThreadCallBackBody::AsyncMessage(TInt aParam)
	{
	TInt err = KErrNone;
	TRequestStatus logonStatus;
	iLocalThread.Logon(logonStatus);
	if(logonStatus == KErrNoMemory)
		{
		// This seems kludgy, but I think it is the most reliable way.
		User::WaitForRequest(logonStatus); // Ensure the all requests are correct...
		err = KErrNoMemory;
		}
	else
		{
		iInbound.SendBlocking(aParam);
		TRequestStatus status;
		iOutbound.NotifyDataAvailable(status);
		User::WaitForRequest(status, logonStatus);
		if(status == KRequestPending)
			{
			// Remote thread is dead
			iOutbound.CancelDataAvailable();
			User::WaitForRequest(status);
			err = KErrDied;
			}
		else
			{
			// Success (the thread may have subsequently died, but we are only concerned with this call).
			iLocalThread.LogonCancel(logonStatus);
			User::WaitForRequest(logonStatus);
			err = status.Int();
			if(err == KErrNone)
				{
				iOutbound.ReceiveBlocking(err);
				}
			}
		}
	return err;
	}


void CSpecificThreadCallBackBody::RunL()
	{
	TInt threadHandle;
	iInbound.ReceiveBlocking(threadHandle);
	if(threadHandle == 0)
		{
		// 0 is a cancel message
		// therefore don't do anything
		iOutbound.SendBlocking(KErrNone);
		}
	else
		{
		RThread remoteThread;
		remoteThread.SetHandleNC(threadHandle);
		
		TInt result = iCallBack.CallBack();
		
		// There doesn't seem to be a safe way of handling when the other thread
		// dies......
		iOutbound.SendBlocking(result);
		
		remoteThread.Close();
		
		iInbound.NotifyDataAvailable(iStatus);
		SetActive();
		}
	}

void CSpecificThreadCallBackBody::DoCancel()
	{
	if(RThread().Id() == iLocalThread.Id())
		{
		iInbound.CancelDataAvailable();
		}
	else
		{
		// other thread cancelling - so just complete the
		// request
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, KErrCancel);
		}
	}

void CSpecificThreadCallBackBody::HandleCancel()
	{
	if(IsAdded())
		{
		if(RThread().Id() == iLocalThread.Id())
			{
			Cancel(); // synchronous cancel is fine in same thread...
			}
		else
			{
			// In a different thread - this is more interesting...
			TInt err = AsyncMessage(0); // 0 is special as it means cancel.
			if(err == KErrDied && IsActive())
				{
				// Remote thread has already died so we need to tidy up the
				// active object ourselves.
				Cancel();
				}
			}
		}
	// else shouldn't be active...
	}