remotecontrol/avrcp/remconbeareravrcp/src/avrcputils.cpp
changeset 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remotecontrol/avrcp/remconbeareravrcp/src/avrcputils.cpp	Wed Oct 13 16:20:29 2010 +0300
@@ -0,0 +1,360 @@
+// 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...
+	}
+
+